0
点赞
收藏
分享

微信扫一扫

vue面试题及其结合源码分析之请说一下 vue响应式数据的理解

程序员阿狸 2022-04-20 阅读 41

Vue面试题结合源码

请说一下 vue响应式数据的理解

可以监控一个数据的修改和获取操作。针对对象格式会给每个对象的属性进行劫持。其使用了Object.difineProperty方法。

关于vue的响应式原理我是有书写过的。内部就是在递归的劫持引用类型的属性,在属性也是引用类型的情况下会再次拦截。

回答该问题:

  1. 需要先找到基本的问题在哪
  2. 源码层面回答
  3. 使用的时候可能会伴随什么问题(就是踩坑)

源码层面:

  1. 先走 InitData方法
  2. observe
  3. defineReactive方法(内部对所有属性进行了重写,肯定是有性能消耗的)
  4. 在3中递归的观测引用类型的数据,给对象增加getter和setter

我们在使用vue的时候,如果data数据层级过深,需要考虑优化。比如:

  1. 数据不是响应式的,没必要放到data中

  2. 属性取值的时候,尽量避免多次取值

    for(let i = 0; i < 100; i++){
    	this.count++
    }
    // 优化 只会取值一次
    let count = this.count
    for(let i = 0; i < 100; i++){
    	count++
    }
    
  3. 如果有些对象是放到data中的,但是不需要是响应式的,可以考虑采用Object.freeze()冻结对象。冻结的对象Vue是不会进行观测的(一般这种对象都是会在模板中使用的数据)

    image-20220419223102194

源码分析:

new Vue的时候,来到了核心方法 _init。在该方法的 initState 方法用来初始化所有的状态信息。

image-20220419225657891

初始化data

image-20220419230122916

来到initData,该方法就是拿到data数据,然后进行判断,最后观测数据。

function initData (vm: Component) {
  let data = vm.$options.data
  // 顺便把 data也放在 _data属性上
  data = vm._data = typeof data === 'function'
    ? getData(data, vm) // data.call(vm)
    : data || {}
    // 不是计划的对象 data不是对象 或者data函数返回值不是对象
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    // props中有该属性
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) { // 属性不要以 _ 和 $ 开头
        // 代理 vm._data
      // 访问 vm.name 代理到 vm._data.name
      proxy(vm, `_data`, key)
    }
  }
  // observe data 观测数据 标记整数根数据 new Vue
  observe(data, true /* asRootData */)
}

image-20220419230737819

在observe方法中,就是创建Observer观测者

image-20220419231313427

在这里就给引用类型创建观察者对象,如果该对象被观测过,直接返回观察过的observer对象.

观测的对象可能是数组,也可能是对象。

image-20220419231506826

如果是一个对象(不是数组对象),就把对象的每个属性变成响应式的数据。那么此时就会来到定义响应式数据的核心方法:defineReactive

walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      // 把对象的的每个 key 都变成响应式的
      defineReactive(obj, keys[i])
    }
  }

image-20220419233003398

defineReactive方法,Vue是给我们导出了的,在Vue.utils下,有该工具函数。

虽然vue内部是基本是没有使用过shallow参数进行引用类型数据的浅层观测的,但是把方法暴露给我们用户了,我们使用的时候是可以浅层观测对象的。

image-20220419234554437

可以自己尝试debugger看起响应式的过程。

image-20220419233448785

image-20220419234044426

举报

相关推荐

0 条评论