Знаете ли вы, что вы можете использовать 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 здесь .
Что я сделал …
связанные с
Вы должны следовать за мной в твиттере, здесь .