函数式编程
- 纯函数
reducer必须是一个纯函数,即没有副作用的函数,不修改输入值,相同的输入一定会有相同的输出 - 不可变值
state必须是不可变值,否则在shouldComponentUpdate中无法拿到更新前的值,无法做性能优化操作。
vdom 和 diff 算法
JSX 本质
- 是
React.createElement函数React.createElement(tag, props, child1, child2, child3)React.createElement(tag, props, [child1, child2, child3])
- 执行生成
vnode
javascript
const elem = <div>
<p>aaa</p>
<p style={{ color: 'red' }}>bbb</p>
</div>;
javascript
const elem = React.createElement(
"div",
null,
React.createElement("p", null, "aaa"),
React.createElement("p", { style: { color: "red" } }, "bbb")
);
javascript
const lisElem = <div>
{
this.state.list.map((item, index) => {
return (<span key={item.id}>
{item.name}
</span>);
})
}
</div>;
javascript
const listElem = React.createElement(
"div",
null,
(void 0).state.list.map((item, index) => {
return React.createElement("span", { key: item.id }, item.name);
})
);
合成事件
react的事件不是原生事件MouseEvent,而是合成事件SyntheticEventreact16是挂载到document上的;react17开始是挂载到root上的- 事件处理函数交给合成事件,事件冒泡到
document/root上进行处理
合成事件的好处:
- 更好的兼容性和跨平台:比如
react-native - 全部挂载到
document/root上,减少内存消耗,避免频繁解绑 - 方便事件的统一管理(事务机制)
React17 开始挂载到 root 组件上:
document只有一个,root有多个,有利于多个react版本共存,例如:微前端
setState 和 batchUpdate
setState 主流程
- 异步:左边分支
- 非异步:右边分支
isBatchingUpdates
javascript
class ListDemo extends React.Component
constructor(props) {
// ...
}
render() {
// ...
}
increase = () => {
// 开始: 处于 batchUpdate
// isBatchingiUpdates = true
this.setState({
count: this.state.count + 1
});
// 结束
// isBatchingUpdates = false
}
}
javascript
class ListDemo extends React.Component
constructor(props) {
// ...
}
render() {
// ...
}
increase = () => {
// 开始: 处于 batchUpdate
// isBatchingUpdates = true
setTimeout(() => {
// 此时 isBatchingUpdates 是 false
this.setState({
count: this.state.count + 1
});
});
// 结束
// isBatchingUpdates = false
}
}
javascript
componentDidMount() {
// 开始: 处于 batchUpdate
// isBatchingUpdates = true
document.body.addEventListener('click', () => {
// 此时 isBatchingUpdates 是 false
this.setState({
count: this.state.count + 1
});
console.log('count in body event', this.stae.count);
});
// 结束
// isBatchingUpdates = false
}
哪些能命中 batchUpdate 机制:
- 生命周期(和它调用的函数)
React中注册的事件(和它调用的函数)React可以"管理"的入口
transaction 事务机制
javascript
class ListDemo extends React.Component
constructor(props) {
// ...
}
render() {
// ...
}
increase = () => {
// 开始:处于 batchUpdate
// isBatchingUpdates = true
// 其他任何操作
// 结束
// isBatchingUpdates = false
}
}
javascript
transaction.initialize = function() {
console.log('initialize');
};
transaction.close = function() {
console.log('close');
};
function method() {
console.log('abc');
}
transaction.perform(method);
// 输出 'initialize'
// 输出 'abc'
// 输出 'close'
react 组件渲染过程
JSX如何渲染为页面:- 初始化时候继承
props和 生成state - 通过
render()函数 生成vnode patch(elem, vnode):通过patch函数将vonde更新到dom上
- 初始化时候继承
setState之后如何更新页面:setSate(newState)->dirtyComponents(可能有子组件):通过setState产生新的state,存到dirtyComponent进行异步更新- 通过
render()函数生成新的vnode patch(elem, vnode):再通过patch函数用 newVnode 去更新旧的 vnode
react-fiber
react 的 patch 被拆分为两个阶段:
reconciliation阶段:执行diff算法,纯js计算commit阶段:将diff结果渲染成dom
背景
js是单线程的,且和dom渲染共用一个线程- 当组件足够复杂,组件更新时计算和渲染压力都很大
- 同时再有
dom操作需求,比如动画、鼠标拖拽等,那么将会卡顿
解决方案:fiber
fiber
react内部的运行机制,开发者体会不到- 将
reconciliation阶段进行任务拆分(commit无法拆分) dom需要渲染时暂停,空闲时恢复- 通过
window.requestidleCallback进行控制(并非所有浏览器支持)
FQA
JSX的本质是什么?jsx的本质是React.createElement函数,执行生返回vnode。
react组件更新渲染的过程。- 初始化时候继承
props和 生成state - 通过
render()函数生成vnode - 再通过
patch函数将vonde渲染成真实dom - 通过
setState修改产生新的state - 触发
re-render生成新的vnode - 再通过
patch函数用newVnode去更新旧的vnode
- 初始化时候继承
react为什么要将patch过程拆分成reconciliation和commit两个阶段?- 因为
js是单线程的,且和dom渲染共用一个线程 - 当组件很复杂的时候,组件更新时计算和渲染压力都很大
- 同时再有
dom操作需求,比如动画、鼠标拖拽等,那么将会卡顿
- 因为



