写点什么

react 的 jsx 语法是怎样解析的

  • 2022-12-12
    浙江
  • 本文字数:2390 字

    阅读完需:约 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是如何输出上图所示的结构的?

环境配置

安装reactbabel


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"
复制代码

写个函数来模拟它的执行过程

为了便于理解 我们把<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);}
复制代码


参考 React面试题详细解答


  1. 创建 tagNode 变量

  2. 创建 React.createElement 表达式

  3. 创建 attribs 对象

  4. 创建 React.createElement("div", {}, ...children)表达式

  5. 最后替换 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);
复制代码


用户头像

还未添加个人签名 2022-09-05 加入

还未添加个人简介

评论

发布
暂无评论
react的jsx语法是怎样解析的_React_夏天的味道123_InfoQ写作社区