iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >使用JSX 建立组件 Parser(解析器)开发的示例
  • 984
分享到

使用JSX 建立组件 Parser(解析器)开发的示例

2024-04-02 19:04:59 984人浏览 独家记忆
摘要

目录jsX 环境搭建建立项目初始化 NPM安装 webpack安装 Babel配置 WEBpack安装 Babel-loader模式配置引入 JSXJSX 基本用法JSX 基础原理实

这里我们一起从 0 开始搭建一个组件系统。首先通过上一篇《前端组件化基础知识》中知道,一个组件可以通过 Markup 和 javascript 访问的一个环境。

所以我们的第一步就是建立一个可以使用 markup 的环境。这里我们会学习使用两种建立 markup 的风格。

第一种是基于与 React 一样的 JSX 去建立我们组件的风格。第二种则是我们去建立基于类似 Vue 的这种,基于标记语言的 Parser 的一种风格。

JSX 环境搭建

JSX 在大家一般认知里面,它是属于 React 的一部分。其实 Facebook 公司会把 JSX 定义为一种纯粹的语言扩展。而这个 JSX 也是可以被其他组件体系去使用的。

甚至我们可以把它单独作为一种,快捷创建 html 标签的方式去使用。

建立项目

那么我们就从最基础的开始,首先我们需要创建一个新的项目目录:


mkdir jsx-component

初始化 NPM

在你们喜欢的目录下创建这个项目文件夹。建立好文件夹之后,我们就可以进入到这个目录里面并且初始化 npm


npm init

执行以上命令之后,会出现一些项目配置的选项问题,如果有需要可以自行填写。不过我们也可以直接一直按回车,然后有需要的同学可以后面自己打开 package.JSON 自行修改。

安装 webpack

Wepack 很多同学应该都了解过,它可以帮助我们把一个普通的 JavaScript 文件变成一个能把不同的 import 和 require 的文件给打包到一起。

所以我们需要安装 webpack ,当然我们也可以直接使用 npx 直接使用 webpack,也可以全局安装 webpack-cli。

那么这里我们就使用全局安装 webpack-cli:


npm install -g webpack webpack-cli

安装完毕之后,我们可以通过输入下面的一条命令来检测一下安装好的 webpack 版本。如果执行后没有报错,并且出来了一个版本号,证明我们已经安装成功了。


webpack --version

安装 Babel

因为 JSX 它是一个 babel 的插件,所以我们需要依次安装 webpack,babel-loader, babel 和 babel 的 plugin。

这里使用 Babel 还有一个用处,它可以把一个新版本的 JavaScript 编译成一个老版本的 JavaScript,这样我们的代码就可以在更多老版本的浏览器中运行。

安装 Babel 我们只需要执行以下的命令即可。


npm install --save-dev webpack babel-loader

这里我们需要注意的是,我们需要加上 --save-dev,这样我们就会把 babel 加入到我们的开发依赖中。

执行完毕后,我们应该会看到上面图中的消息。

为了验证我们是正确安装好了,我们可以打开我们项目目录下的 package.json


