react 的 jsx 语法是怎样解析的
作者:夏天的味道123
- 2022-10-29 浙江
本文字数:2394 字
阅读完需:约 8 分钟
首先我们来看看下面的代码
import "react" from "react"; const element = (<div> <div> <span>1</span> <span>2</span> <span>3</span> </div> <div>1</div> <div>2</div></div>)console.log(element)
复制代码
问题来了,
element是如何输出上图所示的结构的?
环境配置
安装react和babel
npm i react react-dom --savenpm i @babel/core @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
复制代码
配置babel
{ test: /\.(js|jsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { { "presets": [ "@babel/preset-env" ], "plugins": [ "@babel/plugin-transform-react-jsx" ] }, cacheDirectory: true, }}
复制代码
@babel/plugin-transform-react-jsx做了什么?
遇到 <div>123</div>执行React.createElement("div", "123");
遇到 <div> <div>1</div> <div>2</div> <div>3</div> </div>执行 React.createElement("div", React.createElement("div", "1"), React.createElement("div", "2"), React.createElement("div", "3") )// 也就是说,用react开发的时候只要你用到了jsx语法,那么不管你有没有用到React都必须import react from "react"
复制代码
参考 React 实战视频讲解:进入学习
写个函数来模拟它的执行过程
为了便于理解 我们把<div> <div> <span>1</span> <span>2</span> <span>3</span> </div> <div>1</div> <div>2</div></div>当做一棵树let element = { type:"div", children:[{ type:"div", children:[{ type:"span", children:"1" }, { type:"span", children:"2" }, { type:"span", children:"3" }] }, { type:"div", children:1 }, { type:"div", children:2 }]}写一个函数对这颗树进行深度遍历
function jsxTransformNode(element, callback){ let children = []; if (Array.isArray(element.children)) { children = element.children.map(child => jsxTransformNode(child, callback)) } else { children = [element.chidren] } return callback(element.type, ...children);}
let nodes = jsxTransformNode(child, function ReactCreateElement(type, ...children){ return { tag: type, children }})
复制代码
@babel/plugin-transform-react-jsx的原理
对babel不熟的话可以先看这边文章从零开始编写一个 babel 插件
它其实就是将
<div className="name" age="12"> <div>1</div> <div>2</div> <div>3</div></div>转化为React.createElement( "div", {}, React.createElement("div", {}, ...chidren), React.createElement("div", {}, ...chidren), React.createElement("div", {}, ...chidren))代码块
复制代码
废话不多说直接上代码,下面是我写的一个简单的babel-plugin来对jsx语法进行解析
var generator = require("@babel/generator").defaultfunction buildAttrsCall (attribs, t){ let properties = []; attribs.forEach(attr => { let name = attr.name.name; let value = attr.value; properties.push(t.objectProperty(t.stringLiteral(name), value)) }); return t.ObjectExpression(properties);}const createVisitor = (t) => { const visitor = {}; visitor.JSXElement = { // 为什么是exit,因为jsx是DFS而不是BFS; exit(path, file){ let openingPath = path.get("openingElement"); let children = t.react.buildChildren(openingPath.parent); let tagNode = t.identifier(openingPath.node.name.name); // 创建React.createElement let createElement = t.memberExpression(t.identifier("React"),t.identifier("createElement")); // 创建属性 let attribs = buildAttrsCall(openingPath.node.attributes, t); // 创建React.createElement(tag, attrs, ...chidren)表达式 let callExpr = t.callExpression(createElement, [tagNode, attribs, ...children]); path.replaceWith(t.inherits(callExpr, path.node)); } } return { visitor, // 配置jsx解析器 inherits:() => { return { manipulateOptions(opts, parserOpts) { parserOpts.plugins.push("jsx"); } };
} }}module.exports = function(babel){ const t = babel.types; return createVisitor(t);}
复制代码
创建 tagNode 变量
创建 React.createElement 表达式
创建 attribs 对象
创建 React.createElement("div", {}, ...children)表达式
最后替换 node
效果如下
源代码如下
const a = <div className="name" age="12"> <div>1</div> <div>2</div> <div>3</div></div>;
复制代码
编译之后
var a = React.createElement(div, { "className": "name", "age": "12"}, React.createElement(div, {}, "1"), React.createElement(div, {}, "2"), React.createElement(div, {}, "3"));console.log(a);
复制代码
划线
评论
复制
发布于: 刚刚阅读数: 3
夏天的味道123
关注
还未添加个人签名 2022-09-05 加入
还未添加个人简介









评论