源码解析
源码解析
ReactElement创建虚拟DOM节点对象
const ReactElement = function (type, key, ref, self, source, owner, props) {
const element = {
type: type,
key: key,
ref: ref,
props: props,
...
} // element结构
...
if (Object.freeze) {
Object.freeze(element.props) // 冻结对象
Object.freeze(element)
}
}jsx解析代码并使用ReactElement创建节点对象。服务端渲染时self和source是必传参数,客户端渲染传的是undefinedReact.createElement、React.cloneElement2个方法基于ReactElementComponent创建组建
function Component(props, context, updater) {
this.props = props
this.context = context
...
this.updater = updater || ReactNoopUpdateQueue
}
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState')
// 创建此组件实例的renderer(可能是react-dom/react-native等等)来执行任务队列
}
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueSetState(this, callback, 'forceUpdate')
}虚拟DOM
虚拟DOM是描述DOM树的对象,目的是将所有操作统一处理完之后再进行DOM更新。(常规的树比较算法的时间复杂度为立方阶O(n3),react diff 为线性阶O(n))
tree diff对比2棵树的同一层次的节点component diff通过不同组件类型生成不同的树形结构,同时允许设置shouldComponentUpdate()对进行优化element diff通过唯一key进行比较
key的作用
准确判断当前节点是否在旧集合中
减少遍历次数
生命周期
初始化阶段
constructorstatic getDerivedStateFromProps()UNSAFE_componentWillMount()render()componentDidMount()
更新阶段
UNSAFE_componentWillReceiveProps()(onyl props改变)static getDerivedStateFromProps()(onyl props改变)shouldComponentUpdate()UNSAFE_componentWillUpdate()render()getSnapshotBreforeUpdate()componentDidUpdate()
卸载阶段:
componentWillUnmount()错误处理:
static getDerivedStateFromError()componentDidCatch()
更新机制
setState后会进入到shouldComponentUpdate钩子函数,由该函数的返回值决定是否调用render如果包含子组件,那么父组件
render完成后进入子组件的生命周期,直到子组件的生命周期完成后,回到父组件render的后续生命周期render返回虚拟DOM,在其后进行diff计算,从而决定是否需要重新渲染PureComponent是浅比较
// 父子组件初始化阶段的生命周期
Parent.constructor
Parent.getDerivedStateFromProps
Parent.render
Child.constructor
Child.getDerivedStateFromProps
Child.render
Child.componentDidMount
Parent.componentDidMountgetDerivedStateFromPros(nextProps, prevState)
返回null表示不做任何改变
shouldComponentUpdate(nextProps, nextState)
返回false控制组件不更新
getSnapshotBreforeUpdate(prevProps, prevState)
其返回值会传入componentDidUpdate(prevProps, prevState, fromSnapData)
setState
第一个参数可以是对象,也可以是
updater函数。第二个参数是回调函数,在
componentDidUpdate后执行setState更新过程是异步的,会合并所有的更新(如果是addEventListener或setTimeout里,则不会合并更新)
forceUpdate
跳过shouldComponentUpdate()直接调用render()钩子函数
性能优化
谨慎分配
state,避免不必要的render状态合并
使用纯函数式组件
使用高阶组件替代继承
控制更新
副作用代码放在
commit阶段
Fiber
参考: 图解Fiber架构
除了React元素树(虚拟DOM)之外,有一个用于保存状态的内部实例树(Host Instance)。该内部实例树的新实现以及操作树的算法被成为Fiber。Fiber也是一个任务调和器:
可分片(拆分任务)
可中断(执行一个任务后可回头继续执行未完成的任务)
具备优先级(任务优先级高的先执行)
function FiberNode(tag, pendingProps, key, mode) {
this.tag = tag
this.key = key
...
// Fiber
this.return = null
this.child = null
this.sibling = null
this.index = 0
...
// effects
this.effectTag = NoEffect
this.nextEffect = null
...
}Fiber Tree的构建
通过child找子节点、sibling找兄弟节点、return找父节点的循环方法构建Fiber tree。
function List () {
return [1,2,3].map((i) => <span key={i}>{i}</span>)
}
function App () {
return [
<button>btn</button>,
<List />,
<div></div>
]
}
ReactDom.render(<App />, document.getElementById('root'))Hook
hooks的数据作为Fiber组件上的节点。
调用原理
基于FunctionalComponent
首先调用
prepareToUseHooks初始化一些模块内的全局变量调用
finishHooks
事件合成
事件委托:把所有事件绑定到最外层,使用一个统一的事件监听器
自动绑定:每个事件的上下文均指向所属组件
setState
setState的更新是异步的。在非异步方法中,无论在多少个组件中调用多少个setState,它们都在最后一次setState后,全部放入enqueueSetState更新队列,然后执行一次统一的更新
// 更新过程
enqueueSetState(instance) {
// 获取fiber
const fiber = getInstance(instance)
// 计算当前时间
const currentTime = requestCurrentTime()
// 计算fiber对象过期时间
const expirationTime = computeExpirationForFiber(currentTime, fiber)
// 创建update对象
const update = createUpdate(expirationTime)
// 进入待更新队列
enqueueUpdate(fiber, update)
// 任务调度
scheduleWork(fiber, expirationTime)
}forceUpdate
// 更新过程 和 setState一样,唯一不同是更新对象的tag值为ForceUpdate(2)
// UpdateState: 0 更新
// ReplaceState: 1 替换
// ForceUpdate: 2 强制更新
// CaptureUpdate: 3 捕获性更新
function createUpdate() {
return {
tag: UpdateState,
}
}ssr
renderToString
Last updated
Was this helpful?