Первое критическое изменение обеспечивает большую гибкость, удаляя часть API, чтобы дать вам еще больше возможностей.

Не прошло и месяца с момента первого коммита, а Duckweed 1.0.0 выходит с обратно несовместимым изменением. Стоит ли нарушать совместимость? Новую кодовую базу легче поддерживать, а новый API намного мощнее, несмотря на удаление некоторых вещей.

Надеюсь, новый API какое-то время будет оставаться стабильным, но я ничего не обещаю. Философия Duckweed всегда будет заключаться в минимализме, и удаление API — самый быстрый способ добиться этого.

Новое в ряске?

Я полагаю, что большинство из вас впервые знакомятся с Ряской, так что я снова представлю ее.

Ряска — это исследование, свобода от оков объектно-ориентированного беспорядка и огромных поверхностей API, попытка писать приложения с наименьшим количеством возможного кода, сохраняя при этом все возможности открытыми (иногда теряясь в них в увлекательной игровой форме).

Вместо того, чтобы упаковывать тысячи и тысячи строк кода, чтобы вам не нужно было его писать, он пытается коренным образом изменить способ написания ваших приложений, чтобы вам не нужны тысячи строк кода. начать с. Все функции Duckweed включаются менее чем тысячей строк кода. Ядро Snabbdom содержит 300 с чем-то строк, а ядро ​​Duckweed — 150 строк.

Duckweed — это микрофреймворк JavaScript для написания компактных и подлых MAV-приложений.

Название микрофреймворк связано с размером полезной нагрузки чуть более 7 КБ в минимизированном и сжатом виде, а также с тем фактом, что его поверхность API состоит всего из 3 функций и 2 контрактов.

Не обманывайтесь крошечным следом. 7KB включает в себя Snabbdom с некоторыми из его наиболее полезных расширений и сам фреймворк. Это означает, что да, Duckweed говорит на виртуальном DOM, как и большие мальчики.

Паттерн MAV вдохновлен вязом. Приложения Duckweed состоят из слоев модели, действий и представления. Представления создают виртуальную модель DOM как функцию модели и определяют сопоставление между событиями и сообщениями. Действия обрабатывают сообщения и обновляют модель. Модели хранят состояние приложения. Представления — это простые чистые функции. Действия также реализуются с помощью чистой функции, которая принимает сообщение и может делать с ним что угодно.

По сравнению с другими фреймворками вы обнаружите, что Duckweed толкает вас в пустоту API, где, кроме некоторых базовых вещей, ничего не определено, и вам нужно придумать адекватную архитектуру для вашего приложения. Изучение фреймворка, который имеет только 3 функции, может показаться достаточно простым, но у Duckweed есть другая кривая обучения, связанная с наличием (слишком) большой свободы.

Если ваши вопросы о Duckweed сводятся к следующему: Сколько крупных компаний используют его?, Быстро ли это приложение?, Могу ли я построить X с его помощью?, Есть ли работа для программистов Duckweed?, вы находятся не в том месте. Ответ на все эти вопросы: используйте React.

Если вам уже интересно узнать о Duckweed, вы можете попробовать его в этой скрипке и прочитать документацию.

Что изменилось

Теперь кое-что для тех из вас, кто уже посмотрел на ряску.

Более гибкие действия

Основное изменение заключается в том, как обрабатываются действия. Теперь действия отправляются вашим собственным кодом. Давайте посмотрим на быстрый пример как старого, так и нового.

В версии до 1.0.0 вы бы написали свои действия так:

const actions = {
  increment: (patch) => {
    patch((module) => ({count: module.count + 1}));
  },
  decrement: (patch) => {
    patch((module) => ({count: module.count - 1}));
  },
};

Начиная с 1.0.0 больше нет patch(), а действия обрабатываются функцией обновления.

const update = (model, address, ...args) => {
  switch (address) {
    case 'increment':
      return {...model, count: model.count + 1);
      
    case 'decrement':
      return {...model, count: model.count - 1};
      
    default:
      return model;
  }
};

На первый взгляд кажется, что это противоречит философии ряски «меньше значит больше». Новый код, безусловно, более многословен, чем старый.

Новая функция обновления выполняет диспетчеризацию сама. Поначалу это может показаться немного менее эффективным, чем написание объектов, но это означает, что мы можем делать это так, как захотим. На самом деле, мы можем легко выполнить диспетчеризацию с использованием объекта, как и раньше, если реализуем функцию обновления таким образом!

