函数式编程
- 纯函数
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
,而是合成事件SyntheticEvent
react16
是挂载到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
操作需求,比如动画、鼠标拖拽等,那么将会卡顿
- 因为