[VUE] Ответ данных и процесс реализации кэша зависимостей вычисляемого атрибута

исходный код JavaScript Vue.js Отзывчивый дизайн

Эта статья представляет собой заметку об изучении исходного кода vue.js, подходящую для студентов, которые имеют определенное представление о реализации vue data response.Я надеюсь, что в тексте есть неточные выражения. затем начните

Совет: Вы увидите в тексте пометку типа (точка знаний: xxx), указывающую на то, что соответствующая позиция исходного кода помечена для удобства чтения всеми

Во-первых, посмотрите на исходный код реализации вычисляемого

/src/core/instance/state.js

// initState会在new Vue()时执行
export function initState (vm: Component) {
  /*
  	other
  */
  // 如果我们定义了comouted属性则执行initComputed
  if (opts.computed) initComputed(vm, opts.computed)
  /*
  	other
  */
}

Найдите определение initComputed в том же файле.

function initComputed (vm, computed) {
  // 往组件实例上添加一个_computedWatchers属性,保存所有的computed watcher
  const watchers = vm._computedWatchers = Object.create(null)
  
  // 对所有的computed属性遍历处理
  for (const key in computed) {
  
    // 将我们定义的computed属性值用userDef保存
    const userDef = computed[key]
    
    // 我们在定义computed时可以是一个函数,也可以是一个对象{get:function(){}, set:function(){}}
    const getter = typeof userDef === 'function' ? userDef : userDef.get
    
    // 数据响应过程中的watcher(注意第二个参数是我们刚才拿到的getter,记住了)
    watchers[key] = new Watcher(
      vm,
      getter || noop, // 注意这里,注意这里,注意这里,(****知识点getter)
      noop,
      computedWatcherOptions
    )
    
    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    } 
  }
}

Далее мы еще находим реализацию defineComputed в этом файле

