例子
实现
新增$props
例子中用了this.props,我们把新增一个$props对象
componentPublicInstanceProxyHandlers.ts
/*
* @Author: Lin zefan
* @Date: 2022-03-23 17:52:57
* @LastEditTime: 2022-04-09 10:43:19
* @LastEditors: Lin ZeFan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\componentPublicInstanceProxyHandlers.ts
*
*/
// 扩展的实例Map
const PublicInstanceMap = {
$el: (i) => i.vnode.el,
$slots: (i) => i.slots,
$props: (i) => i.props,
};
实现
组件更新
思考
- 前面借助了effect,能够侦听响应式数据更新,并且判断了数据初始化或者更新
- 更新component依旧是需要借助effect来触发更新的
- 之前实现effect的时候,实现了runner功能,可以利用runner来触发更新
- 要拿到runner,得先在实例上存储runner
- 要拿到当前实例,还要储存当前的实例
render.ts
function mountComponent(n2, container, parentComponent, anchor) {
const instance = (n2.component = createComponentInstance(
n2,
parentComponent
));
}
function setupRenderEffect(instance, container, anchor) {
instance.runner = effect(() => {})
}
component.ts
export function createComponentInstance(initVNode, parent) {
const component = {
// other code
runner: null,
component: null
};
// other code
}
我们把runner和实例拿到后,可以开始写我们的逻辑了
- 获取当前组件实例
- 保存当前新的vnode,在触发更新时做数据更新
- 利用runner来触发更新
触发effect更新
// render.ts
function updateComponent(n1, n2) {
const instance = (n2.component = n1.component);
instance.next = n2;
instance.runner();
}
在effect更新逻辑中,加上判断是否有新的vnode,有则更新
// render.ts
function setupRenderEffect(instance, container, anchor) {
instance.runner = effect(() => {
if (!instance.isMounted) {
} else {
const { proxy, next, vnode } = instance;
if (next) {
// 保存当前的dom节点,因为新vnode没有走创建流程,所以没有el
next.el = vnode.el;
updateComponentPreRender(instance, next);
}
// other code
}
})
}
function updateComponentPreRender(instance, nextVNode) {
// 更新当前虚拟节点
instance.vnode = nextVNode;
// 更新当前的props
instance.props = nextVNode.props;
// 把next清空
nextVNode = null;
}
优化更新逻辑
现在不管子组件需不需要更新,都会触发runner更新,有时候我们只是当前组件更新了,并没有涉及子组件的更新,所以需要做进一步优化判断
组件更新判断
function updateComponent(n1, n2) {
/** 更新组件
* 1. 获取当前组件实例
* 2. 对比props数据,若有变更,则走更新逻辑;否则直接将vnode替换即可
* 2. 保存当前新的vnode,在触发更新时做数据更新
* 3. 利用runner来触发更新逻辑
*/
const instance = (n2.component = n1.component);
/** 需要更新props
* 1. 保存新的vnode数据
* 2. 触发runner再次调用更新逻辑
*/
if (shouldUpdateComponent(n1, n2)) {
instance.next = n2;
instance.runner();
} else {
// 不需要更新props,更新当前vnode即可
n2.el = n1.el;
instance.vnode = n2;
}
}
新增shouldUpdateComponent函数,判断props是否有差异
/*
* @Author: Lin ZeFan
* @Date: 2022-04-09 12:04:52
* @LastEditTime: 2022-04-09 12:04:53
* @LastEditors: Lin ZeFan
* @Description:
* @FilePath: \mini-vue3\src\runtime-core\componentUpdateUtils.ts
*
*/
export function shouldUpdateComponent(prevVNode, nextVNode) {
const { props: prevProps } = prevVNode;
const { props: nextProps } = nextVNode;
for (const key in nextProps) {
if (nextProps[key] !== prevProps[key]) {
return true;
}
}
return false;
}