От Фреда Эвери, ведущего разработчика проекта Typester.

Эта статья является первой в серии, предлагающей взглянуть на внутреннюю работу проекта с открытым исходным кодом Typester, WYSIWYG-редактора, разработанного и разработанного в Type/Code для возврата стандартизированных, очищенных и предсказуемый HTML. В этой статье будут рассмотрены основные компоненты Typester.

Для получения дополнительной информации о Typester, демоверсиях или о том, как начать работу с редактором, посетите http://typester.io/.

Typester использует модули, чтобы разложить сложность плагина на независимые, связанные и взаимозаменяемые компоненты. Поступая таким образом, мы используем все преимущества модульных принципов проектирования программного обеспечения.

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

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

Для этого есть три основных элемента:

  1. Контейнеры
  2. Медиаторы
  3. Модули

Контейнеры

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

Контейнер имеет следующие преимущества:

  1. Область домена: когда связанные модули инкапсулируются, а затем связываются через общий посредник, что позволяет осуществлять взаимодействие между модулями в локальной области.
  2. Междоменная связь: когда локальный домен не может выполнить запрос, запрос поднимается в родительский контейнер, который, в свою очередь, проверяет одноранговые контейнеры, чтобы узнать, есть ли какие-либо из них или их модули. , способны выполнить запрос. Если ни один из пиров не может обработать запрос, он будет снова поднят. Этот подъем будет продолжаться до тех пор, пока не будет обработан запрос или не будет достигнут корневой контейнер. Если запрос не был обработан к этому моменту, он просто завершится с ошибкой.
  3. Многоэкземплярность модуля с изоляцией: за счет определения области и инкапсуляции доменов в контейнерах вы можете иметь модули с несколькими экземплярами, существующие в одной системе, но используемые в разных областях. Например: в Typester есть модуль, отвечающий за обработку выделения, но у нас есть две области, для которых требуется модуль: текущий активный редактируемый узел на странице, а также холст, на котором Typester выполняет всю тяжелую работу. Используя контейнеры для разделения доменов редактора и холста, мы можем активировать два экземпляра модуля выбора без их перекрестного взаимодействия или борьбы друг с другом.

Сейчас в библиотеке 4 контейнера:

  1. AppContainer: этот контейнер содержит модули, отвечающие за взаимодействие пользователя с активным редактируемым контейнером на странице. Это единственный контейнер, который будет иметь несколько экземпляров, по одному для каждого редактируемого контейнера с привязкой Typester.
  2. UIContainer (Singleton): этот контейнер содержит модули, отвечающие за отображение и обработку взаимодействия компонентов пользовательского интерфейса, таких как панель инструментов, всплывающее окно, которое оборачивает и позиционирует панель инструментов, и обработчик мыши.
  3. FormatterContainer (Singleton): этот контейнер содержит все модули форматирования.
  4. CanvasContainer (Singleton): этот контейнер содержит модули, связанные с холстом и обработкой содержимого на холсте и за его пределами.

Медиаторы

Медиаторы — это клей, который скрепляет все вместе в Typester и облегчает взаимодействие системы. Контейнеры и модули регистрируются с помощью посредников, а затем используют посредник для отправки команд, выполнения запросов и привязки к системным событиям.

Медиаторы также могут быть объединены в цепочку от родительского к дочернему, чтобы обеспечить иерархию посредников. Это полезно для распространения диспетчеризации посредника, которая не может быть выполнена текущим посредником. Посредники также допускают горячую замену своих родителей, что используется Typester для присоединения экземпляров редактора к одноэлементным экземплярам контейнеров UI, Formatter и Canvas в фокусе редактируемого элемента DOM.

Медиаторы предлагают три режима общения внутри системы:

  1. Запросы. Получение данных или содержимого из другой части системы.
  2. Команды. Запуск другой части системы для выполнения метода или подпрограммы.
  3. События. Отправка общесистемного сообщения о значимом событии.

Некоторые из преимуществ использования посредника для межсистемной связи включают в себя:

  1. Развязка. Модули не соединяются и не взаимодействуют друг с другом напрямую. Все взаимодействия опосредованы посредником, поэтому замена модуля или перемещение обработчика в другой модуль требует только объявления или переноса правильных обработчиков. Так что остальная часть системы не нуждается в изменениях. Нет необходимости выполнять grep кодовую базу и заменять несколько вызовов методов модуля с пространством имен.
  2. Изоляция. Модули практически не видят внутреннюю работу остальной системы. Они могут предполагать, что система может что-то сделать, но не знают, как или где система будет обрабатывать запрос или выполнять команду. Все, что им нужно сделать, это запросить что-то или отдать команду. Мне нужно, чтобы это было сделано, и мне все равно, кто это делает и как это делается.
  3. Отказоустойчивость:сбои внутри модулей не распространяются на другие части системы и не должны приводить к остановке всей системы.

Определения модуля и контейнера могут включать объект handler: {}, который объявляет сопоставление запросов, команд и событий с методами обработчика с использованием произвольных строк вызова в качестве ключей. В Typester мы выбрали строки вызова, разделенные двоеточиями, которые являются декларативными и описывают ожидаемый результат команды или результат запроса. В приведенном выше примере кода, сопоставителе обработчика для модуля Canvas, вы можете видеть, что mediator.get('canvas:document') будет сопоставлен с методом с именем getCanvasDocument, и вы можете ожидать получить в ответ узел документа iframe холста.

Модули

Следующая часть системы и настоящие рабочие лошадки — это модули. Они создаются с использованием фабричного метода, который принимает определение модуля и возвращает метод конструктора для создания экземпляра модуля.

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

Модули предлагают два хука, которые вызываются при их создании. Хук setup() и хук init(), внутри которых может быть размещен соответствующий код установки или пост-инициализации.

В остальном разработчик может заполнить объект methods:{} всеми необходимыми методами. Все объявленные методы будут связаны с модулем context

Контексты

Контексты, используемые модулями и контейнерами, дали нам способ установить this контекст дочерних методов в расширяемый объект. Система заполняет контекст ссылками на посредник, доступные методы из модуля или контейнера, реквизиты модуля и любые объявленные ссылки DOM.

Плавник

Для получения дополнительной информации ознакомьтесь с технической документацией для typester здесь: https://github.com/typecode/typester/tree/devel/docs