在现实世界中使用 React

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

你知道吗,你可以在不重写整个应用程序的情况下使用 React?

一位客户最近向我要了一张闪亮的图表。他们有一个可以输出大量数据的 API,并且他们想要一个交互式可视化来帮助用户做出决策。截止日期很紧,技术限制是 “只要它有效” ,而且规格很宽松。

“啊哈!” ,我想, “这是 React+d3.js 的工作!” .小而精,完美。测试新技术的最佳项目。

反应邪恶阴谋

但是他们的网络应用程序是用 Joomla 或 WordPress 或其他东西构建的。如果我没理解错的话,他们的前端堆栈是 jQuery。当我向工程师询问有关集成的问题时,他谈到了在 PHP 中创建视图并加载 JavaScript 文件等的框架插件。

它听起来很像 Ruby on Rails gems。微型包本质上是独立的应用程序,旨在集成到更大的项目中。

他们已经这样做了一年多了。整个事情都是由这些框架插件组件构建的,这些事情将大部分艰苦的工作放在服务器上,并且只少量使用 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 组件:

  • 加载反应
  • 加载编译后的JS文件
  • 调用带有一些参数的函数

是的,只为一个组件加载整个 React 库会产生一些开销。但缩小压缩后只有38KB。例如,jQuery 是 29KB,而且没有人会眨眼。

此外,如果您使用公共 CDN,许多用户甚至不必下载 React。然后只需 400 字节即可确认您拥有最新版本。一点也不差。

但我觉得全局函数并不是集成 React 组件的最佳方式。首先,它们扰乱了全局命名空间,其次,它们只允许单向一次性通信。一旦你调用了一个组件,它就留给了它自己的设备,你没有任何洞察力。

我们可以做得更好。

React 组件作为 jQuery 插件,概念验证

我们可以将 React 组件打包为 jQuery 插件。是的,我们可以制作 Backbone 视图,是的,我们可以制作 Angular 组件,是的,这是对所有合理事物的侮辱。

无论如何它都是完美的。说出一个在过去 10 年左右构建的没有使用 jQuery 的网站或 Web 应用程序?举出一位无法使用 jQuery 插件帮助自己的 Web 开发人员?

确切地。

这是我之前准备的概念证明:

您正在查看一个组件,该组件会告诉您点击按钮的次数。就是这样。没有什么花哨。

该示例的其余部分是一个普通的老式网络应用程序。有一些 jQuery 用于渲染组件,还有两个 jQuery 按钮用于在渲染后与组件交互。

没错,白色按钮既可以读取也可以写入组件的内部状态。他们不需要 React 来做这件事。对于实现它们的人来说,它们看起来就像普通的 jQuery。

使用组件/插件

“在此处渲染我的组件” 代码如下所示:


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

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

window.RenderChart = RenderChart;

简单易用——选择元素,调用 jQuery 插件。砰。

组件通过 val() 函数公开其内部状态。使用它看起来像这样:


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

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

window.RenderChart = RenderChart;

我们选择一个元素,获取它的插件实例,然后使用或不使用参数调用 val() 。取决于你想要什么。

我们正在做 .clickCounter()[0] 因为我不知道如何正确地将插件转换为每个元素的单例。获取引用很容易,但是 jQuery 选择器可以返回数组,这意味着我也必须返回一个数组。但是随后 .val() 函数变得混乱,这就是为什么我们必须访问返回数组的单个元素。

但这现在已经足够了。任何知道如何使用 jQuery 的人都可以使用我们的 React 组件。他们甚至不需要 React。

赢了。

将 React 组件打包到 jQuery 插件中

要将我们的 React 组件变成一个 jQuery 插件,我们必须处理两件事:制作插件,为现实世界编译我们的代码。

我们将首先处理插件。我喜欢将这段代码放在 src/main.jsx 文件中。它作为组件的入口点,导出外界所需的一切。当有人想在 React 中使用我的组件时,他们可以直接 require 该组件。

首先我们加载 React,组件,并创建一个渲染函数:


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

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

window.RenderChart = RenderChart;

这将允许我们导出函数,并将其设置为全局函数。像这样:


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

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

window.RenderChart = RenderChart;

现在,任何拥有依赖加载器但不使用 React 的人都可以 require 我们的组件,并调用一个函数在特定元素内渲染它。

任何没有依赖加载器的人都可以使用全局函数。

我们的 jQuery 插件的基础直接来自 jQuery 样板 项目。


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

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

window.RenderChart = RenderChart;

哇,那是很多代码。

如果 jQuery 可用,我们只制作一个插件,我们将其定义包装在一个闭包中,以便我们可以共享 pluginName defaults 变量。然后我们定义一个名为 Plugin 的构造函数,它将一些基本属性存储在新构造的对象中,并调用 init

init 中,我们渲染我们的 React 组件。我们不必使用 document.querySelectorAll ,因为 this.element 已经引用了我们想要的 DOM 元素。

我们定义了一个 val 函数,我们可以使用它来访问组件的内部状态。如果有参数,我们会更改值,并重新渲染组件。我们指望 React 足够聪明,可以进行正常的道具刷新,而不是从头开始实例化所有内容。

如果 val 没有参数,它将返回组件的当前状态。

好的,我们已经有了插件功能。现在我们必须将它添加到 jQuery 中,并在闭包中添加几行:


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

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

window.RenderChart = RenderChart;

这将允许我们调用 $(...).clickCounter() 。因为它将插件的每个实例保存在每个元素的 $.data 集合中,所以我们可以确保将每个实例视为每个元素的单例。每次有人在元素上调用 clickCounter 时,他们将获得相同的对象。

当您想弄乱组件的内部值时,这很好。

为现实世界编译 React

太好了,我们有插件。我们现在需要做的就是制作一个现实世界中的人可以使用的 JavaScript 文件。

它不需要太多,只是告诉 Webpack 将 React 和 jQuery 视为外部,并将编译后的代码放在 ./build/counter.js 文件中。我使用的配置如下所示:


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

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

window.RenderChart = RenderChart;

有了这个,我们告诉 Webpack 我们模块的入口点是 ./src/main.jsx 。那就是我们放置插件代码的地方。

然后我们告诉它在哪里放置编译后的代码 – ./build/counter.js ,并且它应该为源文件使用 babel-loader 。这允许我们使用 React 的 JSX 语法在 JavaScript 中嵌入 HTML,以及 ES6 的所有奇特新功能。

使用 externals ,我们解释了 React 和 jQuery 都是外部依赖项。这样 Webpack 就不会尝试将它们捆绑到编译后的文件中。

在某些情况下,将两者捆绑到同一个文件中可能是有意义的,但我们失去了从 CDN 和用户浏览器缓存提供服务的好处。

总结

好了,一个打包为 jQuery 插件的 React 组件。当您没有时间或预算来完全重写时。

你像往常一样编写组件,然后制作一个薄的 jQuery 包装器,并确保生态系统之外的人可以包含你的文件。砰。

您可以在 Github 上的 此处 找到完整的示例代码。

我做了什么 …

有关的

你应该在推特上关注我, 在这里