一、简介
JSX 是属于 React 中的一大特性,因此,本文将实现自定义 JSX 渲染功能,同时也会实现部分 React 中拥有的功能,以便加深理解.
二、准备工作
目录结构
目录结构比较简单,就不详细说明了
webpack 配置
- 由于我们需要在 .js 或者 .jsx 文件中编写 jsx 语法,同时,也为了我们可以使用一些 js 新特性,因此需要通过 webpack 中的 loader 配置进行编译.
- 这里我们需要用到的 loader 如下:
- babel-loader
- @babel/core
- @babel/preset-env: 将 js 转换为运行环境能识别的语法
- @babel/plugin-transform-react-jsx: 将 JSX 语法转换为对应内容的输出结果
- 为了避免多次手动执行 webpack 编译命令,这里是使用了 webpack-dev-server 来监听文件变化,自动执行编译命令
- 配置文件内容如下
const path = require('path'); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", entry: { main: "./main.jsx", }, module: { rules: [ { test: /\.jsx$/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: [ [ "@babel/plugin-transform-react-jsx", { pragma: "createElement" }, ], ], }, }, exclude: /node_modules/, }, ], }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', }, plugins: [ new HtmlWebpackPlugin({ title: "My App", template: "public/index.html", }), ], optimization: { minimize: false, }, }; 复制代码
三、编写 JSX
1. 首先在 main.jsx 中编写一段简单的 JSX 内容
2. 观察被编译的结果
- 从以上结果可以看到,最终 JSX 语法被 @babel/plugin-transform-react-jsx 被编译成了 React.createElement 方法,由此可见,要实现 JSX 渲染的关键就是要实现 createElement
- 这里我们要调整一下编译后的结果,我们需要 jsx 被编译为我们自定义的 createElement 方法,而不是 React.createElement,因此我们修改 webpack 配置文件中与 "@babel/plugin-transform-react-jsx" 相关的配置为
module: { rules: [ { test: /\.jsx$/, use: { loader: "babel-loader", options: { presets: ["@babel/preset-env"], plugins: [ [ "@babel/plugin-transform-react-jsx", { pragma: "createElement" }, // 这里就是控制 jsx 语法被编译后要调用的方法名 ], ], }, }, exclude: /node_modules/, }, ], 复制代码
3. 自定义实现 createElement 方法
从编译后的结果来看 createElement 方法具有三个参数:
- type —— 当前元素的类型:HTML标签名、Class 组件、Function 组件
- attributes —— 当前元素上的拥有的属性:{ } || null
- children —— 除了前两个参数,默认后面的参数全部为当前元素的子节点:[ ]
function createElement(type, attributes,...children){ // 创建 dom 实例 const currentElement = document.createElement(type); // 处理属性 if(attributes){ for (const name in attributes) { currentElement.setAttribute(name, attributes[name]); } } // 处理子节点 if(children.length){ for (let child of children) { // 处理文本节点 if(typeof child === "string"){ child = document.createTextNode(child); } currentElement.appendChild(child); } } return currentElement; } const JSX = (<div class="jsx"> <h1>i am Jsx</h1> </div>); document.body.appendChild(JSX); 复制代码
到这里,现在已经可以将简单的 JSX 渲染成了视图
四、升级改造 createElement
1. 虽然现在我们已经可以渲染简单的 JSX 内容了,但是如果要渲染 Class 组件或者 Function 组件的话,createElement 方法明显还无法做到,于是我们需要对其进行升级改造.
2. 同样,我们先观察如果使用 Class 组件,那么最终会被编译为什么呢?
class MyComponent { render() { return (<div> <h1>i am MyComponent</h1> </div>); } } const JSX = ( <div id="jsx"> <h1>i am Jsx</h1> <MyComponent id="MyComponent"> <h1>i am MyComponent child</h1> </MyComponent> </div> ); 复制代码