Код работы с моделями пишется прямо в отображении.
// View
function Task_list() {
return <ul>{
Task.list.map( task =>
<li><Task_row {task} /></li>
)
}</ul>
}
// Model
class Task {
static list = [] as Task[]
}
✅ Отображение может использовать произвольные модели. ✅ Легко добавлять новые отображения, не меняя модели. ❌ Для отображения разных моделей необходимо дублировать код отображения. ❌ Изменение интерфейса модели требует обновления всех использующих её отображений. ❌ Двух слоёв слишком мало на больших масштабах.
✅ Удобно из модели получать любые отображения. ❌ Добавление нового отображения требует изменения модели. ❌ Отображение полностью определяется одной основной моделью. ❌ Загрузка модели вытягивает по зависимостям и все её отображения. ❌ Двух слоёв слишком мало на больших масштабах.
Команда Honeypot выпустила документальный фильм об истории Node.js. В часовом видео подробно рассказали о том, как создавали популярную среду выполнения кода на JavaScript. Фильм продолжает серию, в которой уже есть следующие документальные картины:
Одна из самых серьезных проблем в разработке программных средств — их тучность, или раздутость. Программы просто становятся слишком большими. Это может быть связано с неразумным выбором функций, но чаще всего становится следствием плохой архитектуры. Популярное средство повторного использования кода — наследование, но его работа оставляет желать лучшего, поэтому вместо него зачастую применяются копирование и вставка кода. Не следует сбрасывать со счетов и чрезмерную зависимость от библиотек, платформ и пакетов, тесно связанных со многими другими библиотеками, платформами и пакетами. Раздутость может быть побочным эффектом приемов гибкой разработки. Чтобы справиться с ней, увеличивают численность команды разработчиков, но это порождает еще большую раздутость. … Лучший способ справиться с раздуванием программ — не допускать его. Приоритетом при разработке и реализации программы нужно сделать ее «худобу». Следует избегать внедрения в практику раздутых пакетов и инструментов, способствующих раздуванию. Обходитесь без классов. Нанимайте небольшие квалифицированные команды разработчиков. И активно практикуйте удаление кода. Создайте резерв из нескольких циклов разработки с целью удаления ненужного кода и избавления от проблемных пакетов. Радуйтесь, когда количество строк кода в проекте уменьшается. Придерживайтесь принципа наименьшей раздутости.
Дуглас Крокфорд, программист, автор формата JSON и книги "How JavaScript Works"
useOptimistic — новый хук, который позволяет отобразить “оптимистичное” состояние. Оно называется “оптимистичным”, потому что мы надеемся, что запрос не свалится с ошибкой и после его выполнения состояние будет выглядеть именно так.
❓Как используется
В useOptimistic передаётся реальное состояние и функцию-reducer
Компонент использует “оптимистичное” состояние для рендера
Перед выполнением запроса обновляется “оптимистичное” состояние
Когда запрос завершился, нужно обновить реальное состояние
Как только реальное состояние обновилось, оптимистичное состояние обновится автоматически, так как оно передано в useOptimistic первым параметром.
Если запрос упал с ошибкой, нужно откатить изменения в оптимистичном состоянии.
ℹ️ Первый вопрос, которым я задался, а в чём отличие от обычного setState, путём экспериментов, вот что удалось найти:
useOptimistic работает с формами. Работать с обычной кнопкой в SPA мне не удалось, обновление происходило только после завершения запроса
useOptimistic работает только внутри асинхронного обработчика, что логично. Если убрать async/await, обновление произойдёт только после завершения запроса
Параметр в useState используется только для инициализации, и игнорируется в последующих рендерах. useOptimistic будет сихронизироваться со значением со значением переданным первым параметром.
В любом случае, пока useOptimistic выглядит каким-то низкоуровневым API. Надеюсь скоро появится больше Best Practices.
use — новый хук, который позволяет считывать данные из промиса и при этом интегрирован с Suspense и ErrorBoundary.
Основные моменты:
На этот хук не распространяются правила хуков — его можно использовать внутри циклов и условных операторов.
Если мы используем хук use(Promise), то где-то в родительском компоненте мы должны положить сам промис (не данные как мы делали раньше) в стейт (useState). Это позволяет избавиться от useEffect’а, который был нужен, чтобы запросить данные при первом рендере.
Хук интегрирован с Suspense, поэтому пока промис не разрезолвится — будет показан fallback объявленный в ближайшем Suspense.
Если промис зареджектился, то будет показан fallback объявленный в ближайшем ErrorBoundary.
Состоялся релиз системы для создания отказоустойчивых рабочих процессов Restate 0.8. Код проекта опубликован на GitHub под лицензией Put Restate under Business Source License от Restate Software.
Согласно пояснению разработчиков проекта, Restate отлично подходит для создания:
рабочих процессов типа Lambda как код (Lambda Workflows as Code);
транзакционных обработчиков RPC;
обработки событий с помощью Kafka.
В версии Restate 0.8 разработчики уделили большое внимание доработке API, учтя отзывы пользователей, чтобы уменьшить трудности при создании сервисов Restate. Также там добавлены комбинаторы промисов (Promise combinators), которые позволяют детерминированно комбинировать промисы. Например, если вы хотите дождаться вызова службы A или вызова службы B, то Restate позаботится о записи того, какой из промисов был выполнен первым, и в конечном итоге воспроизведёт этот выбор, когда это необходимо. В новой версии проекта доступны все комбинаторы стандартной библиотеки JavaScript.
21 марта состоится уже 6-я встреча сообщества A?.Frontend, на которой мы подробно поговорим о TypeScript, сравним его с JS, определим плюсы и минусы использования. Кратко о программе.
? TypeScript: Введение в мир надежного программирования
Александр Чернов, Frontend-разработчик в Альфа-Банке, расскажет, как при помощи TypeScript сделать код надёжнее.
? Переход на TypeScript: плавные перемены и непредвиденные сложности
Рустам Султанбеков, Middle Frontend-разработчик в Авито, поделится опытом перевода существующего приложения на TypeScript и поможет понять, стоит ли это делать.
? Генерируй – типизируй
Александр Серов, Senior Frontend-разработчик в UULA, объяснит, как пользоваться продвинутыми возможностями TypeScript.
? Generics – Что? Где? Когда?
Тёма Сенюков, старший разработчик интерфейсов в Яндексе, расскажет, как пользоваться Generics, чтобы избежать дублирования кода и ошибок.
Состоялся релиз TypeScript 5.4. В новой версии языка программирования, построенного на основе JavaScript и позволяющего объявлять и описывать типы, в Microsoft поработали над производительностью языка, добавили новые возможности автодополнения кода для редакторов и упростили способы переподключения библиотек. Типы также используются в инструментах редактора TypeScript, таких как автодополнение, навигация по коду и рефакторинг, которые доступны в Visual Studio и VS Code. Проект доступен через NuGet или npm (npm install -D typescript).
В TypeScript 5.4 появился новый тип утилиты NoInfer, добавлена поддержка вызовов require() в пакете --moduleResolution и --module save, быстрое исправление при добавлении отсутствующих параметров, поддержка автоматического импорта для подпутей, исправлены ранее обнаруженные ошибки.
С выходом TypeScript 5.4 Microsoft продолжает работу над TypeScript 5.5. Согласно дорожной карте, бета-версия этого проекта должна выйти 16 апреля, релиз-кандидат — 4 июня, а финальный релиз — 18 июня.
Также стало возможно запускать тестовые ночные сборки TypeScript 5.4 Nightlies через npm, которые выпускаются каждый день в полночь (npm install -D typescript@next) для VS Code, Visual Studio, Sublime Text и IntelliJ.
7 марта 2024 года состоялся релиз Boa v0.18 — экспериментального лексера, парсера и компилятора Javascript, а также движка ECMAScript, написанного на языке программирования Rust.
Исходный код проекта выложен на GitHub под лицензией MIT License.
Новая версия Boa позволяет легко встраивать JS-движок в различные проекты, и а также использовать его из WebAssembly и командной строки.
Разработчики пояснили, что этот выпуск Boa также знаменует собой серьёзное обновление дизайна сайта проекта и появление нового логотипа.
«Поскольку Boa используется всё большим количеством проектов, важно, чтобы мы могли предоставить стабильный и надёжный API. Нам кажется, что мы ещё не достигли этого результата, но после обсуждения с командой мы решили нацелиться на выпуск версии 1.0 в ближайшем будущем. Это станет для нас важной вехой, и мы надеемся, что к тому времени у нас будет много новых функций и улучшений», — пояснили разработчики проекта.
Впервые проект Boa был представлен на конференции JSConf EU 2019 разработчиком Джейсоном Уильямсом.
Недавно мы выпустили большую статью про полезные фишки Google Apps Script. Делимся одним из примеров, как с помощью API-запроса можно тянуть данные из таск-трекера и CMS (у нас Bitrix) и интегрировать их в любые таблички. Пример в формате JS:
/** Функция обращения к таск трекеру по API */
function taskTrackerAuth() {
const sourceUrl = 'https://your_taskTracker_url/rest/tempo-timesheets/4/worklogs/search';
const options = {
'headers': { 'Authorization': 'Basic *******************' },
'method': 'post',
'contentType': 'application/json',
'Accept': 'application/json',
/** Полезная нагрузка настраивается индивидуально, то что указано тут можно очистить */
'payload': JSON.stringify({'from': [],'to': [], 'worker': [], 'projectKey': [], 'taskKey': [], 'filterId': [] }),
}
const taskTrackerResponse = UrlFetchApp.fetch(sourceUrl, options);
const data = JSON.parse(taskTrackerResponse.getContentText());
//Вывод сообщения о получении данных
if (data.length > 0) {
SpreadsheetApp.getActiveSpreadsheet().toast('Данные Timesheets получены', '(V)_O_o_(V)', 2);
} else {
SpreadsheetApp.getActiveSpreadsheet().toast('Данные Timesheets не получены', '(V)_O_o_(V)', 2);
}
}
Этот запрос обращен на получение данных из таск-трекера. Если его немного переделать, можно получить запрос и в другие системы и выудить данные через API откуда угодно.
Больше примеров — найдете в статье. А еще мы много пишем про разработку в нашем телеграм-канале.
CVE-2024-21892 — возможность подстановки непривилегированным пользователем кода, наследующего расширенные привилегии, с которыми выполняется рабочий процесс;
CVE-2024-22019 — отказ в обслуживании через исчерпание доступных ресурсов (нагрузка на CPU и расходование пропускной способности) при обработке встроенным HTTP-сервером специально оформленных chunked-запросов;
CVE-2024-21896 — выход за границу базового каталога в файловых путях, уязвимость позволяет обойти нормализации файловых путей при помощи path.resolve() в случае передачи пути с использованием класса Buffer;
CVE-2024-22017 — вызов setuid() не сбрасывал все привилегии;
CVE-2023-46809 — уязвимость в API privateDecrypt(), допускающая применение атаки Marvin для расшифровки RSA на основе измерения времени операций;
CVE-2024–21 891 — возможность обхода модели прав доступа при использовании пользовательских обработчиков нормализации файловых путей;
CVE-2024-21890 — некорректная обработка масок в параметрах "--allow-fs-read" и "--allow-fs-write";
CVE-2024-22025 — отказ в обслуживании через израсходование ресурсов при декодировании сжатых данных в формате Brotli, полученных через вызов fetch().
Как-то я уже упоминал паттерн Compound Components (Составные компоненты) для React, теперь остановимся на нём немного подробнее.
ℹ️ Compound components — это подход позволяет объединить несколько компонентов в единую сущность, которая неявно имеет общее состояние. Эти компоненты тесно взаимодействуют друг с другом и работают как единое целое, представляя собой полноценный UI компонент.
? Основные характеристики:
Используется React контекст, чтобы управлять состоянием
Должен быть главный компонент, в котором хранится состояние и объявляется React контекст
Все дочерние компоненты используют состояние через React контекст
ℹ️ Он состоит из 2 простых подходов React:
Композиция компонентов
Паттерн “Провайдер” — использование контекста React
Здесь идёт речь об обычном использовании контекста реакта, чтобы передавать какие-то данные на любую глубину дерева компонентов, минуя дочерние компоненты.
? Если объединить два этих подхода, то сможем реализовать паттерн Compound Components. Как пример, можно использовать компонент табов из библиотеки material-ui.
Недавно, когда я рассказывал, как мы пишем тесты, я уже упоминал, что в TypeScript’е появилось новое ключевое слово using. Оно позволяет нам сделать код чище и более линейным, избавившись от try/finally.
Но мы можем использовать using не только, когда открываем файл или подключение к базе данных, иначе его использование ограничилось бы исключительно сервером.
Мне нравится рассматривать using, как Undo/Redo только наоборот, сперва мы выполняем какое-то действие, а в конце отменяем его:
- создали объект, удалили
- показали спиннер и скрыли, когда получили данные
Причем отмена удобно происходит в самом конце функции, даже если мы используем async/await.
Вот простой пример, как можно использовать using, чтобы показывать/скрывать спиннер в React коде.
useEffect(() => {
(async () => {
using manager = new LoadingManager(setIsLoading);
await Promise.resolve().then(() => console.log("promise.resolve"));
})();
}, []);
/**
* Класс, который управляет состоянием спиннера
*/
class LoadingManager {
constructor(private setIsLoading: (value: boolean) => any) {
this.setIsLoading(true);
console.log("constructor");
}
[Symbol.dispose]() {
this.setIsLoading(false);
console.log("disposer")
}
}
// В консоли будет выведено в следующем порядке
// constructor
Вышел проект Bun Shell для запуска кроссплатформенных скриптов на JavaScript и TypeScript.
Оболочки для bash или sh существуют уже несколько десятилетий. Но они плохо работают в JavaScript. macOS (zsh), Linux (bash) и Windows (cmd) имеют немного разные оболочки с разным синтаксисом и разными командами. Команды, доступные на каждой платформе в итоге там разные, также одна и та же команда может иметь разные флаги при запуске и поведение после активации.
Bun Shell — это новый экспериментальный встроенный язык и интерпретатор Bun, который позволяет запускать кроссплатформенные скрипты на JavaScript и TypeScript. Проект работает на Windows, macOS и Linux. Там уже реализованы множество общих команд и функций, таких как подстановка, переменные среды, перенаправление, конвейерная обработка и многое другое.
Когда пользователь VK Видео откроет видеоролик, по которому в базе данных уже есть размеченные участки, в плеере на полосе таймлайна появятся цветные сегменты, которые будут автоматически пропускаться во время просмотра. После пропуска участка отобразится уведомление о пропущенном участке с возможностью отмотать обратно. Также рядом с переключателем звука будет кнопка "Реклама в видео", где можно просмотреть подробную информацию и добавить новый сегмент для автопропуска.
Пользователи (и команда асессоров) сами размечают на видеороликах участки с рекламой и другими типами лишнего контента. У всех других пользователей на тех же видеороликах появятся сегменты для автопропуска.
VKADSKIP создан командой энтузиастов, которые стараются сделать мир чуть лучше. Мы верим, что агрессивная политика рекламодателей не должна мешать пользователям наслаждаться контентом.
Как будет отсортирован следующий массив [-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4].sort()?
? Предыстория
На выходных я решал литкод, и в задаче 3sum было необходимо отсортировать массив по возрастанию, перед тем как перейти к основной реализации алгоритма.
Я написал решение, подебажил на бумаге — всё работает, отправляю код на проверку — не работает ?♂️. Перепроверяю всё глазами — ну должно же работать!
Сдаюсь и начинаю дебажить в VS Code и вижу, что сортировка массива работает не так как я ожидал.
ℹ️ Объяснение
Если перейти на MDN и прочитать документацию Array.prototype.sort(), то станет всё понятно.
Метод sort() в JavaScript преобразует элементы в строки и затем сравнивает их последовательности значений кодов UTF-16. Это означает, что при сортировке числа рассматриваются как строки.
Таким образом, числа в данном случае сортируются на основе их строкового представления. Например, '-10' будет идти перед '-2', потому что строка '10' идет перед строкой '2' в лексикографическом порядке.
Чтобы выполнить числовую сортировку массива, нужно предоставить функцию сравнения методу sort(), как показано здесь:
[-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4].sort((a, b) => a - b);
Я уже упоминал атомарные обновления, когда говорил о проблемах контекста реакта.
Я называю обновления атомарными, если компонент обновляется только тогда, когда изменяются данные, которые он использует. У контекста реакта с этим большие проблемы, но и при использовании Zustand можно выстрелить себе в ногу.
Zustand сравнивает по ссылке предыдущее и текущее значение, возвращаемое из useStore(), и если объект изменился — происходит ре-рендер.
❌ В следующем примере у нас всегда будет возвращаться новый объект, и ре-рендер произойдёт даже если изменился только age, а lastName и firstName не изменились