Эта статья представляет собой введение в новую библиотеку JS под названием feature-u, которая упрощает разработку на основе функций в вашем проекте React.

Примечание: 14.08.2018 была выпущена feature-u V1, в которой Межфакторная коммуникация была переработана, чтобы включить Композицию пользовательского интерфейса как основное предложение. В этой статье рассматривается выпуск V1. Первую статью, основанную на Feature Runtime Consolidation3">feature-u V0, можно найти здесь. Мы очень рады этому обновлению, потому что оно предлагает единое решение для совместной работы всех функций!

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

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

feature-u - это служебная библиотека, которая управляет и оптимизирует этот процесс. Он автоматизирует рутинные детали управления функциями и помогает продвигать функции, которые действительно являются plug-and-play.

Эта статья представляет собой основу концепций и терминологии feature-u, помогая понять, как вы можете продвигать отдельные функции plug-and-play в своем проекте. Это объясняет, почему была разработана feature-u, и дает вам лучшее представление о ее преимуществах.

Ознакомьтесь с полной документацией, исходным кодом и пакетом npm.

feature-u открывает новые двери в захватывающий мир функциональной разработки. Это дает вам возможность сосредоточить свое внимание на бизнес-стороне ваших функций!

С одного взгляда

Для вашего удобства это Оглавление (TOC) ссылается непосредственно на каждый раздел. Также обратите внимание, что заголовок каждого раздела ссылается на оглавление.

Feature Based Development
  Segregating Features
  Feature Goals
    Feature Runtime Consolidation
    Feature Collaboration
The feature-u Solution
  launchApp()
  Feature Object
  aspects
  Running the App
    App Initialization
    Framework Configuration
    Launching Your Application
  Cross Feature Communication
  Feature Based UI Composition
    Resource Contracts
  Feature Enablement
In Summary
Benefits
References

Пожалуйста, помогите мне рассказать о функции u. Ваши хлопки определяют распространение / продвижение этой статьи. Если вы считаете, что у feature-u есть потенциал, дайте этой статье несколько аплодисментов :-)

Разработка на основе функций

С точки зрения 30000 футов разработка на основе функций (как и в большинстве программ) сводится к разделению сложных проблем на более мелкие части. Даже когда я начинал свою карьеру (еще в 70-х), это была известная цитата:

«Все проблемы в информатике можно решить с помощью другого уровня косвенного обращения». Дэвид Уиллер

Разбив ваше приложение на функции, каждая функция может сосредоточиться на более конкретном и изолированном наборе задач. В некотором смысле вы можете рассматривать функцию как «мини-приложение»!

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

По большей части эти соображения являются частью дизайна каждого отдельного проекта. Хотя feature-u не диктует общих соображений по дизайну, он способствует применению хороших функциональных принципов (таких как инкапсуляция). Этому будет посвящена данная статья.

Разделение функций

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

При этом ваш код упорядочивается по тому, что он выполняет (то есть по функциям), а не по тому, что он есть (то есть по компонентам, маршрутам, логике, действиям, редукторам, селекторам и т. Д.).

Разделение ваших функций по отдельным каталогам создает видимость изоляции.

Функциональные цели

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

Структура каталогов - это только начало. Есть несколько препятствий, которые необходимо преодолеть, чтобы достичь нашей цели ...

  • Как мы инкапсулируем и изолируем наши функции, при этом позволяя им взаимодействовать друг с другом?
  • Как выбранные функции могут вводить инициализацию запуска (даже внедрять утилиту в корневой DOM), не полагаясь на какой-либо внешний процесс запуска?
  • Как можно выполнить композицию пользовательского интерфейса на основе функций изолированным и автономным способом?
  • Как мы настроим выбранные нами фреймворки теперь, когда наш код настолько разросся?
  • Как включить / отключить выбранные функции, которые являются необязательными или требуют обновления лицензии?

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

Если вы все сводите к минимуму, то для достижения наших целей необходимо выполнить две главные характеристики:

  1. Feature Runtime Consolidation: объединение наших функций в одно работающее приложение
  2. Feature Collaboration: обеспечивают механизм, с помощью которого наши функции могут взаимодействовать друг с другом

Как оказалось, все остальное является побочным продуктом этих двух артефактов. Давайте подробнее рассмотрим каждый из этих элементов.

Консолидация среды выполнения функций

Теперь, когда мы разделили наши функции на отдельные объекты, как нам собрать их вместе, чтобы они работали как одно приложение? Мы должны иметь возможность извлекать и настраивать различные аспекты наших индивидуальных функций и «запускать» их как единое однородное работающее приложение.

