Статьи

Использование React в реальном мире

Знаете ли вы, что вы можете использовать React без переписывания всего приложения?

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

«Ага!» Я подумал: «Это работа для React + d3.js, Маленький, содержательный, идеальный. Лучший вид проекта для тестирования новых технологий.

Реагировать на злой заговор

Реагировать на злой заговор

Но их веб-приложение было построено на Joomla или WordPress или что-то в этом роде. И если я правильно понимаю, их интерфейсный стек был jQuery. Когда я спросил инженера об интеграции, он рассказал о плагинах фреймворка, которые создают представления в PHP и загружают файлы JavaScript и… прочее.

Это звучало очень похоже на камни Ruby on Rails. Крошечные пакеты, которые по сути являются автономными приложениями, предназначенными для интеграции в более крупные проекты.

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

Команда инженеров не собирается переписывать весь свой проект в React, просто чтобы вы могли сделать небольшой компонент. Я не собирался спрашивать, а они бы этого не сделали.

Я должен был бы разместить их .

Но как?

Решение

Ну, в конце концов, это все еще просто веб-сайт. Есть современный браузер, HTML, модель DOM, CSS, все основы.

Но нет ES6, нет причудливой системы сборки, нет управления модулями JavaScript. Ни один из модных штанов будущего.

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

Я порадовал своего клиента, экспортировав весь компонент визуализации в глобальную функцию, которую они могли вызывать где угодно. Это заняло лишь немного продуманной мысли:

const React = require('react'),
           Chart = require('./hart');

function RenderChart(options) {
    React.render(
        <Counter />,
        document.querySelectorAll(options.selector)[0]
    );
}

window.RenderChart = RenderChart;

Теперь мой клиент может интегрировать компонент React, просто:

  • загрузка React
  • загрузка скомпилированного файла JS
  • вызов функции с некоторыми параметрами

Да, есть некоторые издержки при загрузке всей библиотеки React только для одного компонента. Но это только 38 КБ после минимизации и сжатия. Например, размер jQuery составляет 29 КБ, и никто не унывает.

Кроме того, если вы используете общедоступный CDN, многим пользователям даже не придется загружать React. Тогда это всего лишь 400 байтов, чтобы подтвердить, что у вас установлена ​​последняя версия. Совсем неплохо.

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

Мы можем сделать лучше.

Реагируйте на компоненты как плагины jQuery, подтверждение концепции

Мы можем упаковать компоненты React в виде плагинов jQuery. Да, мы могли бы сделать представления Backbone, да, мы могли бы сделать компоненты Angular, да, это оскорбление для всего разумного.

И все равно идеально. Назовите один веб-сайт или веб-приложение, созданное за последние 10 лет или около того, у которого нет jQuery? Назовите одного веб-разработчика, который не может помочь себе с плагином jQuery?

Точно.

Вот подтверждение концепции, которую я подготовил ранее:

Вы смотрите на компонент, который говорит вам, сколько раз вы нажали на кнопку. Вот и все. Ничего фантастического.

Остальная часть примера — обычное веб-приложение старой школы. Есть несколько jQuery для рендеринга компонента и две кнопки jQuery для взаимодействия с компонентом после его рендеринга.

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

Использование компонента / плагина

Код «сделать мой компонент здесь» выглядит следующим образом:

$(".container .counter").clickCounter();

Легко и просто — выберите элемент, вызовите плагин jQuery. Bam.

И компонент выставляет свое внутреннее состояние через val()функцию. Использование выглядит следующим образом:

$(".btn-10x").click(function () {
    var counter = $(".container .counter")
            .clickCounter()[0];

    counter.val(counter.val()+10);
});

$(".btn-get").click(function () {
    var val = $(".container .counter")
            .clickCounter()[0]
            .val();

    alert("Current counter value is: "+val);
});

Мы выбираем элемент, получаем его экземпляр плагина и вызываем val()с или без аргумента. В зависимости от того, что вы хотите.

Мы делаем это, .clickCounter()[0]потому что я не знал, как правильно превратить плагин в синглтон для каждого элемента. Получить ссылку было достаточно просто, но селекторы jQuery могут возвращать массивы, а это значит, что я должен также возвращать массив. Но затем .val()функция запутывается, поэтому мы должны получить доступ к одному элементу возвращаемого массива.

Но этого пока достаточно. Любой, кто знает, как использовать jQuery, может использовать наш компонент React. И им даже не нужен Реакт.

Победа.

Упаковка компонентов React в плагины jQuery

Чтобы превратить наш компонент React в плагин jQuery, нам нужно позаботиться о двух вещах: сделать плагин, скомпилировать наш код для реального мира.

Сначала мы позаботимся о плагине. Мне нравится помещать этот код в src/main.jsxфайл. Он служит точкой входа для компонента, который экспортирует все, что нужно внешнему миру. Когда кто-то хочет использовать мой компонент из React, он может requireнапрямую подключить его.

