vue 源码学习第一天 vue 内部运行机制 new Vue()后有几个过程 初始化和挂载 初始化 初始化生命周期,事件,props,methods,data,computed 和 watch 等,最重要是通过 设置 和 来实现响应式和依赖收集。 挂载 $mount 会挂载所有组件,如果存在 template,需要进行编译 编译 编译分为三个过程 1.parse 2.optimize 3.gene…

vue 源码学习第一天

vue 内部运行机制

图片来自[染陌同学](https://github.com/answershuto)

vue 内部运行机制

new Vue()后有几个过程

  1. 初始化和挂载

    1. 初始化

      初始化生命周期,事件,props,methods,data,computed 和 watch 等,最重要是通过Object.defineObjectProperty设置gettersetter来实现响应式和依赖收集。

    2. 挂载

      $mount 会挂载所有组件,如果存在 template,需要进行编译

  2. 编译

    编译分为三个过程 1.parse 2.optimize 3.generate

    1. parse

      parse 将 template 转成字符串,然后正则匹配指令,如v-if v-for class等,然后生成抽象语法树

    2. optimize

      标记静态节点,在以后的 update 过程中,diff 算法会跳过静态节点,如果存在v-if v-for就不是静态节点

    3. generate

      将抽象语法树生成 render function 进而生成 Vnode,Vnode 可以用来模拟虚拟 DOM,虚拟 DOM 本质是 Object,可运行在不同的环境中

  3. 响应式
    1. 当读取所需对象的值时,会触发 setter 进行依赖收集,依赖手机的目的是,将 Watcher 对象放在 Dep 集合中,对对象进行 watch
    2. 当修改对象的值时,会触发对应的 setter,setter 通知 Dep 的 Watcher 值改变了,Watcher 开始调用 update 进行更新,进行更新的时候会触发 patch 更新,patch 是一种异步更新策略,patch 可实现将新老 Vnode 的差异进行 diff 对有差异的进行更新
    3. patch 的算法核心是 diff 算法,diff 算法是进行同层节点进行比较,而不是逐层遍历,所以时间复杂度为 O(n)
  4. 虚拟 DOM

    虚拟 DOM 是把真实 DOM 抽象成了有着 DOM 属性的对象

  5. 更新视图

    更新视图的触发条件是 patch 到了有差异的 Vnode,执行的过程是 setter->Dep->Watcher->patch->视图

响应式系统的基本原理

  1. 响应式系统

    vue.js 是通过 Object.defineProperty 来实现响应式系统,Object.defineProperty 有 4 个关键属性,1.enumerable,属性是否可枚举,默认 false ;2.configurable,属性是否可以被修改或者删除,默认 false ;3.get,获取属性的方法。4.set,设置属性的方法。

  2. 响应式依赖

    new Vue 对象将 data 中的数据进行响应式化,对 data 的属性进行操作,就会触发更新方法,代码如下。

function notify() {
  console.log('有内容更新了');
}

function defineData(obj, key, value) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      return value;
    },
    set: function(newVal) {
      if (newVal === value) return;
      notify();
    }
  });
}

function observer(data) {
  if (typeof data !== 'object') {
    return;
  }

  Object.keys(data).forEach((key) => {
    defineData(data, key, data[key]);
  });
}

class Vue {
  constructor(options) {
    this._data = options.data;
    observer(this._data);
  }
}

let myVue = new Vue({
  data: {
    value: 'hello world'
  }
});
myVue._data.value = 'nioe world';

响应式系统的依赖收集追踪原理

  1. 为什么要依赖收集

    1. 依赖收集是为了确定哪些数据需要真正的更新,如果是没用到数据,即使改变了,也不需要触发更新。
  2. 如何实现依赖收集

    1. 依赖收集需要有一个订阅者 Dep,去收集所有的 Watcher,当 set 时触发 Dep 的 notify 通知 Watcher 来更新视图
// 依赖收集的集合
class Dep {
  constructor() {
    this.collections = [];
  }
  add(watcher) {
    this.collections.push(watcher);
  }
  notify() {
    this.collections.forEach((watcher) => {
      watcher.update();
    });
  }
}
class Watcher {
  constructor() {
    Dep.self = this;
  }
  update() {
    console.log('更新了');
  }
}

function defineData(data, key, value) {
  let dep = new Dep();
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      dep.add(Dep.self);
      return value;
    },
    set: function(newValue) {
      if (value == newValue) {
        return;
      }
      dep.notify();
    }
  });
}

function observer(data) {
  if (typeof data != 'object') {
    return;
  }
  Object.keys(data).forEach((key) => {
    defineData(data, key, data[key]);
  });
}
class Vue {
  constructor(options) {
    this._data = options.data;
    observer(this._data);
    // 在这里模拟render的过程
    new Watcher();
    this._data.value = this._data.value;
  }
}
let myVue = new Vue({
  data: {
    value: 'hello world'
  }
});
myVue._data.value = 'nice world';
Dep.self = null;
  1. 依赖收集的前提条件是 1. 新建一个 Watcher 对象 2. 触发了 get 方法
  2. 依赖收集主要是get进行依赖收集,set通过观察者更新视图
  3. 记住一个过程:当读取所需对象的值时会触发 setter 方法(observer 方法会触发 setter 方法),setter->Dep->Watcher->patch->视图

如何实现 Virtual DOM

class VNode {
  constructor(tag, data, children, text, elm) {
    /*当前节点的标签名*/
    this.tag = tag;
    /*当前节点的一些数据信息,比如props、attrs等数据*/
    this.data = data;
    /*当前节点的子节点,是一个数组*/
    this.children = children;
    /*当前节点的文本*/
    this.text = text;
    /*当前虚拟节点对应的真实dom节点*/
    this.elm = elm;
  }
}
console.log(
  new VNode(
    'span',
    {
      /* 指令集合数组 */
      directives: [
        {
          /* v-show指令 */
          rawName: 'v-show',
          expression: 'isShow',
          name: 'show',
          value: true
        }
      ],
      /* 静态class */
      staticClass: 'demo'
    },
    [new VNode(undefined, undefined, undefined, 'This is a span.')]
  )
);

总结

今天学习了 vue 的初始化和挂载、vue 是如何编译的、响应式系统的基本原理,虚拟 DOM 是如何生成的、响应式系统的依赖收集追踪原理、更新视图 patch 的比较算法

暂无评论