Эту проблему можно разделить на две подзадачи:

  • App Initialization
    Для некоторых функций может потребоваться определенная инициализация при запуске. Например, функция, которая инкапсулирует некоторую абстракцию БД, будет полагаться на настройку службы БД во время выполнения.
    Конечно, мы не хотим полагаться на некоторую глобальную логику приложения для выполнения этого (еще раз , мы хотим, чтобы наши функции были инкапсулированными и самодостаточными).
  • Framework Configuration
    Если ваше приложение полагается на другие фреймворки, скорее всего, в каждой функции есть ресурсы, которые необходимо накапливать и использовать в процессе настройки фреймворка.
    Как это достигается?

Совместная работа с функциями

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

Лучшая практика разработки на основе функций (насколько это возможно) - это рассматривать каждую функцию как изолированную реализацию. Большинство аспектов функции являются внутренними по отношению к реализации этой функции (например, действия обычно создаются и используются исключительно логикой / редукторами / компонентами, которые являются внутренними по отношению к этой функции).

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

Однако с учетом сказанного мы знаем, что « ни один человек не остров »! Любая данная функция в конечном итоге существует как часть более крупного приложения. Бывают случаи, когда функция должна продвигать ограниченное подмножество своих аспектов в другие функции. Например, для функции может потребоваться:

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

Эти элементы составляют основу того, зачем нужны Cross Feature Communication и Feature Based UI Composition.

Чтобы усложнить ситуацию, как правило, импорт JS НЕ должен пересекать границы объектов. Причина в том, что перекрестное общение должно быть ограничено общедоступными точками доступа, что помогает облегчить работу по принципу plug-and-play.

Учитывая все это, как достигается межфакторная коммуникация таким образом, чтобы не нарушалась инкапсуляция?

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

Решение feature-u

Давайте посмотрим на решение, которое feature-u предоставляет для всех этих целей. В следующих разделах будут постепенно строиться концепции feature-u.

LaunchApp ()

launchApp() - важная утилита в feature-u. Это агент, работающий от вашего имени, который обеспечивает основу для достижения всех целей feature-u! Это облегчает как Feature Runtime Consolidation, так и Feature Collaboration.

С помощью этой утилиты ваш основной процесс запуска становится чрезвычайно простым… она просто вызывает launchApp(), и все готово!

Функция launchApp() фактически запускает ваше приложение, используя различные перехватчики, которые управляют как инициализацией приложения, так и конфигурацией платформы!

Вы можете найти launchApp() примеров в разделе Usage, а Launching Your Application.

Как это работает? Какие привязки к launchApp()? ... давайте углубимся ...

Feature Object

Для этого каждая функция продвигает объект Feature (с использованием createFeature()), который каталогизирует интересующие аспекты для feature-u.

Это основной вход для launchApp().

Аспекты

В feature-u «аспект» (маленькая буква «а») - это обобщенный термин, используемый для обозначения различных ингредиентов, которые (в сочетании) составляют ваше приложение. Аспекты могут принимать разные формы: Компоненты пользовательского интерфейсаМаршрутыУправление состоянием (действия, редукторы, селекторы)Бизнес-логикаКод инициализации запускаи т. Д. и т. д. и т. д.

Не все аспекты представляют интерес для функции-u ... только те, которые необходимы для настройки и запуска приложения ... все остальные считаются деталями внутренней реализации функции. В качестве примера рассмотрим диспетчер состояний Redux: хотя он использует действия, редукторы и селекторы ... для установки и настройки Redux необходимы только редукторы.

Объект Feature - это просто облегченный контейнер, содержащий аспекты, представляющие интерес для feature-u. Эти аспекты могут быть либо Built-In aspects (из ядра feature-u), либо Extendable aspects (из расширений плагинов).

Запуск приложения

Давайте посмотрим, как launchApp() выполняет две подцели запуска приложения:

Инициализация приложения

Поскольку launchApp() управляет запуском приложения, он может ввести Application Life Cycle Hooks.

Это позволяет каждой функции выполнять инициализацию для конкретного приложения и даже внедрять компоненты в корень приложения.

Есть два крючка:

  1. Feature.appWillStart() - вызывается один раз при запуске приложения
  2. Feature.appDidStart() - вызывается один раз сразу после запуска приложения

Application Life Cycle Hooks значительно упростите основной процесс запуска приложения, поскольку инициализация, специфичная для данной функции, может быть инкапсулирована в эту функцию.

Конфигурация фреймворка

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

Как этого добиться, когда существует так много фреймворков… и в каждом проекте используется свое сочетание?

feature-u расширяется! Он работает в открытой подключаемой архитектуре, где расширяемые аспекты интегрируют feature-u с другими фреймворками в соответствии с вашим конкретным стеком времени выполнения. Это хорошо , потому что не все используют одни и те же фреймворки!