{
 "name": "jsx-component",
 "version": "1.0.0",
 "description": "",
 "main": "index.js",
 "scripts": {
 "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "",
 "license": "ISC",
 "devDependencies": {
 "babel-loader": "^8.1.0",
 "webpack": "^5.4.0"
 }
}

好,我们可以看到在 devDependencies 下方,确实是有我们刚刚安装的两个包。还是担心的同学,可以再和 package.json 确认一下眼神哈。

配置 webpack

到这里我们就需要配置一下 webpack。配置 webpack 我们需要创建一个 webpack.config.js 配置文件。

在我们项目的根目录创建一个 webpack.config.js 文件。

首先 webpack config 它是一个 nodejs 的模块,所以我们需要用 module.exports 来写它的设置。而这个是早期 nodejs 工具常见的一种配置方法,它用一个 JavaScript 文件去做它的配置,这样它在这个配置里面就可以加入一些逻辑。


module.exports = {}

Webpack 最基本的一个东西,就是需要设置一个 entry (设置它的入口文件)。这里我们就设置一个 main.js 即可。


module.exports = {
 entry: "./main.js"
}

这个时候,我们就可以先在我们的根目录下创建一个 main.js 的文件了。在里面我们先加入一个简单的 for 循环。


// main.js 文件内容
for (let i of [1, 2, 3]) {
 console.log(i);
}

这样 webpack 的基本配置就配置好了,我们在根目录下执行一下 webpack 来打包一下 main.js 的文件来看看。需要执行下面的这行命令进行打包:


webpack

执行完毕之后,我们就可以在命令行界面中看到上面这样的一段提示。

注意细节的同学,肯定要举手问到,同学同学!你的命令行中报错啦!黄色部分确实有给我们一个警告,但是不要紧,这个我们接下的配置会修复它的。

这个时候我们会发现,在我们的根目录中生成了一个新的文件夹 dist。这个就是 webpack 打包默认生成的文件夹,我们所有打包好的 JavaScript 和资源都会被默认放入这个文件夹当中。

这里我们就会发现,这个 dist 文件夹里面有一个打包好的 main.js 的文件,这个就是我们写的 main.js,通过 webpack 被打包好的版本。

然后我们打开它,就会看到它被 babel 编译过后的 JavaScript 代码。我们会发现我们短短的几行代码被加入了很多的东西,这些其实我们都不用管,那都是 Webpack 的 “喵喵力量”。

在代码的最后面,还是能看到我们编写的 for 循环的,只是被改造了一下,但是它的作用是一致的。

安装 Babel-loader

接下来我们来安装 babel-loader,其实 babel-loader 并没有直接依赖 babel 的,所以我们才需要另外安装 @babel/core@babel/preset-env。我们只需要执行下面的命令行来安装:


npm install --save-dev @babel/core @babel/preset-env

最终的结果就如上图一样,证明安装成功了。这个时候我们就需要在 webpack.config.js 中配置上,让我们打包的时候用上 babel-loader。

在我们上面配置好的 webpack.config.jsentry 后面添加一个选项叫做 module

然后模块中我们还可以加入一个 rules,这个就是我们构建的时候所使用的规则。而 rules 是一个数组类型的配置,这里面的每一个规则是由一个 test 和一个 use 组成的。

test:

  • test 的值是一个正则表达式,用于匹配我们需要使用这个规则的文件。这里我们需要把所有的 JavaScript 文件给匹配上,所以我们使用 /\.js/ 即可。

use: loader:

  • 只需要加入我们的 babel-loader 的名字即可

options:

presets:

  • 这里是 loader 的选项,这里我们需要加入 @babel/preset-env

最后我们的配置文件就会是这个样子:


module.exports = {
 entry: './main.js',
 module: {
 rules: [
 {
 test: /\.js$/,
 use: {
 loader: 'babel-loader',
 options: {
 presets: ['@babel/preset-env'],
 },
 },
 },
 ],
 },
};

这样配置好之后,我们就可以来跑一下 babel 来试一试会是怎么样的。与刚才一样,我们只需要在命令行执行 webpack 即可。

如果我们的配置文件没有写错,我们就应该会看到上面图中的结果。

然后我们进入 dist 文件夹,打开我们编译后的 main.js,看一下我们这次使用了 babel-loader 之后的编译结果。

编译后的结果,我们会发现 for of 的循环被编译成了一个普通的 for 循环。这个也可以证明我们的 babel-loader 起效了,正确把我们新版本的 JavaScript 语法转成能兼容旧版浏览器的 JavaScript 语法。

到了这里我们已经把 JSX 所需的环境给安装和搭建完毕了。 

模式配置

最后我们还需要在 webpack.config.js 里面添加一个环境配置,不过这个是可加也可不加的,但是我们为了平时开发中的方便。

所以我们需要在 webpack.config.js 中添加一个 mode,这我们使用 development。这个配置表示我们是开发者模式。

一般来说我们在代码仓库里面写的 webpack 配置都会默认加上这个 mode: 'development' 的配置。当我们真正发布的时候,我们就会把它改成 mode: 'production'


module.exports = {
 entry: './main.js',
 mode: 'development',
 module: {
 rules: [
 {
 test: /\.js$/,
 use: {
 loader: 'babel-loader',
 options: {
 presets: ['@babel/preset-env'],
 },
 },
 },
 ],
 },
};

改好之后,我们在使用 webpack 编译一下,看看我们的 main.js 有什么区别。

显然我们发现,编译后的代码没有被压缩成一行了。这样我们就可以调试 webpack 生成的代码了。这里我们可以注意到,我们在 main.js 中的代码被转成字符串,并且被放入一个 eval() 的函数里面。那么我们就可以在调试的时候把它作为一个单独的文件去使用了,并且可以进行断点调试。

引入 JSX

万事俱备,只欠东风了,最后我们需要如何引入 JSX呢?在引入之前,我们来看看,如果就使用现在的配置在我们的 main.js 里面使用 JSX 语法会怎么样。作为程序员的我们,总得有点冒险精神!

所以我们在 main.js 里面加入这段代码:


var a = <div/>

然后大胆地执行 webpack 看看!

好家伙!果然报错了。这里的报错告诉我们,在 = 后面不能使用 “小于号”,但是在正常的 JSX 语法中,这个其实是 HTML 标签的 “尖括号”,因为没有 JSX 语法的编译过程,所以 JavaScript 默认就会认为这个就是 “小于号”。

所以我们要怎么做让我们的 webpack 编译过程支持 JSX 语法呢?这里其实就是还需要我们加入一个最关键的一个包,而这个包名非常的长,叫做 @babel/plugin-transfORM-react-jsx。执行以下命令来安装它:


npm install --save-dev @babel/plugin-transform-react-jsx

安装好之后,我们还需要在 webpack 配置中给他加入进去。我们需要在 module 里面的 rules 里面的 use 里面加入一个 plugins 的配置,然后在其中加入 ['@babel/plugin-transform-react-jsx']

然后最终我们的 webpack 配置文件就是这样的:


module.exports = {
 entry: './main.js',
 mode: 'development',
 module: {
 rules: [
 {
 test: /\.js$/,
 use: {
 loader: 'babel-loader',
 options: {
 presets: ['@babel/preset-env'],
 plugins: ['@babel/plugin-transform-react-jsx'],
 },
 },
 },
 ],
 },
};

配置好之后,我们再去执行一下 webpack。这时候我们发现没有再报错了。这样也就证明我们的代码现在是支持使用 JSX 语法了。

最后我们来围观一下,最后编程的效果是怎么样的。

我们会发现,在 eval 里面我们加入的 <div/>翻译成一个 React.createElement("div", null) 的函数调用了。

所以接下来我们就一起来看一下,我们应该怎么实现这个 React.createElement,以及我们能否把这个换成我们自己的函数名字。

JSX 基本用法

首先我们来尝试理解 JSX,JSX 其实它相当于一个纯粹在代码语法上的一种快捷方式。在上一部分的结尾我们看到,JSX语法在被编译后会出现一个 React.createElement 的调用。

JSX 基础原理

那么这里我们就先修改在 webpack 中的 JSX 插件,给它一个自定义的创建元素函数名。我们打开 webpack.config.js,在 plugins 的位置,我们把它修改一下。


module.exports = {
 entry: './main.js',
 mode: 'development',
 module: {
 rules: [
 {
 test: /\.js$/,
 use: {
 loader: 'babel-loader',
 options: {
 presets: ['@babel/preset-env'],
 plugins: [
				[
					'@babel/plugin-transform-react-jsx',
					{ pragma: 'createElement' }
				]
			],
 },
 },
 },
 ],
 },
};

上面我们只是把原来的 ['@babel/plugin-transform-react-jsx'] 参数改为了 [['@babel/plugin-transform-react-jsx', {pragma: 'createElement'}]]。加入了这个 pragma 参数,我们就可以自定义我们创建元素的函数名。

这么一改,我们的 JSX 就与 React 的框架没有任何联系了。我们执行一下 webpack 看一下最终生成的效果,就会发现里面的 React.createElement 就会变成 createElement

接下来我们加入一个 HTML 文件来执行我们的 main.js 试试。首先在根目录创建一个 main.html,然后输入一下代码:


<script src="./main.js"></script>

然后我们执行在浏览器打开这个 HTML 文件。

这个时候我们控制台会给我们抛出一个错误,我们的 createElement 未定义。确实我们在 main.js 里面还没有定义这个函数,所以说它找不到。

所以我们就需要自己编写一个 createElement 这个函数。我们直接打开根目录下的 main.js 并且把之前的 for 循环给删除了,然后加上这段代码:


function createElement() {
 return;
}

let a = <div />;

这里我们就直接返回空,先让这个函数可以被调用即可。我们用 webpack 重新编译一次,然后刷新我们的 main.html 页面。这个时候我们就会发现报错没有了,可以正常运行。

实现 createElement 函数

在我们的编译后的代码中,我们可以看到 JSX 的元素在调用 createElement 的时候是传了两个参数的。第一个参数是 div, 第二个是一个 null

这里第二个参数为什么是 null 呢?其实第二个参数是用来传属性列表的。如果我们在 main.js 里面的 div 中加入一个 id="a" ,我们来看看最后编译出来会有什么变化。

我们就会发现第二个参数变成了一个以 Key-Value 的方式存储的JavaScript 对象。到这里如果我们想一下,其实 JSX 也没有那么神秘,它只是把我们平时写的 HTML 通过编译改写成了 JavaScript 对象,我们可以认为它是属于一种 “[[语法糖]]”。

但是 JSX 影响了代码的结构,所以我们一般也不会完全把它叫作语法糖。

接下来我们来写一些更复杂一些的 JSX,我们给原本的 div 加一些 children 元素。


function createElement() {
 return;
}

let a = (
 <div id="a">
 <span></span>
 <span></span>
 <span></span>
 </div>
);

最后我们执行以下 webpack 打包看看效果。

在控制台中,我们可以看到最后编译出来的结果,是递归的调用了 createElement 这个函数。这里其实已经形成了一个树形的结构。

父级就是第一层的 div 的元素,然后子级就是在后面当参数传入了第一个 createElement 函数之中。然后因为我们的 span 都是没有属性的,所以所有后面的 createElement 的第二个参数都是 null

根据我们这里看到的一个编译结果,我们就可以分析出我们的 createElement 函数应有的参数都是什么了。

  • 第一个参数 type —— 就是这个标签的类型
  • 第二个参数 attribute —— 标签内的所有属性与值
  • 剩余的参数都是子属性 ...children —— 这里我们使用了 JavaScript 之中比较新的语法 ...children 表示把后面所有的参数 (不定个数) 都会变成一个数组赋予给 children 变量

那么我们 createElement 这个函数就可以写成这样了:


function createElement(type, attributes, ...children) {
 return;
}

函数我们有了,但是这个函数可以做什么呢?其实这个函数可以用来做任何事情,因为这个看起来长的像 DOM api,所以我们完全可以把它做成一个跟 React 没有关系的实体 DOM。

比如说我们就可以在这个函数中返回这个 type 类型的 element 元素。这里我们把所有传进来的 attributes 给这个元素加上,并且我们可以给这个元素挂上它的子元素。

创建元素我们可以用 createElement(type),而加入属性我们可以使用 setAttribute(),最后挂上子元素就可以使用 appendChild()


function createElement(type, attributes, ...children) {
 // 创建元素
 let element = document.createElement(type);
 // 挂上属性
 for (let attribute in attributes) {
 element.setAttribute(attribute);
 }
 // 挂上所有子元素
 for (let child of children) {
 element.appendChild(child);
 }
 // 最后我们的 element 就是一个节点
 // 所以我们可以直接返回
 return element;
}

这里我们就实现了 createElement 函数的逻辑。最后我们还需要在页面上挂載上我们的 DOM 节点。所以我们可以直接挂載在 body 上面。


// 在 main.js 最后加上这段代码
let a = (
 <div id="a">
 <span></span>
 <span></span>
 <span></span>
 </div>
);

document.body.appendChild(a);

这里还需要注意的是,我们的 main.html 中没有加入 body 标签,没有 body 元素的话我们是无法挂載到 body 之上的。所以这里我们就需要在 main.html 当中加入 body 元素。


<body></body>
<script src="dist/main.js"></script>

好,这个时候我们就可以 webpack 打包,看一下效果。

Wonderful! 我们成功的把节点生成并且挂載到 body 之上了。但是如果我们的 div 里面加入一段文字,这个时候就会有一个文本节点被传入我们的 createElement 函数当中。毋庸置疑,我们的 createElement 函数以目前的逻辑是肯定无法处理文本节点的。

接下来我们就把处理文本节点的逻辑加上,但是在这之前我们先把 div 里面的 span 标签删除,换成一段文本 “hello world”。


let a = <div id="a">hello world</div>;

在我们还没有加入文本节点的逻辑之前,我们先来 webpack 打包一下,看看具体会报什么错误。

首先我们可以看到,在 createElement 函数调用的地方,我们的文本被当成字符串传入,然后这个参数是接收子节点的,并且在我们的逻辑之中我们使用了 appendChild,这个函数是接收 DOM 节点的。显然我们的文本字符串不是一个节点,自然就会报错。

通过这种调试方式我们可以马上定位到,我们需要在哪里添加逻辑去实现这个功能。这种方式也可以算是一种捷径吧。

所以接下来我们就回到 main.js,在我们挂上子节点之前,判断以下 child 的类型,如果它的类型是 “String” 字符串的话,就使用 createTextNode() 来创建一个文本节点,然后再挂載到父元素上。这样我们就完成了字符节点的处理了。


function createElement(type, attributes, ...children) {
 // 创建元素
 let element = document.createElement(type);
 // 挂上属性
 for (let name in attributes) {
 element.setAttribute(name, attributes[name]);
 }
 // 挂上所有子元素
 for (let child of children) {
 if (typeof child === 'string') 
		child = document.createTextNode(child);
 element.appendChild(child);
 }
 // 最后我们的 element 就是一个节点
 // 所以我们可以直接返回
 return element;
}

let a = <div id="a">hello world</div>;

document.body.appendChild(a);

我们用这个最新的代码 webpack 打包之后,就可以在浏览器上看到我们的文字被显示出来了。

到了这里我们编写的 createElement 已经是一个比较有用的东西了,我们已经可以用它来做一定的 DOM 操作。甚至它可以完全代替我们自己去写 document.createElement 的这种反复繁琐的操作了。

这里我们可以验证以下,我们在 div 当中重新加上我们之前的三个 span, 并且在每个 span 中加入文本。11


let a = (
 <div id="a">
 hello world:
 <span>a</span>
 <span>b</span>
 <span>c</span>
 </div>
);

然后我们重新 webpack 打包后,就可以看到确实是可以完整这种 DOM 的操作的。

现在的代码已经可以完成一定的组件化的基础能力。

实现自定义标签

之前我们都是在用一些,HTML 自带的标签。如果我们现在把 div 中的 d 改为大写 D 会怎么样呢?


let a = (
 <Div id="a">
 hello world:
 <span>a</span>
 <span>b</span>
 <span>c</span>
 </Div>
);

果不其然,就是会报错的。不过我们找到了问题根源的关键,这里我们发现当我们把 div 改为 Div 的时候,传入我们 createElement 的 div 从字符串 ‘div' 变成了一个 Div 类。

当然我们的 JavaScript 中并没有定义 Div 类,这里自然就会报 Div 未定义的错误。知道问题的所在,我们就可以去解决它,首先我们需要先解决未定义的问题,所以我们先建立一个 Div 的类。


// 在 createElment 函数之后加入
class Div {}

然后我们就需要在 createElement 里面做类型判断,如果我们遇到的 type 是字符类型,就按原来的方式处理。如果我们遇到是其他情况,我们就实例化传过来的 type


function createElement(type, attributes, ...children) {
 // 创建元素
 let element;
 if (typeof type === 'string') {
 element = document.createElement(type);
 } else {
 element = new type();
 }

 // 挂上属性
 for (let name in attributes) {
 element.setAttribute(name, attributes[name]);
 }
 // 挂上所有子元素
 for (let child of children) {
 if (typeof child === 'string') child = document.createTextNode(child);
 element.appendChild(child);
 }
 // 最后我们的 element 就是一个节点
 // 所以我们可以直接返回
 return element;
}

这里我们还有一个问题,我们有什么办法可以让自定义标签像我们普通 HTML 标签一样操作呢?在最新版的 DOM 标准里面是有办法的,我们只需要去注册一下我们自定义标签的名称和类型。

但是我们现行比较安全的浏览版本里面,还是不太建议这样去做的。所以在使用我们的自定义 element 的时候,还是建议我们自己去写一个接口。

首先我们是需要建立标签类,这个类能让任何标签像我们之前普通 HTML 标签的元素一样最后挂載到我们的 DOM 树上。

它会包含以下方法:

  • mountTo() —— 创建一个元素节点,用于后面挂載到 parent 父级节点上
  • setAttribute() —— 给元素挂上所有它的属性
  • appendChild() —— 给元素挂上所有它的子元素

首先我们来简单实现以下我们 Div 类中的 mountTo 方法,这里我们还需要给他加入 setAttributeappendChild 方法,因为在我们的 createElement 中有挂載属性子元素的逻辑,如果没有这两个方法就会报错。但是这个时候我们先不去实现这两个方法的逻辑,方法内容留空即可。


class Div {
 setAttribute() {}
 appendChild() {}
 mountTo(parent) {
 this.root = document.createElement('div');
 parent.appendChild(this.root);
 }
}

这里面其实很简单首先给类中的 root 属性创建成一个 div 元素节点,然后把这个节点挂載到这个元素的父级。这个 parent 是以参数传入进来的。

然后我们就可以把我们原来的 body.appendChild 的代码改为使用 mountTo 方法来挂載我们的自定义元素类。


// document.body.appendChild(a);
a.mountTo(document.body);

用现在的代码,我们 webpack 打包看一下效果:

我们可以看到我们的 Div 自定义元素是有正确的被挂載到 body 之上。但是 Div 中的 span 标签都是没有被挂載上去的。如果我们想它与普通的 div 一样去工作的话,我们就需要去实现我们的 setAttributeappendChild 逻辑。

接下来我们就一起来尝试完成剩余的实现逻辑。在开始写 setAttribute 和 appendChild 之前,我们需要先给我们的 Div 类加入一个构造函数 constructor。在这里个里面我们就可以把元素创建好,并且代理到 root 上。


constructor() {
 this.root = document.createElement('div');
}

然后的 setAttribute 方法其实也很简单,就是直接使用 this.root 然后调用 DOM API 中的 setAttribute 就可以了。而 appendChild 也是同理。最后我们的代码就是如下:


class Div {
 // 构造函数
 // 创建 DOM 节点
 constructor() {
 this.root = document.createElement('div');
 }
 // 挂載元素的属性
 setAttribute(name, attribute) {
 this.root.setAttribute(name, attribute);
 }
 // 挂載元素子元素
 appendChild(child) {
 this.root.appendChild(child);
 }
 // 挂載当前元素
 mountTo(parent) {
 parent.appendChild(this.root);
 }
}

我们 webpack 打包一下看看效果:

我们可以看到,div 和 span 都被成功挂載到 body 上。也证明我们自制的 div 也能正常工作了。

 这里还有一个问题,因为我们最后调用的是 a.mountTo(),如果我们的变量 a 不是一个自定义的元素,而是我们普通的 HTML 元素,这个时候他们身上是不会有 mountTo 这个方法的。

所以这里我们还需要给普通的元素加上一个 Wrapper 类,让他们可以保持我们元素类的标准格式。也是所谓的标准接口。

我们先写一个 ElementWrapper 类,这个类的内容其实与我们的 Div 是基本一致的。唯有两个区别

  1. 在创建 DOM 节点的时候,可以通过传当前元素名 type 到我们的构造函数,并且用这个 type 去建立我们的 DOM 节点
  2. appendChild 就不能直接使用 this.root.appendChild,因为所有普通的标签都被改为我们的自定义类,所以 appendChild 的逻辑需要改为 child.mountTo(this.root)

class ElementWrapper {
 // 构造函数
 // 创建 DOM 节点
 constructor(type) {
 this.root = document.createElement(type);
 }
 // 挂載元素的属性
 setAttribute(name, attribute) {
 this.root.setAttribute(name, attribute);
 }
 // 挂載元素子元素
 appendChild(child) {
 child.mountTo(this.root);
 }
 // 挂載当前元素
 mountTo(parent) {
 parent.appendChild(this.root);
 }
}

class Div {
 // 构造函数
 // 创建 DOM 节点
 constructor() {
 this.root = document.createElement('div');
 }
 // 挂載元素的属性
 setAttribute(name, attribute) {
 this.root.setAttribute(name, attribute);
 }
 // 挂載元素子元素
 appendChild(child) {
 child.mountTo(this.root);
 }
 // 挂載当前元素
 mountTo(parent) {
 parent.appendChild(this.root);
 }
}

这里我们还有一个问题,就是遇到文本节点的时候,是没有转换成我们的自定义类的。所以我们还需要写一个给文本节点,叫做 TextWrapper


class TextWrapper {
 // 构造函数
 // 创建 DOM 节点
 constructor(content) {
 this.root = document.createTextNode(content);
 }
 // 挂載元素的属性
 setAttribute(name, attribute) {
 this.root.setAttribute(name, attribute);
 }
 // 挂載元素子元素
 appendChild(child) {
 child.mountTo(this.root);
 }
 // 挂載当前元素
 mountTo(parent) {
 parent.appendChild(this.root);
 }
}

有了这些元素类接口后,我们就可以改写我们 createElement 里面的逻辑。把我们原本的 document.createElementdocument.createTextNode 都替换成实例化 new ElementWrapper(type)new TextWrapper(content)即可。


function createElement(type, attributes, ...children) {
 // 创建元素
 let element;
 if (typeof type === 'string') {
 element = new ElementWrapper(type);
 } else {
 element = new type();
 }

 // 挂上属性
 for (let name in attributes) {
 element.setAttribute(name, attributes[name]);
 }
 // 挂上所有子元素
 for (let child of children) {
 if (typeof child === 'string') 
		child = new TextWrapper(child);
 element.appendChild(child);
 }
 // 最后我们的 element 就是一个节点
 // 所以我们可以直接返回
 return element;
}

然后我们 webpack 打包一下看看。

没有任何意外,我们整个元素就正常的被挂載在 body 的上了。同理如果我们把我们的 Div 改回 div 也是一样可以正常运行的。

当然我们一般来说也不会写一个毫无意义的这种 Div 的元素。这里我们就会写一个我们组件的名字,比如说 Carousel,一个轮播图的组件。

完整代码 —— 对你有用的话,就给我一个 ⭐️ 吧,谢谢!


我们在这里互相监督,互相鼓励,互相努力走上人生学习之路,让学习改变我们生活!

学习的路上,很枯燥,很寂寞,但是希望这样可以给我们彼此带来多一点陪伴,多一点鼓励。我们一起加油吧! (๑ •̀ㅂ•́)و


到此这篇关于使用JSX 建立组件 Parser(解析器)开发的示例的文章就介绍到这了,更多相关JSX建立组件Parser内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 使用JSX 建立组件 Parser(解析器)开发的示例

本文链接: https://www.lsjlt.com/news/123014.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 使用JSX 建立组件 Parser(解析器)开发的示例
    目录JSX 环境搭建建立项目初始化 NPM安装 webpack安装 Babel配置 webpack安装 Babel-loader模式配置引入 JSXJSX 基本用法JSX 基础原理实...
    99+
    2024-04-02
  • 使用JSX 建立 Markup 组件风格开发的示例(前端组件化)
    目录JSX 环境搭建初始化 NPM安装 webpack安装 Babel配置 webpack安装 Babel-loader模式配置引入 JSXJSX 基本用法JSX 基础原理实现 cr...
    99+
    2024-04-02
  • Flex组件开发的示例分析
    这篇文章主要介绍Flex组件开发的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Flex组件开发总结在平时的Flex开发过程中遇到的问题以及解决办法总结如下:1.如何监听键盘事件?<mx:TextAre...
    99+
    2023-06-17
  • Vue3搭建组件库开发环境的示例详解
    目录1 packages 目录1.1 foo 目录1.2 yyg-demo-ui 目录2 实现 foo 示例组件2.1 初始化 package.json2.2 初始化 foo 目录结...
    99+
    2022-11-16
    vue3搭建组件库 vue搭建组件库开发环境 vue组件库
  • Vue中拖拽组件开发的示例分析
    这篇文章主要为大家展示了“Vue中拖拽组件开发的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue中拖拽组件开发的示例分析”这篇文章吧。为什么选择Vu...
    99+
    2024-04-02
  • Xamarin图表开发中OxyPlot组件构成的示例分析
    这篇文章主要为大家展示了“Xamarin图表开发中OxyPlot组件构成的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Xamarin图表开发中OxyPlot组件构成的示例分析”这篇文章...
    99+
    2023-06-04
  • 基于uni-app开发刻度尺组件的示例分析
    这篇文章将为大家详细讲解有关基于uni-app开发刻度尺组件的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、前言小编最近接到一个任务,就是在输入数值的时候不再使用传统的键盘了,而是用拖拉尺子的...
    99+
    2023-06-29
  • Compose 的 Navigation组件使用示例详解
    目录正文Jetpack Compose Clean Navigation使用传递普通参数,String、Int传递多个参数传递序列化参数注册导航正文 Navigation 组件支持 ...
    99+
    2022-11-13
    Compose Navigation组件 Compose Navigation
  • 使用Vuex进行web开发的示例分析
    这篇文章主要介绍使用Vuex进行web开发的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1 vuex的概念理解提到vuex,就不能不先提vue.我个人开始尝试学习使用vue...
    99+
    2024-04-02
  • React Native开发封装Toast与加载Loading组件的示例分析
    小编给大家分享一下React Native开发封装Toast与加载Loading组件的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去...
    99+
    2024-04-02
  • JavaScript组件开发之输入框加候选框的示例分析
    这篇文章主要为大家展示了“JavaScript组件开发之输入框加候选框的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JavaScript组件开发之输入...
    99+
    2024-04-02
  • vue组件化开发种vuex状态管理库的示例分析
    这篇文章主要介绍vue组件化开发种vuex状态管理库的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中...
    99+
    2024-04-02
  • 独立使用umi的核心插件模块示例详解
    目录引言实践结语引言 今天我们做一个有趣的尝试,将 umi 的核心插件模块独立出来作为另一个框架的基础架构,这里我们将它称为 konos。 介于 umi 自身的源码的独立拆分,要实...
    99+
    2023-01-12
    umi 核心插件模块 umi 插件
  • SpringBoot开发技巧之使用AOP记录日志示例解析
    目录为什么要用AOP?常用的工作场景必须知道的概念AOP 的相关术语Spring 中使用注解创建切面实战应用-利用AOP记录日志定义日志信息封装统一日志处理切面为什么要用AOP? 答...
    99+
    2024-04-02
  • 微信小程序云开发之项目建立与我的页面功能的示例分析
    这篇文章主要介绍微信小程序云开发之项目建立与我的页面功能的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!开发环境搭建使用自己的AppID新建小程序项目,后端服务选择小程序·云...
    99+
    2024-04-02
  • Android开发组件flutter的20个常用技巧示例总结
    目录 1.map遍历快速实现边距,文字自适应改变大小2.使用SafeArea 添加边距3.布局思路4.获取当前屏幕的大小5.文本溢出显示省略号6.一个圆角带搜索icon的搜...
    99+
    2024-04-02
  • Vue+Element switch组件的使用示例代码详解
    代码如下所示: <el-table-column label="商品状态" align="center"> <template slot-sco...
    99+
    2024-04-02
  • 使用 React Hooks 重构类组件的示例详解
    目录1. 管理和更新组件状态2. 状态更新后的操作3. 获取数据4. 卸载组件时清理副作用5.  防止组件重新渲染6. Context API7. 跨重新渲染保留值8. 如...
    99+
    2024-04-02
  • VS2008程序某些组件使用不当的示例分析
    这篇文章将为大家详细讲解有关VS2008程序某些组件使用不当的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。我的电脑是Vista系统,装了VS2008程序,最近由于某些原因必须要装VS2005,请...
    99+
    2023-06-17
  • Android开发flow常见API的使用示例详解
    目录collect通知flow执行launchIn()指定协程作用域通知flow执行catch{}捕捉异常merge()合流map{}变换发送的数据类型总结collect通知flow...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作