《Vuejs设计与实现》8.5 卸载操作

310 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情

前文中主要讨论了挂载,还有挂载时如何设置元素,这一节将要讨论如何卸载元素。卸载主要发生在更新阶段,对应于vue的生命周期updated,也是我们之前的还未完全实现的patch函数。

我们之前实现了渲染一个虚拟DOM,可以使用renderer.render(vnode,container),然后我们会在patch中对比,如果只是vnode的内容,属性变化了,我们可以直接重新赋值,但如果vnode不存在了,我们就需要出发卸载操作,比如 renderer.render(null,container)

在之前实现的patch函数里,当元素卸载时,简单粗暴的调用了container.innerHTML = ''。诚然,这样是最简单,最快速的清除DOM的方法,但这样是不行的,书中写了3点原因:

  1. container中可能有多个DOM,对应着就是多个虚拟DOM,这样操作会直接清空其他vnode,也就是相邻组件。
  2. 其他元素中可能有自定义指令,应当执行指令中卸载元素的回调函数
  3. innerHTML不会解绑事件

解决的办法很简单,我们需要把虚拟Node与真实NODE联系起来,我们可以这样操作 const el = createElement(vnode.type);vnode.el=el,把渲染出来的真是元素放到vnode的结构里,这样我们卸载的时候,可以根据这个el属性执行卸载。

因此,我们可以封装出来一个unmount函数,来执行卸载操作。

function unmount(vnode){
    const p = vnode.el.parentNode
    if(p){
         //执行其他操作
         p.removeChild(vnode.el)
    }
}

之所以封装起来,不仅仅是因为少写一次removeChild,原因在前文也说过,卸载元素的时候还要考虑是否有自定义指令,自定义指令中是否有卸载的相关操作,可以在这里执行。

是否有事件或者自定义事件,我们也可以在这里解绑。甚至还要触发beforeUnmount或者beforeDestroy这些vue的声明周期。

封装起来,我们也可以把这些操作都集中起来,方便管理和执行