export function defineComputed (target, key, userDef) {
  /* other */

  // 这里我对源码进行了简化
  // sharedPropertyDefinition是一个全局对象
  // 拿到一个get函数
  sharedPropertyDefinition.get = createComputedGetter(key)

  /* other */

  // 这个函数的主要功能是computed属性的get进行了重写
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

Или продолжайте видеть createComputedGetterочень важно, очень важно, очень важно

function createComputedGetter (key) {

  // 返回一个函数,也就是我们在上一个函数中那个get函数
  return function computedGetter () {

    // 拿到我们在initComputed函数中添加到vm上面的_computedWatchers
    const watcher = this._computedWatchers && this._computedWatchers[key]

    // 如果我们有定义computed属性,watcher必定存在
    if (watcher) {
      
      // 注意,注意,注意,只要在模板中使用了这个computed属性,之后每次页面更新就是循环知识点1到知识点5这个过程
      // 第二节主要就是在讲这一块,在理解下面的步骤时可以对照的看一下
      if (watcher.dirty) { // ****标记:知识点1
        watcher.evaluate() // ****标记:知识点2
      }
      if (Dep.target) { // ****标记:知识点3
        watcher.depend() // ****标记:知识点4
      }
      return watcher.value // ****标记:知识点5
    }
  }
}

Во-вторых, вычисленный ответ свойства

1. Происхождение всего

или в этом файле мы можем найти этот объект

const computedWatcherOptions = { lazy: true }

При возврате к новому Watcher в функции initComputed

watchers[key] = new Watcher(
  vm,
  getter || noop, 
  noop,
  computedWatcherOptions  // 看这里,看这里,看这里(传入{lazy: true})
)
2. Начало всего

Наблюдательисходный код

constructor (vm, expOrFn, cb, options, isRenderWatcher) {
    this.vm = vm
    if (isRenderWatcher) {
      vm._watcher = this
    }
    vm._watchers.push(this)
    /* ... */
    if (options) {
      this.lazy = !!options.lazy // this.lazy = true
    }
    this.getter = expOrFn
    this.dirty = this.lazy // 初始化this.dirty = true
    /* ... */
    // 注意了,注意了,注意了
    // new时this.lazy为true所以this.value = 'undefined'
    this.value = this.lazy ? undefined : this.get()
  }

Основной процесс находится в функции createComputedGetter

  • существуетnew WatcherВремяthis.dirty = this.lazyтакПункт знаний 1: watcher.dirty = true
  • тогдаПункт знаний 2: watcher.evaluate()будуthis.dirty = falseТогда он будет выполнять наблюдательthis.get()В конце концов, это реализацияПолучатель очков знаний:this.getter()
  • ПродолжатьПункт знаний 3:Dep.targetвсегда правда
  • СмотретьПункт знаний 4Собрать зависимости
  • наконецЗнания пункт 5получить вычисленное значение

Вот и все, что касается реализации Watcher.Эта статья основана на общем понимании класса Wathcer. Если у вас есть какие-либо потребности, вы можете оставить сообщение, и я подробно разберу процесс реализации ответа данных позже.В Интернете, кажется, много статей по теме.

3. Начать шоу
new Vue({
  data(){
    return {
      dataA: 'a',
      dataB: 'b'
    }
  },
  template: '<div>{{computedA}}-{{dataB}}</div>',
  computed: {
    computedA() {
      return 'computed ' + this.dataA
    }
  },
  method: {
      changeA(){
          this.dataA = 'change dataA'
      },
      changeB(){
          this.dataA = 'change dataB'
      }
  }
})

Посмотрите на функцию createComputedGetter

  • 1, в шаблоне при первом отображении страницы{{computedA}}воплощать в жизньcomputedA.get() Перейти к функции createComputedGetter
  • 2,Пункт знаний 1: this.dirty = true
  • 3,Пункт знаний 2:watcher.evaluate()выполнить, будетthis.dirty = false, наблюдатель внутренне выполняетПолучатель очков знаний:this.getter()
this.getter = computedA = function(){
    return 'computed' + this.dataA // 看这里,看这里,看这里,知识点update
}

Естьwacher.valueравныйcomputed a

  • 4,watcher.depend()Повторный сбор зависимостей
  • 5. Верните wacher.value и отобразите его на странице<div>computed a-b</div>
  • 6, мы звонимthis.changA()Изменить dataA, вызвать dataA вdep.notify(), выполнит все объекты-наблюдатели dataAwathcer.update(),потому чтоЛенивый наблюдателя, которому принадлежит вычисление, всегда истинен,Пункт знаний 1: this.dirty = true
  • 7, поскольку dataA изменился, инициируйте повторную визуализацию страницы, повторную визуализацию шаблона, шаблона в{{computedA}}позвони сноваcomputedA.get(), петля шаг 1

Три, вычисляемый кэш атрибутов

Благодаря процессу вычисляемого ответа в Разделе 3, Разделе 3 мы знаем, что вычисленный A будет отслеживать изменения в dataA, чтобы изменитьПункт знаний 1: this.dirty = trueнаконец казненПолучатель очков знаний

Предположения:

  • мы сейчас выполняемthis.changeB(), изменил значение dataB,
  • Все объекты-наблюдатели dataB будут выполненыwathcer.update(),
  • Поскольку dataB изменился, запустите повторную визуализацию страницы, повторную визуализацию шаблона, шаблона в{{computedA}}позвони сноваcomputedA.get()Перейти к функции createComputedGetter.
  • потому чтоcomputedAОн не отслеживает изменения dataB, поэтому не будет выполняться.computedAизwatcher.update()Пункт знаний 1: this.dirty = false,не будет выполняться в концеТочка знаний: геттер
  • Вернитесь прямо к последнему разуТочка знаний: геттеррезультатreturn watcher.value

Суммировать

Поскольку в этой статье в основном говорится о знании вычисляемых данных, в ней нет подробного описания знания ответа данных, что может показаться немного запутанным, а процесс реализации ответа данных vue будет подробно проанализирован позже.