我们从 react 应用的入口开始对源码进行分析,创建一个简单的 hello, world 应用:
import React, {
Component } from 'react';
import ReactDOM from 'react-dom';
export default class App extends Component {
render() {
return <div>hello, world</div>;
}
}
ReactDOM.render(<App />, document.getElementById('root'));
我们注意到,我们在 App 组件中直接写了 return
的 jsx 语句,那么 jsx 语法是如何被浏览器识别执行的呢?
另外我在第一次学习 react 的时候,就有一个疑惑: import React, { Component } from 'react'
这段代码中,React
似乎在代码中没有任何地方被用到,为什么要引入呢?
我们在 react16.8 版本的代码中,尝试将 React
的引用去掉:
// import React, { Component } from 'react';
import {
Component } from 'react'; // 去掉 React 的引用
import ReactDOM from 'react-dom';
export default class App extends Component {
render() {
return <div>hello, world</div>;
}
}
ReactDOM.render(<App />, document.getElementById('root'));
运行应用程序,发现会提示 'React' must be in scope when using JSX
的 error:
这是因为上述的类组件 render 中返回了
的 jsx 语法,在React16版本及之前,应用程序通过 @babel/preset-react 将 jsx 语法转换为 React.createElement
的 js 代码,因此需要显式将 React 引入,才能正常调用 createElement。我们可以在 Babel REPL 中看到 jsx 被 @babel/preset-react 编译后的结果
React17版本之后,官方与 bbel 进行了合作,直接通过将 react/jsx-runtime
对 jsx 语法进行了新的转换而不依赖 React.createElement
,转换的结果便是可直接供 ReactDOM.render
使用的 ReactElement 对象。因此如果在React17版本后只是用 jsx 语法不使用其他的 react 提供的api,可以不引入 React
,应用程序依然能够正常运行。
更多有关于 React jsx 转换的内容可以去看官网了解:介绍全新的JSX转换,在这里就不再过多展开了。
虽然现在 react17 之后我们可以不再依赖 React.createElement
这个 api 了,但是实际场景中以及很多开源包中可能会有很多通过 React.createElement
手动创建元素的场景,所以还是推荐学习一下React.createElement源码。
React.createElement
其接收三个或以上参数:
'div'
或者 'span'
等;也可以是 React组件 类型(class组件或者函数组件);或者是 React fragment 类型。函数中会对参数进行一系列的解析,源码如下,对源码相关的理解都用注释进行了标记:
export function createElement(type, config, children) {
let propName;<