初始化
jsx —经过babel转换—> createElement(创建vdom)——> render(vdom -> 真实dom)
更新
state/props改变 ——> update(diff…) ——> render(vdom -> 真实dom)
React渲染的过程实际上是由JSX创建React元素作为虚拟dom节点,调用render方法创建一棵虚拟dom树,然后映射到真实dom上由浏览器渲染出来。state和props改变时,会再次调用render生成一棵新的虚拟dom树,通过diff算法计算新旧两棵虚拟dom的差异,再异渲染到真实dom上
JSX
JSX是一个 JavaScript 的语法扩展,在HTML模板中嵌入JavaScript语法。由于React认为组件渲染逻辑与UI视图存在内在耦合,而JSX 可以很好地描述UI应该呈现出它应有交互的本质形式,所以React建议使用JSX语法。JSX完全可以实现JavaScript原有的功能。
JSX本质是React.createElement的语法糖,如下图例子所示,JSX会被Babel编译为createElement函数调用
createElement
实现转义后的jsx渲染到浏览器页面
挂载是递归过程
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: [],
},
};
}
function render(element, container) {
const dom =
element.type == "TEXT_ELEMENT"
? document.createTextNode("")
: document.createElement(element.type);
const isProperty = (key) => key !== "children";
Object.keys(element.props)
.filter(isProperty)
.forEach((name) => {
dom[name] = element.props[name];
});
// 一旦我们开始渲染,我们不会停止,直到我们渲染了完整的元素树。
// 如果元素树很大,可能会阻塞主线程太久。如果浏览器需要做高优先级的事情,
// 比如处理用户输入或保持动画流畅,它必须等到渲染完成。
element.props.children.forEach((child) => render(child, dom));
container.appendChild(dom);
}
const Didact = {
createElement,
render,
};
// https://stackoverflow.com/questions/53803466/what-does-the-comment-jsx-jsx-do-in-the-emotion-css-in-js-library
// 这是一个自定义 pragma,它告诉 jsx 转换器,在这种情况下,babel-plugin-transform-react 使用什么函数将您的 jsx 转换为纯 javascript。
/** @jsx Didact.createElement */
const element = (
<div style="background: red">
<h1>Hello World</h1>
<h2 style="text-align:right">from Didact</h2>
</div>
);
const el = document.getElementById("root");
Didact.render(element, el);
存在问题
一旦我们开始渲染,我们不会停止,直到我们渲染了完整的元素树。如果元素树很大,可能会阻塞主线程太久。如果浏览器需要做高优先级的事情,比如处理用户输入或保持动画流畅,它必须等到渲染完成。
react完整的createElement
- $$typeof属性用于检测节点是否为合法的React元素
- type属性即节点类型
- key用作节点的唯一标识
- ref用于获取真实的dom节点
- props包含了节点的默认属性和用户给它定义的属性以及子节点
- _owner标识该节点隶属于哪个组件