Vue 深入汇总

本文深入探讨Vue.js的响应式原理,详细讲解了Vue.js如何通过Object.defineProperty实现数据监听与更新,以及在Vue 3.0中使用Proxy的原因。此外,介绍了Vue的特点,如双向绑定、组件化、高性能和易用性。讨论了Vue中的计算属性、侦听器、虚拟DOM、diff算法和Vue生命周期,以及如何在Vue中处理数据层级结构深的问题。文章还涵盖了Vue组件的编写原则、CSS隔离、keep-alive的使用、图片动态加载以及Vue CLI中使用自定义组件的方法。最后,文章提到了Vuex的状态管理,包括state、getters、mutations、actions和模块化,并解释了何时使用Vuex以及在Vue项目中如何进行性能优化。
摘要由CSDN通过智能技术生成

1.Vue 响应式原理

核心实现类:

  • Observer : 它的作用是给对象的属性添加 getter 和 setter,用于依赖收集和派发更新
  • Dep : 用于收集当前响应式对象的依赖关系,每个响应式对象包括子对象都拥有一个 Dep 实例(里面 subs 是 Watcher 实例数组),当数据有变更时,会通过 dep.notify()通知各个 watcher。
  • Watcher : 观察者对象 , 实例分为渲染 watcher (render watcher),计算属性 watcher (computed watcher),侦听器 watcher(user watcher)三种。

Watcher 和 Dep 的关系:

watcher 中实例化了 dep 并向 dep.subs 中添加了订阅者,dep 通过 notify 遍历了 dep.subs 通知每个 watcher 更新。

依赖收集:

  • initState 时,对 computed 属性初始化时,触发 computed watcher 依赖收集
  • initState 时,对侦听属性初始化时,触发 user watcher 依赖收集
  • render()的过程,触发 render watcher 依赖收集
  • re-render 时,vm.render()再次执行,会移除所有 subs 中的 watcer 的订阅,重新赋值。

派发更新:

组件中对响应的数据进行了修改,触发 setter 的逻辑

调用 dep.notify()

遍历所有的 subs(Watcher 实例),调用每一个 watcher 的 update 方法。

原理:

当创建 Vue 实例时,vue 会遍历 data 选项的属性,利用 Object.defineProperty 为属性添加 getter 和 setter 对数据的读取进行劫持(getter 用来依赖收集,setter 用来派发更新),并且在内部追踪依赖,在属性被访问和修改时通知变化。

每个组件实例会有相应的 watcher 实例,会在组件渲染的过程中记录依赖的所有数据属性(进行依赖收集,还有 computed watcher,user watcher 实例),之后依赖项被改动时,setter 方法会通知依赖与此 data 的 watcher 实例重新计算(派发更新),从而使它关联的组件重新渲染。

一句话总结:

vue.js 采用数据劫持结合发布-订阅模式,通过 Object.defineproperty 来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发响应的监听回调。

1.2.Vue.js 的特点

易用: 简单,易学,上手快

灵活: (渐进式)不断繁荣的生态系统,可以在一个库和一套完整框架之间自如伸缩。

高效: 20kB min+gzip 运行大小;超快虚拟 DOM;最省心的优化

双向绑定:开发效率高

基于组件的代码共享

Web项目工程化,增加可读性、可维护性

1.3. Vue.js 双向绑定的原理

Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。

Vue.js 3.0, 放弃了Object.defineProperty ,使用更快的ES6原生 Proxy (访问对象拦截器, 也称代理器)

步骤:

  •         需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
    •         compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
      •         Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
        •         ①在自身实例化时往属性订阅器(dep)里面添加自己
          •          ②自身必须有一个update()方法
            •         ③待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
              •         MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。

1.4.Vue中如何监控某个属性值的变化?

比如现在需要监控data中, obj.a 的变化。Vue中监控对象属性的变化你可以这样:

watch: {
      obj: {
      	handler (newValue, oldValue) {
        console.log('obj changed')
      },
      deep: true
    }

        deep属性表示深层遍历,但是这么写会监控obj的所有属性变化,并不是我们想要的效果,所以做点修改:

watch: {
   'obj.a': {
      	handler (newName, oldName) {
        console.log('obj.a changed')
      }
   }
  }

还有一种方法,可以通过computed 来实现,只需要:

computed: {
    a1 () {
      return this.obj.a
    }
}

利用计算属性的特性来实现,当依赖改变时,便会重新计算一个新值。

1.5 Vue.js 3.0 放弃defineProperty, 使用Proxy的原因

Object.defineProperty缺陷

  •         监控到数组下标的变化时,开销很大。所以Vue.js放弃了下标变化的检测;
    •         Object.defineProperty只能劫持对象的属性,而Proxy是直接代理对象。
      •         Object.defineProperty需要遍历对象的每个属性,如果属性值也是对象,则需要深度遍历。而 Proxy 直接代理对象,不需要遍历操作。
        •         Object.defineProperty对新增属性需要手动进行Observe。vue2时需要使用 vm.$set 才能保证新增的属性也是响应式。
          •         Proxy支持13种拦截操作,这是defineProperty所不具有的。
            •         Proxy 作为新标准,长远来看,JS引擎会继续优化 Proxy,但 getter 和 setter 基本不会再有针对性优化。

1.6.Vue 2 中给 data 中的对象属性添加一个新的属性时会发生什么?如何解决?

视图并未刷新。这是因为在Vue实例创建时,新属性并未声明,因此就没有被Vue转换为响应式的属性,自然就不会触发视图的更新,这时就需要使用Vue的全局 api $set():

this.$set(this.obj, 'new_property', 'new_value')

1.7.Computed和Watch的区别及运用场景

computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的 属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。

watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每 当监听的数据变化时都会执行回调进行后续操作。

运用场景:

        当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。

        当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率, 并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

        多个因素影响一个显示,用Computed;一个因素的变化影响多个其他因素、显示,用Watch;

1.8. Computed 和 Methods 的区别

        1.computed: 计算属性是基于它们的依赖进行缓存的,只有在它的相关依赖发生改变时才会重新求值对于 method ,只要发生重新渲染,

        2.method 调用总会执行该函数

1.9.虚拟DOM,diff算法

        1.让我们不用直接操作DOM元素,只操作数据便可以重新渲染页面

        2.虚拟dom是为了解决浏览器性能问题而被设计出来的

当操作数据时,将改变的dom元素缓存起来,都计算完后再通过比较映射到真实的dom树上

        3.diff算法比较新旧虚拟dom。如果节点类型相同,则比较数据,修改数据;如果节点不同,直接干掉节点及所有子节点,插入新的节点;如果给每个节点都设置了唯一的key,就可以准确的找到需要改变的内容,否则就会出现修改一个地方导致其他地方都改变的情况。比如A-B-C-D, 我要插入新节点A-B-M-C-D,实际上改变的了C和D。但是设置了key,就可以准确的找到B C并插入

1.10.为何需要Virtual DOM?

        1.具备跨平台的优势

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜飞鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值