JSX

JSX 的本质

我们都知道 React 中独特的 JSX 结构,可以像写 js 一样写 html 结构。那么 JSX 到底是什么呢?

const element = <div title='hello'>hello world!</div>;

使用 babel 在线编译的地址https://www.babeljs.cn/replopen in new window 测试,我们会发现 JSX 被编译成了 React.createElement

const element = /*#__PURE__*/ React.createElement(
  'div',
  {
    title: 'hello'
  },
  'hello world!'
);

其实, JSX 就是 React.createElement 的语法糖。

我们手写 React 的第一步就是实现 React.createElement 函数。

React.createElement 做了什么

通过打印 element,我们可以发现 element 是一个 object。也就是说,React.createElement执行后返回了一个对象。

我们可以看到,React.createElement 接收了三个参数 type,props 和 ...children, 最终返回了一个对象。对象里面有多个属性,我们这里只关心 type 和 props。 返回结构里,children 成为了 props 的一个属性。

手写 React.createElement

我们在 src 下新建 mini-react 目录,并创建两个文件 react.jsreact-dom.js。将来我们手写 React 相关的方法会放到这两个文件里面。

在 react.js 添加如下内容:

// react.js
function createElement(type, props, ...children) {
  return {
    type,
    props: { ...props, children }
  };
}

export default { createElement };

这里,我们创建了一个对象,且把 children 属性添加到了 props 里。可是, children 可能存在纯文本的情况。DOM 针对标签元素和文本元素的处理方式不太一样(在 render 原理会深入讲解),因此我们做个区分:文本元素类型是 TEXT_ELEMENT,且有一个 nodeValue 属性,是它本身这个字符串。

然后我们再完善下代码,如下:

// react.js
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) => (typeof child === 'object' ? child : createTextElement(child)))
    }
  };
}

function createTextElement(text) {
  return {
    type: 'TEXT_ELEMENT',
    props: {
      nodeValue: text,
      children: []
    }
  };
}

export default { createElement };

src/index.js 里引用 mini-react,并使用魔法注释 /** @jsx miniReact.createElement */。这句注释能够让 babel 编译下面的 JSX 的时候,使用我们的miniReact.createElement方法。

import React from 'react';
import ReactDOM from 'react-dom';

import miniReact from './mini-react/react';
import miniReactDOM from './mini-react/react-dom';

/** @jsx miniReact.createElement */
const element = <div title='hello'>hello world!</div>;
console.log(element);

const container = document.getElementById('root');
// ReactDOM.render(element, container);



 
 

 





此时,查看我们打印的 element,它的结构应如下:

{
    "type": "div",
    "props": {
        "title": "hello",
        "children": [
            {
                "type": "TEXT_ELEMENT",
                "props": {
                    "nodeValue": "hello world!",
                    "children": []
                }
            }
        ]
    }
}

至此,我们完成了 React.createElement函数的手写。