Также обратите внимание, что функция обновления немедленно возвращает модель. В отсутствие patch() мы не можем продолжать исправление бесконечно (есть новый, более элегантный способ делать такие вещи с новым API, не бойтесь).

Однако приведенный выше пример не раскрывает всей истории. Есть больше.

В старом API мы могли бы реализовать действие, которое обновляет текущее время, например так:

const actions = {
  updateTime: (patch) => {
    setInterval(() => 
      patch(model => 
        ({...model, time: (new Date()).getTime()})), 
      1000);
  },
};

С API после версии 1.0.0 мы делаем это следующим образом:

const delay = (interval, msg) => new Promise((res) =>
  setTimeout(() => res(msg), interval));
const update = (model, address, ...args) => {
  switch (address) {
    case 'updateTime':
      return [
        {...model, time: (new Date()).getTime()},
        delay(1000, ['updateTime']),
      ];
  }
};

Функция обновления может возвращать либо копию модели, либо массив [model, message] или [model, Promise<message>], что позволяет функциям обновления передавать сообщения. В приведенном выше примере мы возвращаем пару обновлений модуля и отложенное сообщение с тем же адресом 'updateTime'. Это рекурсивное действие. Это кажется намного более естественным и функциональным.

Этот шаблон можно использовать для AJAX таким же образом, где мы используем обещания, такие как delay, и преобразуем их в сообщения.

Выйдите из patch.scoped(), введите линзы

В старом API у нас было patch.scoped(). Это также ушло вместе с самим patch(), так как же нам делать патчи с ограниченной областью действия? Что ж, Duckweed теперь раскрывает ядро ​​функции patch.scoped() как новую функцию duckweed.scoped.transform(). Вы можете увидеть его в действии в демонстрационной скрипке, поэтому я не буду вдаваться в подробности. То, что делает transform(), известно как линзы, и оно работает более или менее так же, как patch.scoped(), но без ограничений, наложенных старым API.

Попрощайтесь с act.as()

Еще одно небольшое изменение заключается в том, что act.as() был удален. Замены нет, но можно сделать act.bind(null, ...). Реализация act.as() была слишком сложной для того, чего можно было бы добиться с помощью встроенной функции JavaScript, поэтому отказ от act.as() в пользу более подробного bind() был оправданным компромиссом.

Более мощное промежуточное ПО

До версии 1.0.0 ПО промежуточного слоя упаковывало только вызовы patch(). Это ограничивало промежуточное ПО доступом только к изменениям модели. С исчезновением patch() функции промежуточного программного обеспечения были автоматически обновлены, чтобы иметь доступ ко всему циклу обновления. Теперь они могут получить доступ ко всем аргументам, которые получает обновление, включая сообщения. Несмотря на это, они по-прежнему так же просты в написании, как и старое промежуточное ПО. Разве вам не нравится, когда вы удаляете что-то и получаете обновление мощности бесплатно? :)

Улучшенные документы

Документы очищены от неверной информации, исправлены опечатки. В новые документы также включены советы по оптимизации производительности, а также были добавлены отсутствующие документы промежуточного слоя.

Обновление с предыдущих версий

Инструментов для обновления вашего приложения до версии 1.0.0 не предусмотрено. Это все ручная работа. Сказав это, его должно быть достаточно легко портировать. Единственное, с чем вы столкнетесь, это отсутствующий act.as() и новая функция обновления.

Если вы напишете адаптер для функции обновления, вам не придется слишком резко менять действия (хотя я рекомендую переключиться на функции обновления как можно скорее, потому что они значительно упрощают ваши приложения).

Что дальше?

Есть еще несколько вещей, которые нужно сделать, чтобы ряска действительно засияла.

Инструмент разработчика необходимо портировать на версию 1.0.0, и теперь, когда функции промежуточного программного обеспечения стали намного мощнее, нам нужно воспользоваться этим и показать больше полезной информации в пользовательском интерфейсе инструмента разработчика.

Я также хочу как-то разделить обработку событий на отдельный слой. Я надеюсь сделать это как дополнительную функцию, а не как очередное критическое изменение. Обратная связь по этому поводу будет очень кстати!

Значительное количество времени также будет посвящено улучшению руководства пользователя. В настоящее время он охватывает только некоторые основные шаблоны, но есть много реальных проблем, которые необходимо решить.