这次来到更新 Element 的相关操作了。
更新,即意味着有新的,有旧的,新的替换成旧的。
要找到旧的节点、新的节点的出现位置,然后再次进行 patch,根据 vnode 更新 Element。
获取新旧 vnode
回顾之前的代码,在 setupRenderEffect 的时候,拿到了组件 render 函数返回的 vnode,再次传给 patch,来到 processElement。
那我们就可以在 setupRenderEffect 里存储旧节点,获取新节点,传给 patch 这两个新旧节点。
还有一个点是,每当标签内容有变更的时候,就会自动触发更新的逻辑,针对这个,reactiivty 的 effect 副作用函数就派上用场了。
// component.ts
createComponentInstance(vnode, parent) {
const component = {
isMounted: false,
subTree: {}
}
return component;
}
// renderer.ts
function setupRenderEffect(instance, initialVNode, container) {
effect(() => {
if(!instance.isMounted) {
// subTree 当前实例的 subTree
const subTree = instance.subTree = instance.render.call(proxy);
patch(null, subTree, container, instance);
instance.isMounted = true;
} else {
// 获得新的 subTree
subTree = instance.render.call(proxy);
// 获得旧的 subTree
prevSubTree = instance.subTree;
// 更新组件实例的 subTree
instance.subTree = subTree;
patch(prevSubTree, subTree, container, instance);
}
})
}
修改 patch 参数
patch 多加了一个节点参数
// renderer.ts
function render(vnode, container) {
patch(null, vnode, container, null);
}
function patch(n1, n2, container, parentComponent) {
processFragment(n1, n2, ...)
processText(n1, n2, ...)
processElement(n1, n2, ...)
processComponent(n1, n2, ...)
}
新增 patchElement 函数
-
在
processElement这里,判断有旧节点存在,就意味着需要更新,没有旧节点,就意味着初始化渲染// renderer.ts function processElement(n1, n2, container, parentComponent) { if(!n1) { mountElement(n2, container, parentComponent) } else { patchElement(n1, n2, container); } } function patchElement(n1, n2, container) { // todo }
proxyRefs 处理 setup 执行结果
handleSetupResult执行setup函数得到的返回结果,使用proxyRefs内部的unRef功能,使render函数内部访问ref对象时,省去.value的访问。function handleSetupResult(instance, setupResult) { // 如果 setup 返回的是 object if(typeof setupResult === 'object') { // 设置组件实例的 setupState 状态为 setup 返回的这个对象 instance.setupState = proxyRefs(setupResult); } // 完成组件 setup finishComponentSetup(instance); }
更新 Element 的 Props
// renderer.ts
function createRenderer({ patchProp: hostPatchProp }) {
const EMPTY_OBJ = {};
function patchElement(n1, n2, container) {
// 获得新旧 props
oldProps = n1.props || EMPTY_OBJ;
newProps = n2.props || EMPTY_OBJ;
// 在这里更新 el
el = (n2.el = n1.el);
patchProps(el, oldProps, newProps);
}
function patchProps(el, oldProps, newProps) {
// 添加|更新 props
for (key in newProps) {
prevProp = oldProps[key];
nextProp = newProps[key];
// 对比,是不同的值
if (prevProp !== nextProp) {
// 更新 props
hostPatchProp(el, key, prevProp, nextProp);
}
}
// 移除不存在的旧 props
if (oldProps !== EMPTY_OBJ) {
for (key in oldProps) {
// 新的 props 没有找到旧的 props
if !(key in newProps) {
// 移除旧的 props
hostPatchProp(el, key, oldProps[key], null);
}
}
}
}
}
// runtime-dom/index.ts
patchProp(el, key, prevVal, nextVal) {
if (isOn(key)) {
el.addEventListener(event, nextVal);
} else {
if (nextVal == null) {
el.removeAttribute(key);
} else {
el.setAttribute(key, nextVal);
}
}
}