为了账号安全,请及时绑定邮箱和手机立即绑定

揭秘JSX:从零开始打造自己的JSX解析器

干嘛不呢?!

了解JSX:由Dall-e编写

虽然它不是 Web 标准,Web Components 在某种程度上试图取代它,但 JSX 是与 React 一起出现的出色技术,它简化了我们同时编写 HTML 和 JavaScript 的方式。

但它是怎么真正工作的呢?我的意思是,我们可以返回JSX,但这并不是标准的JavaScript,对吧?所以它究竟是怎么运作的呢?背后到底有什么魔法呢?

我个人非常喜欢技术“直接好用”,但如果我的工作离不开它,我都会尽量去弄懂它。其中一个办法就是试着逆向分析它,看看它是怎么运作的,并试着自己动手做一份。

你可以通过这个过程学到很多东西!

所以在这篇文章里,我将展示如何编写你自己的 JSX 解析器,让它能够将一个 JSX “组件” 转换为返回有效 HTML 的 JavaScript 函数。

走啦!

以AI速度构建 — 构建企业级应用、功能和组件以AI速度构建 — 构建企业级应用、功能和组件

这段我们要解析的 JSX 代码

我们先从最后开始,看看我们要处理的JSX文件。

如果你用 React 写这段代码,会是这样的。

说实话,唯一会改变的部分就是初始的导入。当我们需要用JSX时,我们实际上会明白为什么需要导入React。

虽然解析本身有点麻烦,但这个背后的逻辑其实很简单。如果你去看 Rect 的文档,他们就会展示解析 JSX 的结果。

