如何将 ReactJS 组件用作 jQuery 插件
介绍
我们中的任何人都可能收到客户的请求,该客户拥有一个可产生大量数据的自定义 API。客户可能会要求提供交互式可视化,以促进用户的决策过程。这感觉就像是一项“不可能完成的任务”,截止日期迫在眉睫。从积极的一面来看,客户对规格要求很宽松,只要能完成工作,就不会对技术施加任何限制。
我们可能会立即决定使用 React +d3.js,一个微小而理想的 JavaScript 库。
但是在某些情况下,这可能行不通。如果客户是老派的,他们可能有一个使用 Joomla 或 WordPress 设计的 Web 应用程序,而 jQuery 构成了前端堆栈。整个应用程序可能都是使用这些框架插件组件编写的,只留下可怜的服务器来处理后端。
客户的工程师团队不会浪费时间将他们的项目转移到 React,这样我们就可以创建一个简单的组件。我们知道答案是“不”,我们必须遵守他们的规则。
解决方案
总而言之,它只是一个网站,这意味着我们将处理网络浏览器、HTML、CSS、DOM 模型等等。
即使我们找不到 ES6 或构建系统,即使我们没有 JavaScript 模块管理或我们习惯的最先进的功能,您也可以将整个可视化组件导出到全局函数中,从而在代码中的任何位置调用它,如下所示:
const React = require('react'),
Chart = require('./chart');
function RenderChart(options) {
React.render(
<Counter />,
document.querySelectorAll(options.selector)[0]
);
}
window.RenderChart = RenderChart;
客户端现在可以通过加载 React,然后加载编译后的 JS 文件来集成 React 组件,最后使用指定的参数调用上述函数。解决了一个问题。
为了一个微小的组件而加载整个 React 库所造成的开销只是很小的代价。(React 库在缩小和压缩后只有 38KB,仅比 jQuery 大 10KB。)
更好的是,如果使用公共 CDN,许多用户的设备上已经缓存了 React。这意味着我们只需要下载不到 1KB 的文件,就可以确保他们拥有正确的版本。
客户很高兴,但我们却不高兴。
全局函数确实不是集成 React 组件的完美方法。全局函数可能会破坏全局命名空间,并且它们的通信是单向、一次性的。此外,在我们调用组件后,它就自由地运行了,我们无法跟踪它发生了什么。
使用 React 组件作为 jQuery 插件
我们可以轻松地将 React 组件用作 jQuery 插件。我们可以创建 Backbone 视图和 Angular 组件。这可能会冒犯我们一生都在使用的合理且众所周知的架构,但面对现实吧:这是完美的解决方案。
过去十年诞生的所有网站或 Web 应用程序都离不开 jQuery。任何理智的 Web 开发人员都不会不惜一切代价避免使用任何 jQuery 插件。
为了真正回答我们之前的问题,请看以下内容:
<div class="counter" style="border: 1px solid #eee; padding: 2em;"><div data-reactid=".0"><p data-reactid=".0.0"><span data-reactid=".0.0.0">You've clicked Our Button </span><span data-reactid=".0.0.1">7</span><span data-reactid=".0.0.2"> times!!</span></p><button class="btn btn-primary" data-reactid=".0.1">Our Button</button></div></div>
这只是一个计算“我们的按钮”被点击次数的组件——没有什么深奥的科学。
除此之外,我们还有 jQuery 来执行渲染,以及两个按钮来在渲染后与所述组件一起工作。
jQuery 驱动的白色按钮的特点是它们能够读取和写入组件的内部状态,并且不需要 React 即可实现。它们看起来就像我们之前使用过的任何 jQuery 代码一样。
使用组件/插件
为了渲染我们的组件,我们需要这行简单的代码:
$(".container .counter").clickCount();
因此,我们只需选择元素并调用所需的插件即可。插件将通过val()函数显示其内部状态,我们稍后会看到。但是,我们可以像这样使用它:
// src/integrate.js
$(".btn-20x").click(function () {
var counter = $(".container .counter")
.clickCount()[0];
counter.val(counter.val()+10);
});
$(".btn-get").click(function () {
var val = $(".container .counter")
.clickCount()[0]
.val();
alert("Current counter value is: "+val);
});
只需选择指定元素,获取其插件实例,然后调用我们前面提到的函数val()即可。这可以使用或不使用参数来完成,具体取决于我们对代码的想法。
我们需要使用.clickCount()[0],因为我们没有办法将插件变成每个元素的单例。我们可以轻松获取引用。但是,jQuery 选择器可以返回数组,因此我们也必须返回一个数组。为了使.val()函数的生命周期更简单并避免混淆,我们必须访问返回数组的单个元素。
现在我们做到了。我们有一个 React 组件,任何了解 jQuery 的人都可以使用它,而无需 React 本身。这是什么魔法?
将 React 组件打包到 jQuery 插件中
为了将我们的 React 组件转换为 jQuery 插件,我们需要做两件事。首先,我们显然需要创建插件。其次,我们需要编译代码,以便它在日常设备上运行良好。
我们慢慢来,先从插件开始。
使用src/main.jsx,我们可以一举两得,因为它充当组件的入口点,同时导出所需的所有数据。现在,当有人希望从 React 中使用此组件时,他们只需直接要求它即可。
因此,我们加载了 React 和我们的组件。现在是时候创建一个渲染函数了:
// src/main.jsx
const React = require('react'),
Counter = require('./Counter');
function RenderCounter(selector) {
React.render(
<Counter />,
document.querySelectorAll(selector)[0]
);
}
此代码片段使我们不仅可以导出函数,还可以将其分配为全局函数,如下所示:
module.exports = RenderCounter;
window.RenderCounter = RenderCounter;
因此,如果另一个不使用 React 但具有依赖加载器的开发人员想要使用该组件,他们可以要求它,然后使用该函数在任何选择的元素中呈现它。
此外,如果他们没有依赖加载器,他们可以使用全局函数。
jQuery 样板项目实际上为这个 jQuery 插件提供了基础。
下面是我们的 main.jsx 的样子:
// src/main.jsx
if (typeof jQuery !== 'undefined') {
(function ($) {
var pluginName = "clickCount",
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和默认变量。
接下来,定义一个名为Plugin的构造函数来保存最近构造的对象的基本属性并调用init。
我们可以在init中渲染 React 组件。但是,由于this.element已经引用了目标 DOM 元素,因此我们不需要使用document.querySelectorAll。
val函数用于访问组件的内部状态。如果传递了参数,我们会更改该值并重新渲染组件。React 可以通过使用新属性更新组件来处理此问题。
但是,如果没有向val传递参数,它将仅返回组件的当前状态。
注意我们如何使用this.component.state.counter访问组件的内部状态。我们只需要知道组件状态存储在哪里。这里面没有什么真正的 React 咒语。
现在需要以下几行代码来将插件添加到 jQuery:
// src/main.jsx
$.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);
现在我们可以调用$(...).clickCount()。考虑到插件的每个实例都存储在每个元素的$.data 集合中,我们可以保证每个实例都作为每个元素的单例处理。这样,每当在元素上调用clickCount时,都会返回相同的对象。如果我们希望操纵组件的内部值,我们可能会发现这很方便。
Webpack:为现实世界编译 React
我们已经准备好了插件,但如果其他人不能使用它,那还有什么用呢?为了解决这个问题,我们需要创建一个对其他人也有效的 JavaScript 文件。
为了实现这一点,我们可以使用 Webpack。它是一个简单的工具,可以像RequireJS或Browserify一样充当 JavaScript 依赖管理器。
Webpack 的工作原理如下:它分析我们的require()语句以创建代码的依赖关系树。接下来,它会生成一个 JavaScript 文件,其中包含我们代码稍后需要的所有内容。
Webpack 可能会将我们的代码分成多个模块,加载样式表、JavaScript 模板文件甚至图像。
为了避免细节,只需使用 React 将 ES6/JSX 代码转换为通常的 JavaScript 并编译最终用户可以加载的单个文件。
这听起来比实际要难。我们只需将 React 和 jQuery 声明为 Webpack 的外部组件,并将编译后的代码放在./build/counter.js文件中。Config.js 将如下所示:
// webpack.config.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": "
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~