Сначала мы загружаем компонент React и создаем функцию рендеринга:

const React = require('react'),
      Counter = require('./Counter');

function RenderCounter(selector) {
    React.render(
        <Counter />,
        document.querySelectorAll(selector)[0]
    );
}

Это позволит нам как экспортировать функцию, так и установить ее как глобальную. Как это:

module.exports = RenderCounter;
window.RenderCounter = RenderCounter;

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

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

Основы для нашего плагина jQuery взяты из стандартного проекта jQuery .

if (typeof jQuery !== 'undefined') {
    (function ($) {
        var pluginName = "clickCounter",
            defaults = {
                value: 0
            };

        function Plugin(element, options) {
            this.element = element;
            this.settings = $.extend({}, defaults, options);
            this._defaults = defaults;
            this._name = pluginName;
            this.init();
        }

        $.extend(Plugin.prototype, {
            init: function () {
                this.component = React.render(
                    <Counter value={this.settings.value},
                    this.element
                );
                return this;
            },

            val: function (val) {
                if (!arguments.length) {
                    return this.component.state.counter;
                }else{
                    this.settings.value = val;
                    this.init();
                }
            }
        });
    })(jQuery);
}

Вот так много кода.

Мы только что делаем плагин , если JQuery доступен, и мы оборачивать его определение в затворе , так что мы можем поделиться pluginNameи defaultsпеременными. Затем мы определяем функцию-конструктор с именем Plugin, которая хранит некоторые базовые свойства во вновь созданном объекте, и вызывает init.

Внутри initмы визуализируем наш компонент React. Мы не должны были использовать document.querySelectorAll, потому что this.elementуже ссылаются на элемент DOM, который мы хотим.

И мы определили valфункцию, которую мы можем использовать для доступа к внутреннему состоянию нашего компонента. Если есть аргумент, мы меняем значение и повторно визуализируем компонент. Мы делаем ставку на то, чтобы React был достаточно умен, чтобы выполнять обычное обновление реквизита, а не создавать все с нуля.

Если valаргумент не получен, он вернет текущее состояние компонента.

Хорошо, у нас есть функция плагина. Теперь мы должны добавить его в jQuery с несколькими строчками внутри замыкания:

        $.fn[pluginName] = function (options) {
            return this.map(function () {
                if (!$.data(this, 'plugin_'+pluginName)) {
                    $.data(this, 'plugin_'+pluginName, new Plugin(this, options));
                }
                return $.data(this, 'plugin_'+pluginName);
            });
        };
    })(jQuery);

Это позволит нам позвонить $(...).clickCounter(). И поскольку он сохраняет каждый экземпляр плагина в $.dataколлекции каждого элемента , мы можем убедиться, что каждый экземпляр рассматривается как отдельный элемент для каждого элемента. Каждый раз, когда кто-то вызывает clickCounterэлемент, он получает один и тот же объект.

Который изящен, когда вы хотите возиться с внутренними ценностями компонентов.

Составление Реакта для Реального Мира

Отлично, у нас есть плагин. Все, что нам нужно сейчас — это создать файл JavaScript, который могут использовать люди в реальном мире.

Это не займет много времени: достаточно просто сказать Webpack, чтобы он рассматривал React и jQuery как внешние и помещал скомпилированный код в ./build/counter.jsфайл. Конфиг, который я использовал, выглядит так:

module.exports = {
    entry: [
        './src/main.jsx'
    ],
    output: {
        filename: 'counter.js',
        path: path.join(__dirname, 'build'),
        publicPath: '/build/'
    },
    module: {
        loaders: [
            {
                test: /\.jsx$/,
                loaders: ['babel'],
                include: path.join(__dirname, 'src')
            }
        ]
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    externals: {
        "react": "React",
        "react/addons": "React",
        "jQuery": "jQuery"
    },
};

После этого мы сообщаем Webpack, что точка входа для нашего модуля есть ./src/main.jsx. Вот где мы поместили код плагина.

Затем мы сказали ему, куда поместить скомпилированный код ./build/counter.js, и что он должен использовать babel-loaderдля исходных файлов. Это позволяет нам использовать синтаксис React JSX для встраивания HTML в JavaScript, а также все необычные новые функции ES6.

Используя externals, мы объяснили, что и React, и jQuery являются внешними зависимостями. Таким образом, Webpack не будет пытаться связать их в скомпилированный файл.

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

Подводить итоги

Вот вам и компонент React, упакованный как плагин jQuery. На случай, если у вас нет времени или бюджета для полной переписки.

Вы пишете компонент, как всегда, затем создаете тонкую оболочку jQuery, и убедитесь, что люди за пределами экосистемы могут включить ваш файл. Bam.

Вы можете найти весь пример кода на Github здесь .

Что я сделал …

связанные с

Вы должны следовать за мной в твиттере, здесь .