react 是怎么运行的?
import React from 'react';
import ReactDOM from 'react-dom';
const App = <div className="title" style={{color:'red'}}>hello world</div>
console.log('App', App)
ReactDOM.render(
App,
document.getElementById('root')
);
react 运行的主要3阶段:
1. jsx 转成 js 代码
由于 js 不认识 jsx 代码,所以需要将下面 jsx 语法转化成 js 代码
<div className="title" style={{color:'red'}}>hello world</div>
点击此 jsx to react 复制上述代码进去即可转化成 js 代码, 代码如下:
React.createElement("div", {
className: "title",
style: {
color: 'red'
}
}, "hello world");
在 create-react-app 项目中,我们通过 react-scripts start
可以运行 react 项目是因为 react-scripts 将 jsx 转化成了 js 代码。
2. js 代码生成 vdom
现在知道为什么没有使用 react 都需要 import React from 'react';
引入了吧, 因为 jsx 转 js 后的代码会用到 react。
执行如下方法会生成什么呢?
React.createElement("div", {
className: "title",
style: {
color: 'red'
}
}, "hello world");
会生成如下对象:
{
'$$typeof': Symbol(react.element), // 标记是 react 节点
type: 'div', // 标签类型
key: null,
ref: null,
props: { // 标签的属性
className: 'title',
style: { color: 'red' },
children: 'hello world'
}
}
该对象就是通过 js 对象的形式描述了一个 html 节点,也就是所谓的 vdom (虚拟dom), 后续 diff 算法就是直接给它进行对比更新dom
3. vdom 转化成 dom 挂载到 #root
ReactDOM.render(
{
'$$typeof': Symbol(react.element), // 标记是 react 节点
type: 'div', // 标签类型
key: null,
ref: null,
props: { // 标签的属性
className: 'title',
style: { color: 'red' },
children: 'hello world'
}
},
document.getElementById('root')
);
ReactDOM.render 内部主要执行过程如下:
function render(vdom, container) {
let newDOM = createDOM(vdom); // 对象转 dom
container.appendChild(newDOM); // 生成的节点挂载到 root 节点里面
}
function createDOM(vdom) {
let { type, props } = vdom;
let dom;
if (type === REACT_TEXT) {
dom = document.createTextNode(props.content);
} else {
dom = document.createElement(type);
}
if (props) {
updateProps(dom, {}, props);
if (typeof props.children == "object" && props.children.type) {
mount(props.children, dom);
} else if (Array.isArray(props.children)) {
reconcileChildren(props.children, dom);
}
}
vdom.dom = dom;
return dom;
}
function updateProps(dom, oldProps={}, newProps={}) {
for (let key in newProps) {
if (key === 'children') {
continue;
} else if (key === 'style') {
let styleObj = newProps[key];
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr];
}
}else {
dom[key] = newProps[key];
}
}
for(let key in oldProps){
if(!newProps.hasOwnProperty(key)){
dom[key] = null;
}
}
}
function reconcileChildren(childrenVdom, parentDOM) {
for (let i = 0; i < childrenVdom.length; i++) {
let childVdom = childrenVdom[i];
mount(childVdom, parentDOM);
}
}
const ReactDOM = { render };
此时打开浏览器我们就看见了 hello world
本系列代码 |
链接 |
GitHub |
https://github.com/shunyue1320/mini-react/tree/react-01 |
Gitee |
https://gitee.com/shunyue/mini-react/tree/react-01/ |