Расширяемые аспекты можно найти во внешних пакетах NPM (обычный случай), или вы можете создать свои собственные, используя createAspect() (более сложная тема) .

Объект Aspect содержит серию Aspect Life Cycle Hooks, которые вызываются под управлением feature-u (launchApp()). Как правило, Аспект обязан:

  • накапливать AspectContent по всем функциям
  • выполнить желаемую настройку и конфигурацию
  • каким-либо образом раскрыть его функциональность (обычно это интеграция с фреймворком)

Aspect автоматически расширяет объект Feature, позволяя ему AspectContent быть 'каталогизированным' в Feature, используя Aspect.name в качестве ключа. На диаграмме выше вы можете видеть, что

  • reducerAspect (Aspect.name: 'reducer') разрешает конструкцию Feature.reducer: reducerContent
  • и logicAspect (Aspect.name: 'logic') разрешает конструкцию Feature.logic: logicContent

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

Запуск вашего приложения

В feature-u основная линия приложения очень проста и универсальна. В нем нет реального кода, специфичного для приложения… даже глобальной инициализации! Это потому, что каждая функция может внедрять свои собственные конструкции, специфичные для приложения !! Основная строка просто накапливает Aspects и Features и запускает приложение, вызывая launchApp():

Вот несколько важных интересных моментов (сравните числа с *n* в приведенном выше коде):

  1. предоставленные Aspects (извлеченные из отдельных пакетов npm) отражают структуру нашего стека времени выполнения (в нашем примере redux, redux-logic и feature-router) и расширить допустимые свойства функции (Feature.reducer, Feature.logic и Feature.route соответственно) ... см. Extendable aspects
  2. предоставлены все функции нашего приложения (собраны из каталога features/)
  3. обратный вызов registerRootAppElm() используется для каталогизации поставляемого rootAppElm с конкретной используемой платформой React. Поскольку эта регистрация выполняется с помощью кода вашего приложения, feature-u может работать на любой из платформ React, например: react-web, react-native и expo ... см .: React Registration
  4. в качестве предварительного просмотра, возвращаемое значение launchApp() - это Fassets object, которое продвигает накопленное публичное лицо всех функций и экспортируется для предоставления Cross Feature Communication.

Межфакторная коммуникация

В поддержку совместной работы функций , которая не нарушает инкапсуляцию, feature-u продвигает функциональные ресурсы с помощью так называемого fassets (функциональные ресурсы). Так осуществляется вся межфункциональная коммуникация. Это можно рассматривать как публичное лицо функции.

Боковая панель: термин fassets - это игра слов. Хотя он произносится как «фасет» и имеет некоторое отношение к этому термину, он пишется как «фасеты» (т. Е. Объекты функций).

Функция может раскрыть все, что сочтет необходимым, с помощью встроенного Feature.fassets aspect). На этот ресурс нет реальных ограничений. Это действительно открыто.

В fassets aspect есть директива define, в которой ресурсы каталогизируются.

Вот простой пример определения fassets:

feature-u накапливает fassets из всех активных функций и продвигает их через Fassets object (исходящий из launchApp()).

Боковая панель. Есть несколько способов получить доступ к Fassets object (см. Obtaining fassets object).

Чтобы сослаться на fassets ресурс, просто разыменуйте его как любую другую ссылку на объект. Также существует метод Fassets.get(), который может быть предоставлен Wildcards, возвращающий массив ресурсов.

Это пример философии push. Здесь поставщик просто публично продвигает ресурс для использования другими функциями (принять или оставить). Поставщик просто говорит: «это мое публичное лицо».

Дополнительную информацию по этой теме можно найти в Cross Feature Communication.

Композиция пользовательского интерфейса на основе функций

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

В поддержку этого feature-u представляет компонент высшего порядка (HoC) withFassets(), который автоматически привязывает свойства к компоненту. Это общий шаблон, популяризированный Redux connect() (упрощение доступа компонентов к состоянию приложения).

Вот как компонент получит доступ к company.logo (определенному другой функцией):

Автоматически подключенные withFassets() HoC назвали активы функций как свойства компонентов через ловушку mapFassetsToPropsStruct. В этом примере, поскольку свойство Logo является компонентом, MyComponent может просто ссылаться на него с помощью JSX.

Дополнительную информацию по этой теме можно найти в UI Composition.

Контракты на ресурсы

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

fassets aspect имеет дополнительные конструкции для облегчения этого договорного соглашения, позволяя feature-u обеспечивать больше валидации в процессе.

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

  • Данная функция может указывать серию инъекций с помощью директивы fassets.use. Это идентифицирует набор ключей внедрения, которые однозначно идентифицируют эти ресурсы.
  • Другие функции будут предоставлять это содержимое с помощью директивы fassets.defineUse, ссылаясь на те же ключи внедрения.

Это больше похоже на философию притяжения. Это дает feature-u больше информации о процессе, позволяя ему проверять правильность предоставленных ресурсов.

Подстановочные знаки (*) могут использоваться для добавления дополнительной динамики в процесс, позволяя функциям автономно внедрять свой контент.

Вот main функция, которая извлекает ряд подкомпонентов (ссылки и тела) из других функций:

главная особенность:

Поскольку наша спецификация включает подстановочные знаки, ряд определений будет соответствовать!

Вот компонент MainPage, который выполняет контракт на использование:

Когда withFassets() встречает подстановочные знаки (*), он просто накапливает все совпадающие определения и продвигает их как массивы.

Благодаря этой реализации любая функция может динамически внедряться в процесс автономно! Кроме того, эта динамика неявно обрабатывает случай, когда функция динамически отключена (действительно очень круто) !!

Следующие ниже фрагменты взяты из других функций, которые предоставляют определения для вставляемого контента:

функция корзины

функция поиска

Две внешние функции (корзина и поиск) определяют контент, который запрашивает основная функция.

Директива fassets.defineUse требует, чтобы ключи ресурсов соответствовали запросу функции fassets.use. Это контракт, который обеспечивает понимание функции u при принудительной проверке.

Боковая панель: поскольку мы также имеем дело с навигацией, мы вводим react-router в микс (с компонентами Link и Route). Из-за дизайна RR V4 наши маршруты также обрабатываются посредством компонентной композиции (см. Feature Based Routes для получения дополнительной информации).

Дополнительную информацию по этой теме можно найти в UI Composition.

Включение функций

Возможности можно отключить динамически, установив логическое свойство Feature.enabled (часть Built-In aspects):

В этом примере функция sandbox не существует. Другими словами, он был логически удален.

Как правило, этот индикатор основан на некотором выражении времени выполнения, позволяющем динамически включать / отключать пакетный код в процессе запуска приложения:

Эта динамика полезна в различных ситуациях. Например:

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

Дополнительную информацию по этой теме можно найти в Feature Enablement.

"В итоге"

На следующей диаграмме обобщены основные концепции feature-u (как описано выше):

"Преимущества"

Использование feature-u дает множество преимуществ!

Два основных артефакта, из которых можно извлечь наибольшую пользу:

  • Формальное средство, с помощью которого функции могут взаимодействовать друг с другом (Cross Feature Communication), что делает их действительно готовыми к работе
    . Сюда входит возможность UI Composition пересекают границы объектов. Он даже позволяет вводить контент пользовательского интерфейса автономно. Это то, что нужно увидеть ... это очень хорошо демонстрирует feature-u.
  • Значительное сокращение стандартного кода за счет:
    автоматической настройки используемых фреймворков (через расширения плагинов - Extendable aspects);
    инициализации запуска, которая инкапсулируется внутри функций (через Application Life Cycle Hooks)

Следующий список преимуществ можно напрямую соотнести с соображениями, которые легли в основу разработки feature-u (см .: Why feature-u?).

  1. Инкапсуляция функций: изоляция границ функций улучшает управляемость кода
  2. Сотрудничество между функциями: продвигайте межфункциональное общение с помощью четко определенного общедоступного интерфейса на основе функций
  3. Композиция пользовательского интерфейса на основе функций: упрощение композиции компонентов между функциями
  4. Перехватчики жизненного цикла приложения: функции могут инициализироваться сами по себе, не полагаясь на внешние процессы
  5. Включение функций: включение / отключение функций с помощью переключателя времени выполнения
  6. Сведите к минимуму проблемы, связанные с зависимостью порядка функций во время расширения кода в строке
  7. Интеграция с фреймворком автоматически настраивает используемые фреймворки (в соответствии со стеком времени выполнения приложения) путем накопления всех аспектов функций (с использованием расширяемого API)
  8. Продвижение компонентов пользовательского интерфейса: функции могут автономно продвигать свои компоненты пользовательского интерфейса с помощью управления маршрутами на основе функций
  9. Единый источник истины: обеспечивается несколькими способами в рамках реализации функции
  10. Упрощенный запуск приложения: запуск приложения можно выполнить с помощью одной строки исполняемого кода!
  11. Работает на любой платформе React React Web, React Native, Expo и т. д.
  12. Plug-and-Play: проще добавлять и удалять функции

feature-u позволяет вам сосредоточить свое внимание на «бизнес-стороне» ваших функций!

Вперед и вычислите !!

"Использованная литература"