从 React 的文档[1](https://reactjs.org/docs/introducing-jsx.html)截图

你知道,你实际上是把每个JSX元素转换成React.createElement的调用。没错!这也是为什么即使你表面上没有直接用到React,也需要导入它,一旦解析后,最终生成的JavaScript代码确实需要用到它。

现在那个谜已经解决了,我们继续。

第一个属性是待创建元素的标签名。第二个属性包含该元素的所有属性。其余属性(可以有一个或多个)将作为此元素的直接子元素,它们可以是纯文本或其它元素。

就这样完了,这个任务的挑战是:

  1. 将 JSX 代码捕获到 JavaScript 中。
  2. 将其解析成一棵树状结构,这样我们就能遍历和查询。
  3. 将该结构翻译成 JavaScript 代码(文本),并将其写入替代 JSX 的位置。
  4. 将步骤 3 的结果保存到一个带有 .js 扩展名的文件中。

开始写代码了!

从组件中提取并解析 JSX 代码

第一步是先从组件中提取 JSX 并将其解析成树状结构。

注:此处的 JSX 保留英文原貌,因为它是一个技术术语。

当然,这是两步,但我们将会一起做。

首先,我们需要做的是读取JSX文件,然后使用正则(确实,我们将在这篇文章中使用几个正则),来捕获JSX代码。

一旦我们拿到它,就可以用HTML解析器来解析它。

记住这里我们可以这样做,因为在这一点上我们只关心结构,而不关心JSX的实际特性。我们使用Node的fs模块来读取文件,以及node-html-parser包。

这个函数大致是这样的:

此功能使用正则(RegExp)来查找函数 (...) 部分中第一个组件的起始标签。在第 10 行,我们调用了 parse 函数,,该函数返回一个 root 元素,其中 firstChild 是我们 JSX 根元素(在这种情况下,即包裹的 div)。

我们现在有了树状结构,让我们开始将其转换成代码。接下来,我们将调用 translate 函数来完成这个转换。

将HTML代码转换成JavaScript代码:

由于我们的树状结构层次有限,我们可以安全地使用递归算法遍历这个树状结构。

以下就是该函数的样子,我们下面来看一下。

首先,我们会遍历所有子项,并对它们调用 translate 函数。如果它们为空或没有子项,它将返回 null,我们会在第 7 行过滤掉这些结果。

处理完孩子们的问题后,让我们看看第9行,我们在那里快速检查节点类型。如果类型是3,这意味着这是一个纯文本节点,所以我们返回纯文本内容。

为什么我们要调用 parseText 函数?因为在文本节点内部,我们也需要寻找这样的 JSX 表达式。因此这个函数会负责检查并在必要时正确处理返回的字符串。

然后,换句话说,也就是说我们实际上处理的不是一个文本节点,我们会获取标签名(第14行),然后解析属性(第16行)。解析属性意味着我们会把那个原始字符串转换成正确的JSON格式。

这就是第 18 行所做的事情,即生成带有正确参数的 createElement 调用,如该行所示。

记得我们在写代码,而不是真的在运行它。所有的代码都写在字符串里。

最后,关于这个功能需要注意的一个细节是,生成的代码中调用了来自 MyLib 模块的 createElement 方法。因此,JSX 文件中需要包含 import * as MyLib from './MyLib.js'

我们现在必须开始用字符串替换每个元素中的JSX表达式,包括文本节点和属性。

你喜欢刚才读的内容吗?试试订阅我的免费通讯,在那里我会与大家分享我在IT行业20年的经验与心得。加入“一位老程序员的随笔”!

解析这些表达式

在这个实现里,我支持的 JSX 表达式是最简单的那种。比如在下面的例子中,您可以在这些表达式里插入 JS 变量,它们仍将保持为变量在最终结果里。

这里有几个相关的函数:

如果有插值的话,即在大括号里的变量,那么我们就调用 replaceInterpolation 函数,这个函数会找到所有匹配的插值,并且把它们替换为格式正确的字符串。这样在写入 JS 文件时,变量名会以一种方式保留下来,生成一个有效的 JS 变量。

我们也会用这些函数来处理属性对象。因为我们返回JS代码时用了JSON.stringify方法,它会把所有值变成字符串,基本上所有的值都会被转换成字符串,尤其是我们单独定义的那些变量。所以我们会解析stringify返回的字符串,并确保正确替换插值变量。

你可以查看 getAttrs 函数 这里,以了解它是怎么做的。

我们现在来看看解析我们JSX文件后得到的代码。

JavaScript代码

阅读并解析我的 JSX 代码后,结果如下:

这段代码中最有趣的部分是生成的createElement调用。你可以看到它们的嵌套方式,它们引用了我在JSX文件中插入的变量。

如果我们运行这段代码,输出将会是这样的:

但是最后一个问题是还没有得到回答的:createElement 方法又是怎么实现的呢?好吧,我这里也有一个简化版的实现:

基本上,我使用tag值创建一个包装元素,如果有属性的话,就添加它们,最后遍历子元素列表(该列表包含所有的子元素),在这个过程中,我将这些值作为字符串返回(见第9行)。

就这样,魔术揭晓了!

JSX 是我最喜欢的在 JS 文件内处理和创建 HTML 的技术之一,它确实简化了这个过程。

当然,你可以直接用createElement方法来写代码,但这对于复杂的应用来说既不简单也不好看。

记得你可以在这里查看此项目的完整源代码,并且如果你有任何问题,可以在下面留言或提问,我们可以进一步讨论!

基于像乐高积木一样可重复使用的组件构建应用程序

图像

Bit的开源工具帮助着250,000+开发者们构建包含组件的应用。

将任何用户界面、功能或页面转化为一个可复用组件,并在您的应用程序之间共享它。这样更便于协作,构建速度更快。

了解更多

将应用拆分为组件可以让你更轻松地进行应用开发,享受你想要的工作流程的最佳体验。

微前端架构

我们如何构建微前端 - Medium

设计体系(我们如何构建设计体系)
代码共享与重用:文章
单仓库示例:单仓库
要了解更多
我们如何构建微前端应用通过构建微前端应用来加快我们的网页开发进程和扩展开发规模。
我们如何构建组件设计系统通过构建组件来标准化并规模化我们的用户界面开发流程。
如何重用React组件最终,你完成了在你的应用程序中为新闻通讯创建了一个很棒的输入字段的任务。你对此感到很满意。
5 种方式构建 React 单仓库构建一个生产级别的 React 单仓库:从快速构建到代码共享和依赖管理,本文将介绍五种方式。blog.bitsrc.io
如何使用 Bit 创建可组合的 React 应用 在此指南中,您将学习如何使用 Bit 构建并部署一个完整的可组合 React 应用。构建一个完整的可组合 React 应用……bit.cloud
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消