前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >react之jsx编译原理

react之jsx编译原理

作者头像
QQ音乐前端团队
发布2019-07-02 12:40:20
3.1K0
发布2019-07-02 12:40:20
举报
文章被收录于专栏:QQ音乐前端团队专栏

使用react的朋友想必对jsx的语法相当熟悉,简单点来说,就是JavaScript和html可以混着写,灵活快速,而且可以一目了然的看清楚DOM的结构,当然jsx需要经过babel编译为javascript对象,再经过渲染插入到页面上。 接下来我们来探讨几个问题:

  • react内部是如何处理JavaScript和html混写的代码?
  • 组件名为啥首字母一定要大写?
  • 在花括号{}里边内容,比如是一行表达式 var a = 'hello world'; 为啥会报错?

下边来具体看看jsx处理逻辑,上例子:

1、demo01

代码语言:javascript
复制
const name = 'world';<h1 title="title" ref="title">hello, { name }</h1>

babel编译为:

代码语言:javascript
复制
var name = 'world';React.createElement("h1", {  title: "title",  ref: "title"}, "hello, ", name);

显然关键部分是 React.createElement, 看官网定义:

代码语言:javascript
复制
React.createElement(  type,  [props],  [...children])

作用是创建并返回指定类型的新 React元素。其中的type类型参数既可以是标签名字符串(如上边demo1的h1标签),也可以是React组件类型(class组件或函数组件),或是React fragment类型。

第2个参数是props,上createElement源码,看看属性title,ref的处理:

react v16.8.6 /packages/react/src/ReactElement.js

代码语言:javascript
复制
...const hasOwnProperty = Object.prototype.hasOwnProperty;const RESERVED_PROPS = {    key: true,    ref: true,    __self: true,    __source: true,};.../** * Create and return a new ReactElement of the given type. * See https://reactjs.org/docs/react-api.html#createelement */export function createElement(type, config, children) {    let propName;    // Reserved names are extracted    const props = {}; //用于存储元素属性    let key = null; //存储key值    let ref = null; //存储ref值    let self = null;    let source = null;    if (config != null) {        if (hasValidRef(config)) {            ref = config.ref; //传入的config有ref属性且config.ref !== undefined,赋值给上边定义的变量ref        }        if (hasValidKey(config)) {            key = '' + config.key; //传入的config有key属性且config.key !== undefined,赋值给上边定义的变量ref        }        self = config.__self === undefined ? null : config.__self;        source = config.__source === undefined ? null : config.__source;        // Remaining properties are added to a new props object        //遍历传入的config判断属性不是对象原型链上的,也不属性RESERVED_PROPS(key/ref等),赋到上边定义的props对象上        for (propName in config) {              if (                hasOwnProperty.call(config, propName) &&                !RESERVED_PROPS.hasOwnProperty(propName)            ) {                props[propName] = config[propName];            }        }    }  ...}

结论:定义的属性ref,key会直接挂在生成元素上,而其他属性挂在props上

第3个参数是children,demo1的编译生成的"hello, ", name 对应的都是子元素children,源码处理如下:

代码语言:javascript
复制
  // Children can be more than one argument, and those are transferred onto  // the newly allocated props object.    const childrenLength = arguments.length - 2;    if (childrenLength === 1) {        props.children = children;    } else if (childrenLength > 1) {        const childArray = Array(childrenLength);        for (let i = 0; i < childrenLength; i++) {            childArray[i] = arguments[i + 2];        }        ...        props.children = childArray;    }

子元素可以是一个或者多个,都会被挂在props.children上,demo1的编译生成的"hello, ", name就是多个文本节点

demo1代码最终生成的虚拟DOM为为:

再经过ReactDOM.render()方法渲染到页面上

2、demo2

我们来看看当组件名首字母是小写的时候,为啥会报错

代码语言:javascript
复制
const Comp = () => <h1>hello, world</h1>const App = () => {    return (      <Comp />    )}

babel编译为:

代码语言:javascript
复制
var Comp = function Comp() {  return React.createElement("h1", null, "hello, world");};var App = function App() {  return React.createElement(Comp, null); //返回React组件类型元素};

而将组件名Comp首字母改为小写comp,babel则编译为:

代码语言:javascript
复制
var comp = function comp() {  return React.createElement("h1", null, "hello, world");};var App = function App() {  return React.createElement("comp", null); //type为标签名字符串类型};

显然html没有comp标签,故无法正常渲染

3、demo3

从demo1可知,花括号{}里边的内容也会被当做子元素处理,我们来看看某些不支持,或者编译不符合预期的写法

1)属性中输出javascript变量,不能加引号,不然会被当做字符串而不被解析

代码语言:javascript
复制
const name = 'world';<h1 title={name}  name="{name}" >hello, { name }</h1>

babel编译为

代码语言:javascript
复制
var name = 'world';React.createElement("h1", {  title: name,  name: "{name}"  //字符串}, "hello, ", name);

2)花括号里边内容是js表达式,如

代码语言:javascript
复制
<h1>{ var a = 1; } </h1>

理论上转换后应该是React.createElement("h1", null, var a = 1;), 显然子元素是var a = 1;是不正确的

3)不支持if语句、for语句等等 比如

代码语言:javascript
复制
var fruits = ['apple', 'banana', 'pear'];<ul>  {    if(fruits.length > 0) {      fruits.map(item => <li>{item}</li>)    }  }</ul>

编译不通过,理由同2,可以调整为

代码语言:javascript
复制
var fruits = ['apple', 'banana', 'pear'];<ul>  {    fruits.length > 0 && fruits.map(item => <li>{item}</li>)  }</ul>

babel编译为:

代码语言:javascript
复制
var fruits = ['apple', 'banana', 'pear'];React.createElement("ul", null, fruits.length > 0 && fruits.map(function (item) {  return React.createElement("li", null, item);}));

还有很多其他的情况,这边就不一一列举,大家在使用react的过程中遇到问题时,可以关联源码理解,可能也就想明白了。

小结

jsx代码经过babel编译为React.createElement(type,[props],[...children])创建的React元素,容易出问题的是花括号里边编写的代码babel编译失败,原因是不符合子元素定义。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 QQ音乐前端团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、demo01
  • 2、demo2
  • 3、demo3
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档