首页 > 其他分享 >记录--手写$forceUpdate,vm.$destroy方法

记录--手写$forceUpdate,vm.$destroy方法

时间:2022-11-14 16:46:53浏览次数:53  
标签:Vue -- vm watcher 实例 ._ destroy js

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

vm.$forceUpdate

(1)作用

迫使Vue.js实例重新渲染。注意它仅仅影响实例本身以及插入插槽内容的子组件,而不是所有子组件。

(2)实现

只需要执行watcher的update方法,就可以让实例重新渲染。

Vue.js的每一个实例都有一个watcher。当状态发生改变时,会通知到组件级别,然后组件内部使用虚拟DOM进行更详细的重新渲染操作。

事实上,组件就是Vue.js实例,所以组件几倍的watcher和Vue.js实例上的watcher说的是同一个watcher。

手动执行实例watcher的update方法,就可以使Vue.js实例重新渲染。

Vue.prototype.$forceUpdate = function(){
 const vm = this;
 if(vm._watcher){
  vm._watcher.update();
 }
}

vm._watcher就是Vue.js实例的watcher,每当组件内依赖的数据发生变化时,都会自动触发Vue.js实例中_watcher的update方法。

重新渲染的实现原理并不难,Vue.js的自动渲染通过变化侦测来侦测数据,即当数据发生变化时,Vue.js实例重新渲染。而vm.$forceUpdate是手动通知Vue.js实例重新渲染。

vm.$destroy

(1)作用

完全销毁一个实例,它会清理该实例与其他实例的连接,并解绑其全部指令及监听器,同时会触发beforeDestory和destroyed的钩子函数。

(2)这个方法并不是很常用,大部分场景下并不需要销毁组件,只需要使用v-if或则v-for等指令以数据驱动的方式控制子组件的生命周期即可。

(3)实现原理

Vue.prototype.$destory = function(){
 const vm = this;
 if(vm._isBeingDestroyed){
  return;
 }
 callHook(vm,"beforeDestroy");
 vm._isBeingDestroyed = = true;
}

1、为了防止vm.$destroy被反复执行,先对属性_isBeingDestroyed进行判断,如果它为true,说明Vue.js实例正在被销毁,直接使用return语句退出函数执行逻辑。因为销毁只需要销毁一次即可,不需要反复销毁。

2、然后调用callHook函数触发beforeDestroy的钩子函数(callHook会触发参数中提供的钩子函数)。

(4)销毁实例的逻辑1 首先,需要清理当前组件与父组件之间的连接。组件就是Vue.js实例,所以要清理当前组件与父组件之间的连接,只需要将当前组件实例从父组件实例的$children属性中删除即可。

说明:Vue.js实例的$children属性存储了所有子组件

const parent = vm.$parent;
if(parent && !parent._isBeingDestroyed && !vm.$options.abstract){
 remove(parent.$children,vm)
}

1、如果当前实例有父级,同时父级没有被销毁且不是抽象组件,那么将自己从父级的子列表中删除,也就是将自己的实例从父级的$children属性中删除。

2、事实上,子组件在不同父组件中是不同的Vue.js实例,所以一个子组件实例的父级只有一个,销毁操作也只需要从父级的子组件列表中销毁当前这个Vue.js实例。

export function remove(arr,item){
 if(arr.length){
  const index = arr.indexOf(item);
  if(index>-1){
   return arr.splice(index,1);
  }
 }
}

(5)销毁实例的逻辑2

1、父子组件间的链接断掉之后,需要销毁实例上的所有watcher,也就是说需要将实例上所有的依赖追踪断掉。

2、状态会收集一些依赖,当状态发生改变时会向这些依赖发送通知,而被收集的依赖就是watcher实例。因此,当Vue.js实例被销毁时,应该将实例所监听的状态都取消掉,也就是从状态的依赖列表中将watcher移除。

3、watcher的teardown方法,它的作用是从所有依赖项的Dep列表中将自己移除。即只要执行这个方法,就可以断掉这个watcher所监听的所有状态。

4、断掉Vue.js实例自身的watcher实例监听的所有状态。

if(vm._watcher){
 vm._watcher.teardown();
}

5、执行了组件自身的watcher实例的teardown方法,从所有依赖项的订阅列表中删除watcher实例。删除之后,当状态发生变化时,watcher实例就不会再得到通知。

6、vm._watcher来源

当执行new Vue()时,会执行一系列初始化操作并渲染组件到实体上,其中就包括vm._watcher的处理

7、从Vue.js2.0开始,变化侦测的粒度调整为中等粒度,它只会发送通知到组件级别,然后组件使用虚拟DOM进行重新渲染。组件其实就是Vue.js实例。

8、怎么通知到组件级别

在Vue.js实例上,有一个watcher,也就是vm._watcher,它会监听这个组件中用到的所有状态,即这个组件内用到的所有状态的依赖列表中都会收集到vm._watcher。当这些状态发生变化时,也都会通知vm._watcher,然后这个watcher再调用虚拟DOM进行重新渲染。

(6)销毁实例的逻辑

1、只从状态的依赖列表中删除Vue.js实例上的watcher实例是不够的。Vue.js提供了vm.watch所创建的watcher实例。

