【4-6】响应式原理 之 哪些api是响应式的,哪些不是响应式的

《哪些api是响应式的,哪些不是响应式的》:

总结一下 Vue 体系内,哪些api是响应式的,哪些不是响应式的。对于个别原理,如provide inject做出一些详细解释。

响应式 vs 非响应式

不是响应式的,有些的原理就是利用forceUpdate绕过响应式来实现重新渲染

  • 哪些是响应式的:
    • 原理:definedReactive + 渲染watcher、computed watcher、用户watcher (即Dep.target。搜索了源码,结果只有这几种 watcher)
    • data
    • props
    • vuex的state(本质上是data)
    • vue-router注入的 this.$route:使用mixin在created时机之前调用defineReactive
  • 哪些不是响应式的
    • vm.$children需要注意:并不保证顺序,也不是响应式的
    • 官网说了插槽不是响应式的
      • 插槽的更新原理基于forceUpdate
      • keep-alive的更新原理基于插槽
    • provide inject 【后有介绍】
    • 经过研究,认为 指令 不是响应式的,尤其自定义指令不是响应式的 【具体看指令的源码解析。不涉及任何响应式的代码】
    • $refs 只会在组件渲染完成之后生效,并且它们不是响应式的。
      • (因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 – 它们还不存在)
      • 用法:这仅作为一个用于直接操作子组件的“逃生舱”
      • 因为不是响应式的,避免误用:你应该避免在模板或计算属性中访问 $refs
    • 冻结对象:data加上Object.freeze()

vuex是响应式的

vuex的state是响应式的:响应式主要是针对state,作为new Vue的data是有被 defineReactive 数据劫持的,再加上可被watcher监测

provide inject 非响应式:#是否响应式,主要由其他机制决定

  • 通过$parent向上查找(持有该key-value的且)最近的祖先节点的数据。
  • toggleObserving(false) + definedReactive "inject属性"设置1次就完事了
    • toggleObserving(false)作用于defineReactive,即对于深层嵌套的属性不做重复的defineReactive处理
      • 如果在父组件中作为data,已经被defineReactive过了:那就是响应式的
      • 如果祖先组件所提供的 provide项 本身非响应式的,那么这里也就非响应式的
    • 之所以在子孙组件defineReactive某inject属性,大概是为了能在 该inject属性变化 或者 该inject属性的引用变化 时,当前子组件的视图能相应地更新。响应式的效果还是由提供的组件那边来实现

provide inject vs vuex,即为什么它不适合做中心化规模化的数据存储

【官网】使用provide inject来创建一个中心化规模化的数据跟使用 $root做这件事都是不够好的。

  • provide inject不能追溯变量改变,代码可维护性问题
  • provide inject的定位更专注于插件/自定义组件内部的各组件间的通信,更偏向于一次性地传递数据或者方法
  • (默认让视图不响应其数据变化)

provide inject想弄成响应式的怎么办

  • 下面一级的字段在父组件中作为data,已经被defineReactive过了,也是可以的
  • computed监听它,然后可再渲染watcher监听它

下面一级的字段在父组件中作为data,已经被defineReactive过了 例子

// 祖先组件:
provide() {
  return { foo: this.bar }
}
data() {
  return {
    bar: { a: 'a of bar' }
  }
}
// 子孙组件
// 视图上使用this.foo.a即可
inject:['foo']

provide inject的值可以作为子孙组件的props或者data的初始值: #暂时不研究

使用一个注入的值作为一个 property 的默认值:

const Child = {
  inject: ['foo'],
  props: {
    bar: { default () { return this.foo } }
  }
}
// 可能相关:
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props

冻结对象Object.freeze() 非响应式

https://github.com/vuejs/vue/issues/10110

https://github.com/vuejs/vue/issues/2637

有时候应为性能问题,我们需要声明非响应式。

办法就是给data加上Object.freeze(),或者在created之后再加到data上面。

但是要注意,freeze这招冻结不了子属性的引用的。之所以这样做,这个主要是为了减少vue无用的数据observe,尤其是一个data对象有很多字段并且不需要被监听和检查。

文章目录
  1. 响应式 vs 非响应式
  2. vuex是响应式的
  3. provide inject 非响应式:#是否响应式,主要由其他机制决定
    1. provide inject vs vuex,即为什么它不适合做中心化规模化的数据存储
    2. provide inject想弄成响应式的怎么办
    3. 下面一级的字段在父组件中作为data,已经被defineReactive过了 例子
    4. provide inject的值可以作为子孙组件的props或者data的初始值: #暂时不研究
  4. 冻结对象Object.freeze() 非响应式