Вт. Авг 3rd, 2021

Мысль создания модульной системы родилась и развивалась снутри нашего опенсорсного проекта ScandiPWA — витрины для онлайн-коммерции на React. Нашей главной задачей было создать расширяемую основу для наших проектов, в которую мы могли бы внедрять расширения, при этом имея возможность поменять облик (создавать темы) как самой базы, так и любого из расширений. При этом, принципиально было предоставить возможность быстрого и комфортного их (базы и расширений) обновления.

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

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

Мы длительно задавались вопросом: как живут все больше проекты в интернете, искали подходы, технологии… Даже тогда, два года обратно, когда мы только начинали, уже были технологии типа DI (dependency injection — прим. редактора). И всё с ними было отлично, да есть у нас проблема: наш продукт ScandiPWA делался для обычных разработчиков. Для закоренелых PHP-разработчиков, которые и React может 1-ый раз в жизни видят. Приходилось прямо в документации говорить о преимуществах над jQuery. Представьте как трудно было бы объяснить непонятные реестры.

В общем, нам необходимо было что-то элементарно простое. Пришлось, как принято, делать самим.

К чему мы пришли: плагиныСкопировать ссылку

Как уже сказано выше, чтоб работать с плагинами, обычно приходится писать ещё и кучу кода. Расставлять точки входа, регистрировать, импортировать файлы регистрации. Это всё длительно, сложно, затратно и очень плохо масштабируется. Мы решили, что всё это можно убрать. Скрыть и делать плагины сходу к самому коду. Мы очень не желали менять прототипы, мы хотели чтобы плагины просто перехватывали вызов той либо иной функции и позволяли модифицировать процесс и итог её выполнения. При этом было принципиально работать на уровне стека вызова уникальной функции, чтобы программу было легко отлаживать.

Итак, что же мы там выдумали? Теперь, вы можете делать сущности вашего языка в вашем приложении (функции, классы и способы) точкой входа для плагинов с помощью всего 1-го комментария! Комментарий — @namespace позволит изменить отмеченную суть через плагины из любого модуля в приложении. А ещё он отлично подсвечивается вашим редактором! Как это в итоге смотрится:

/** @namespace Application/getData */
const getData = () => {
return [‘Initial data’];
}

console.log(getData());

И что, это всё? Да, всё. Сейчас вы можете расширять эту функцию, к примеру, добавить к результату её выполнения новый элемент, сделать это, довольно просто, смотрите:

export default {
// Тот самый @namespace нашей функции
‘Application/getData’: {
// Тип изменяемой сути
function: (args, callback) => {
return [
// Вызов уникальной функции
…callback(…args),
// Наш новый элемент
‘Data from the plugin’
];
}
}
}

