阅读须知
- 文章参考的preact版本是10.5.13
- 文章会省略大部分逻辑,比如hydrating,context,isSvg等。所以如果有大佬点进来需谨慎。
- 简略版本代码
diff概览
- 对于组件(函数 or 类组件):执行组件的生命周期(函数组件会被包装为类组件),调用diffChildren继续递归。
- 对于原生dom节点: 调用diffElementNodes.
export function diff (
parentDom,
newVNode,
oldVNode,
commitQueue,
oldDom,
) {
const newType = newVNode.type;
try {
let c, isNew, oldProps, oldState;
let newProps = newVNode.props;
outer: if (typeof newType == 'function') {
// 1.判断是否初始化
// 2. 执行对对应的生命周期
// 3. 继续对比子节点
}
// 原生节点的处理方式
else {}
}
组件处理
判断是否初始化
- 根据oldVnode._component 判断是否完成初始化,
- 初始化完成直接使用旧的preact实例。
- 初始化未完成:完成组件实例化。标识 isNew = true,后续执行初始化的生命周期。函数组件和Fragment会被封装为类组件。
- 做法:实例化一个空类组件,在类组件的render函数去执行函数组件,这也是为什么每一次更新函数组件都会重新执行,因为每一次更新都会重新调用render函数。
- 好处:函数组件和类组件的逻辑统一。
// 初始化已完成:调用vnode._component(React Component组件实例)
// 初始化未完成:完成React组件初始化
if (oldVNode._component) {
newVNode._component = c = oldVNode._component;
} else {
// Class Component, prototype和render存在时
// Function Component, 非Class Component
if ('prototype' in newType && newType.prototype.render) {
newVNode._component = c = new newType(newProps);
} else {
// 函数组件或者Fragment 通过类组件去实例化 Component原型上有setState, forceUpdate, render = Fragment => (return this.props.children)
newVNode._component = c = new Component(newProps);
c.constructor = newType;
c.render = doRender;
}
// 初始化数据
c.props = newProps;
c.state = c.state || {};
c._renderCallbacks = [];
isNew = c._dirty = true; // _dirty主要是做批量更新的
}
- 组件本质是原型链,平时使用的state,props都在原型链上面
执行生命周期
- preact目前还兼容旧版生命周期,(componentWillMount,componentWilReceiveProps),根据是否定义getDerivedStateFromProps做判断是否调用
数据的变化
生命周期执行过程中state,props会有三种状态,oldState, state, _nextState,oldProps, props, newProps。分别为旧状态(oldState) render能拿到的数据(state),最新的数据(_nextState).
- c.state 和 c.props, c._nextState是组件的属性,其余是变量。主要是为了生命周期函数可以拿到不同状态的props和state。
- 因为有一些生命周期执行过程中会修改state和props,会造成数据变化。并且有一些生命周期需要拿到最新的数据,有一些需要拿到旧数据。所以需要备份一份作为最新的数据,一份作为旧数据。render执行前把最新的数据赋值到组件的属性(c.state, c.props)上,代码中通过this.state, this.props可以拿到最新的数据。
// 复制一份作为最新数据,一份作为旧数据 c._nextState = c._nextState || Object.assign({}, c.state); oldProps = c.props; oldState = c.state; // 执行render之前的生命周期 // 将最新的状态赋值到component上面 c.props = newProps; c.state = c._nextState; c._vnode = newVNode; c._parentDom = parentDom; // 执行render
生命周期执行
- 子组件执行完render之后才会执行父组件的componentDidMount,componentDidUpdate。会将这两个存储在组件的_renderCallbacks数组上。等到组件执行完成之后,再执行commitQueue去执行剩余的生命周期。
// 执行生命周期
if (newType.getDerivedStateFromProps) {
Object.assign(
c._nextState,
newType.getDerivedStateFromProps(newProps, c._nextState),
)
}
if (isNew) {
if (c.componentDidMount) {
c._renderCallbacks.push(c.componentDidMount);
}
} else {
if (
!c._force &&
c.shouldComponentUpdate &&
c.shouldComponentUpdate(newProps, c._nextState) === false
) {
c.props = newProps;
c.state = c._nextState;
c._vnode = newVNode;
newVNode._dom = oldVNode._dom;
newVNode._children = oldVNode._children;
newVNode._children.forEach(vnode => {
if (vnode) vnode._parent = newVNode;
});
if (c._renderCallbacks.length) {
commitQueue.push(c);
}
break outer;
}
if (c.componentDidUpdate) {
c._renderCallbacks.push(() => {
c.componentDidUpdate(oldProps, oldState);
});
}
}
let tmp = c.render(c.props, c.state);
if (!isNew && c.getSnapshotBeforeUpdate) {
c.getSnapshotBeforeUpdate(oldProps, oldState);
}
对比子节点
let isTopLevelFragment =
tmp && tmp.type === Fragment && tmp.key == null;
let renderResult = isTopLevelFragment ? tmp.props.children : tmp;
// 对比子节点
diffChildren(
parentDom,
Array.isArray(renderResult) ? renderResult : [renderResult],
newVNode,
oldVNode,
commitQueue,
oldDom,
);
if (c._renderCallbacks.length) {
commitQueue.push(c);
}
c._dirty = false;
c._force = false;
原生节点处理
newVNode._dom = diffElementNodes(
oldVNode._dom,
newVNode,
oldVNode,
excessDomChildren,
commitQueue,
);