2、从状态的依赖列表中销毁用户创建的watcher实例和销毁Vue实例上的watcher实例相同,只需要执行watcher的teardown方法。

3、问题:如何知道用户创建了多少个watcher?

1)Vue.js的解决方案是执行new Vue()时,在初始化的流程中,在this上添加一个_watchers属性

vm._watchers = [];

2)每当创建watcher实例时,都会将watcher实例添加到vm._watchers中

export default class Watcher{
 constructor(vm,expOrFn,cb){
  <!-- 每当创建watcher实例时,都将watcher实例添加到vm._watchers中 -->
  vm._watchers.push(this);
 }
}

4、只需要遍历vm._watchers并依次执行每一项watcher实例的teardown方法,就可以将watcher实例从它所监听的状态的依赖列表中移除。

let i = vm._watchers.length;
while(i--){
 vm._watchers[i].teardown();
}

(7)向Vue.js实例添加_isDestroyed属性来表示Vue.js实例已经被销毁。

vm._isDestroyed = true;

(8)当vm.$destroy执行时,Vue.js不会将已经渲染到页面中的DOM节点移除,但会将模板中的所有指令解绑。

vm._patch_(vm._vnode,null)

(9)触发destroyed钩子函数

callHook(vm,'destroyed')

(10)最后,移除实例上的所有事件监听器。

vm.$off()

(11)完整代码

Vue.prototype.$destory = function(){
 const vm = this;
 <!-- 防止重复销毁 -->
 if(vm._isBeingDestroyed){
  return;
 }
 <!-- 调用钩子函数beforeDestroy -->
 callHook(vm,"beforeDestroy");
 vm._isBeingDestroyed = = true;
 <!-- 删除自己与父级之间的连接 -->
 const parent = vm.$parent;
 if(parent && !parent._isBeingDestroyed && !vm.$options.abstract){
  remove(parent.$children,vm);
 }
 <!-- 从watcher监听的所有状态的依赖列表中移除watcher -->
 if(vm._watcher){
  vm._watcher.teardown();
 }
 let i = vm._watchers.length;
 <!-- 将从vm.$watcher创建的watcher实例从它所监听的状态的依赖列表中移除 -->
 while(i--){
  vm._watchers[i].teardown();
 }
 <!-- 表示实例已经被销毁 -->
 vm._isDestroyed = true;
 <!-- 将模板中的所有指令解绑 -->
 vm._patch_(vm._vnode,null)
 <!-- 触发destroyed钩子函数 -->
 callHook(vm,'destroyed')
 <!-- 移除实例上的所有事件监听器 -->
 vm.$off();
}

本文转载于:

https://juejin.cn/post/6844904196840357902

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

标签:Vue,--,vm,watcher,实例,._,destroy,js
From: https://www.cnblogs.com/smileZAZ/p/16889445.html

相关文章

  • SpringBoot 05: 接口架构风格 + RESTful接口风格
    接口定义API(ApplicationProgrammingInterface,应用程序接口)是一些预先定义的接口(如函数、HTTP接口,或指软件系统不同组成部分衔接的约定)是用来提供应用程序与开......
  • 代码随想录训练营第三十二天|贪心算法
    本来这是第三十一天的内容,但是三十一天的时候写成第三十二天的了,因此今天写第三十一天的内容 455.分发饼干 classSolution{publicintfindContentChildre......
  • Java学习——11.14
    将近4天没更新啊,为什么呢,主要是面向过程太难太抽象了,不过好在我用四天还是将他理解了。1.封装(关键字:private)保护私有的方法和属性。set直接修改private  ......
  • 【Vue-05】修改Vue的默认配置
    修改Vue的默认配置Vue脚手架隐藏了所有webpack相关的配置,若想查看具体的webpack配置,请执行:vueinspect>output.js,若要个性化配置Vue脚手架,就需要创建vue.config.js这一......
  • 某nas下载
    某nas下载效果图:“不,不应该这样的,我们的界面不是这样的”嗯确实,这里只是探索学习,请不要模仿.同思路: nas其实是定制版的linux,所谓的app大部分都是web服......
  • 肖sir__面试第一天课程__项目数据(3)
    项目数据 一、项目周期(1)新项目:从无到有,从项目开始到上线的时间;一般时间:3个月、6个月、一年、2年(2)迭代项目1个月、2个月、3个月(3)敏捷开发项目:周期特别短:2周、1周......
  • Mysql 数据存储结构
    mysql 数据库存储结构  B-tree与哈希索引的区别B-tree索引    B-tree索引是按照顺序存储的,所以,如果按照B-tree索引,可以直接返回带顺序的数据,但这个数......
  • 状态码400
    400是HTTP的状态码,主要有两种形式:1、badrequest意思是“错误的请求”;2、invalidhostname意思是“不存在的域名”。400BadRequest是由于明显的客户端错误(......
  • 时间计算器
    时间计算器importdatetime'''计算当前时间的前后几天,或后前几小时,或后前几分钟等的时间,支持天时分秒组合,只需要传对应的值:paramdays......
  • 百度离线地图JS API V3.0
    首先,百度地图JavaScriptAPI3.0版本与2.0版本相比增加了几个小功能,整体没有大的改动,具体可以在官网上查阅。于是就照着先前大佬们分享的2.0离线版本进行3.0版本的制作,附上......