А как разрешить проблему с импортом файлов регистрации? Найти для них одно место, и брать оттуда! Забегая мало вперёд: система ищет плагины по паттерну src/plugin/*.plugin.* во всех отмеченных модулях приложения.

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

К чему мы пришли: темыСкопировать ссылку

А что с темами? Мы бы желали надстраивать их бесконечное количество (ну либо больше двух) друг на друге. К примеру так:

  • Голая тема с незапятанной функциональностью.
  • Красивая тема № 101.
  • Кастомизации под клиента.
  • Для нас ответ крылся в формулировке вопроса «надстраивать друг на друге». Мы решили сделать всё уже издавна изведанным во многих бэкенд-фреймворках способом: при сборке предпочитать файлы малышей родительским, в случае, если имена их файлов совпадают! Неясно? Вот как это работает на практике. Спойлер, всё просто!

    Этот файл лежит в файле App.js пакета nice-theme:

    import { PureComponent } from ‘react’;

    export default class App extends PureComponent {
    render() {
    return <p>Это написано в «Прекрасной теме №101».</p>;
    }
    }

    Это файл уже более новейшей темы «Кастомизации под клиента», но имя файла, что принципиально, совпадает: тоже App.js.

    import ParentApp from ‘nice-theme/App’;

    export default class App extends ParentApp {
    render() {
    return (
    <>
    { super.render() }
    <p>А это в «Кастомизации под клиента».</p>
    </>
    );
    }
    }

    В итоге получаем следующий HTML в результате рендера:

    <p>Это написано в «Прекрасной теме №101».</p>
    <p>А это в «Кастомизации под клиента».</p>

    Обратите внимание, мы использовали классы и наследование в React! Такая практика не рекомендуется. Но в нашем случае, она решает больше заморочек, чем создает:

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

    Что принципиально, мы не даём возможности обратиться впрямую к классу родительской темы, если он переписан в новейшей теме. Теперь любое обращение к нему возвратит вам ваш новый, переписанный класс! Потому мы считаем такой подход валидным и, невзирая на популярность функциональных компонентов в React, продолжаем использовать классовые!

    Как это испытать?Скопировать ссылку

    Всё очень просто. На самом деле, механизм может работать не только с React: как плагины, так и темы можно делать для всех сущностей и файлов. Мы же пока уверенно можем заявить о поддержке самых фаворитных фреймворков: Create React App и Next.js. Что, плагины в Next.js? Да-да, конкретно так!

    Как выглядит интеграция? Даже очень просто. Достаточно заменить зависимость с react-scripts на @tilework/mosaic-cra-scripts, а в самих скриптах, аналогично с react-scripts на cra-scripts. И всё! Как это смотрится:

    "dependencies": {
    — "react-scripts": "4.0.3",
    + "@tilework/mosaic-cra-scripts": "0.0.2",
    },
    "scripts": {
    — "start": "react-scripts start",
    — "build": "react-scripts build",
    + "start": "cra-scripts start",
    + "build": "cra-scripts build",
    }

    Для Next.js, всё точно также, только ставить нужно @tilework/mosaic-nextjs-scripts, а скрипты менять необходимо на nextjs-scripts.

    Что делать всем остальным? Если ваш стек — это Webpack и Babel, то и для вас есть очень обычное решение: ставим @tilework/mosaic-config-injectors и изменяем вашу Webpack-конфигурацию последующим образом:

    const webpack = require(‘webpack’);
    const ConfigInjectors = require(‘@tilework/mosaic-config-injectors’);

    module.exports = ConfigInjectors.injectWebpackConfig({
    // Конфигурация
    }, {
    webpack
    });

    Готово? Ура! Поделитесь опытом использования, для нас это очень принципиально!

    Почему все-таки mosaic?Скопировать ссылку

    Чем картины отличаются от мозаики? Мозаика состоит из отдельных частей, а картина, хоть и окрашена в различные цвета, является одним, неразделимым целым.

    Большая часть приложений JavaScript — это картины. Наше изначальное приложение ScandiPWA — тоже. Даже разделив такое приложение на модули, нам всё равно придётся их переплетать, интегрируя один в другой. Модули — как цвета на картине, вначале выходящие из разных тюбиков, оказываются неразрывно связанными в одно целое.

    Но есть способ превратить приложения написанные на JavaScript в реальную мозаику. Модули таких приложений должны содержат в для себя собственную интеграцию. Они больше не будут переплетаться на холсте, как краски, а их будет довольно выложить в определенном порядке, как плитку мозаики.

    Плагины позволят вам сделать это! Мы начали с интеграций в уже готовые приложения, а сейчас предлагаем написать всё с нуля? Да, мы сами в шоке!

    Но как это вам поможет? Какие препядствия решит? Честно говоря, вы просто напишете приложение вначале думая о том, как его можно будет расширять. И не только посторонними расширениями, но и собственной дополнительной логикой. Это зарядка для мозга, нужная сложность, которая делает наш код незначительно более качественным!

    Наверное, не стоит переписывать уже готовые приложения на этот подход, но вот писать новые… Стоит испытать! Особенно в случае, если вы желаете в будущем легко заменять одни его части на другие. Задумайтесь об этом 🙂

    Сообщает: web-standards.ru

    от Admin

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *