Если вы по желанию измените свойство в данных Vue, будет ли обновлено представление?

внешний интерфейс Vue.js
  • Интервьюер: Вы видели исходный код Vue?
  • Кандидат: Я видел это.
  • Интервьюер: Тогда скажи мнеЕсли вы по желанию измените свойство в данных Vue, будет ли обновлено представление?
  • Кандидат: Нет.
  • Интервьюер: почему?
  • Кандидат: если свойство не используется в шаблоне, нет необходимости обновлять представление, что часто приводит к плохой производительности.
  • Интервьюер: Как это решение реализовано во Vue?
  • Кандидат: во время инициализации экземпляра используйтеObject.definePropertyДанные прослушивают свойства в данных, если используются свойства в шаблоне, они собираются классом DEP, и атрибут будет вызываться при изменении свойства.notifyОбновите вид.
  • Интервьюер: Тогда как узнать, какие свойства используются в шаблоне?
  • Кандидат: ВТФ. . . Это не очень понятно, можете пояснить?
  • Интервьюер: Хорошо, тогда я кратко объясню:

Сначала напишите простую демонстрацию, где в данных есть 4 атрибута.a,b,c,d, атрибуты, используемые в шаблоне,a,b. посмотреть, если толькоa,bпозвонюDepСобирать его?

new Vue({
  el: '#app',
  data() {
    return {
      a: 1,
      b: 2,
      c: 3,
      d: 4,
    };
  },
  created() {
    console.log(this.b);
    this.b = 'aaa';
  },
  template: '<div>Hello World{{a}}{{b}}</div>',
});
  1. во вьюinstance/state.jsвнутри, буду использоватьproxyпроксирует каждое свойство
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 (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)) {
      // 代理对象的属性
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)
  1. использоватьdefineReactiveЗахватить каждое свойство в данных
observe(data, true /* asRootData */);

// observe
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
  defineReactive(obj, keys[i]);
}

// defineReactive
Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: function reactiveGetter() {
    const value = getter ? getter.call(obj) : val;
    // 重点在这里,后续如果在模板中使用到的属性,都会被执行reactiveGetter函数
    // 被Dep类 收集起来
    if (Dep.target) {
      console.log(`${key} 属性 被Dep类收集了`)
      dep.depend();
      if (childOb) {
        childOb.dep.depend();
        if (Array.isArray(value)) {
          dependArray(value);
        }
      }
    }
    return value;
  },
  set: function reactiveSetter(newVal) {
    const value = getter ? getter.call(obj) : val;
    /* eslint-disable no-self-compare */
    if (newVal === value || (newVal !== newVal && value !== value)) {
      return;
    }
    if (setter) {
      // 这里是处理computed set 函数
      setter.call(obj, newVal);
    } else {
      val = newVal;
    }
    childOb = !shallow && observe(newVal);
    // 如果我们在更改属性时,就会调用notify 异步更新视图
    dep.notify();
  },
});
  1. воплощать в жизнь$mountПодключить вид
if (vm.$options.el) {
  vm.$mount(vm.$options.el);
}
  1. $mountзаключается в вызове метода прототипа Vue, фокус находится на последнем предложении mount.call(this, el, hydrating)
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el);

  const options = this.$options;
  // resolve template/el and convert to render function
  /**
   * 查看render 函数是否存在?如果不存在就解析template模板
   * Vue渲染页面时,有两个方式 1. template,2. render,最终所有的模板类的都需要使用render去渲染
   */
  if (!options.render) {
    let template = options.template;
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template);
          /* istanbul ignore if */
          if (process.env.NODE_ENV !== 'production' && !template) {
            warn(
              `Template element not found or is empty: ${options.template}`,
              this
            );
          }
        }
      } else if (template.nodeType) {
        template = template.innerHTML;
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this);
        }
        return this;
      }
    } else if (el) {
      // 如果模板不存在,就创建一个默认的html模板
      template = getOuterHTML(el);
    }
  }
  // 重写了Vue.prototype.$mount ,最终调用缓存的mount方法完成对$mount的挂载
  return mount.call(this, el, hydrating);
};
  1. здесьmountназываетсяmountComponent(this, el, hydrating)метод, в то время какmountComponentреализуется_renderфункция, окончание_renderэто вызовrenderгенерироватьvnode.
const { render, _parentVnode } = vm.$options;
vnode = render.call(vm._renderProxy, vm.$createElement);

image.png image.pngНа последней картинке видно, чтоrenderФункция рендерит внутреннюю часть нашего демо.templateшаблон, в конечном итоге толькоa, bОба свойства будутDepКласс собрал.

image.png

Если в тексте есть ошибки, пожалуйста, укажите на них, и я буду продолжать совершенствоваться. Спасибо, Если вам нужно отладить исходный код, нажмите здесьздесь, просто следуйте инструкциям в файле readme. Надеюсь на звезду. Спасибо