[vue源码01] data响应式 和 初始化渲染

导航

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数

[react] Hooks

[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick

前置知识

Watcher类

  • Watcher分为三种
    • computed watcher - 负责更新computed
    • user-watcher - watch 一个函数
    • render-watcher - 重新渲染
  • 三种watcher有固定的执行顺序
    • computed watcher -> user watcher -> render watcher
    • 这样安排三种 watcher 的顺序的原因?
      • computed watcher 在 render watcher 前面执行,就能保证在视图渲染的时候拿到的是最新的computed

(1) data响应式

[图片上传失败...(image-581ea4-1632444988304)]

  • data响应式具体过程

    1. new Vue(options)构造函数中调用this._init(options)方法,而this._init(options)方法是在initMixin(Vue)中定义的
    2. Vue.prototype._init => 这里主要关注 initState(vm)
      • 合并 options 对象
      • 调用 initProxy
      • 调用 initState(vm)
      • 其他数据的初始化
      • vm.$mount(vm.$options.el) 初始化渲染
    3. initState(vm) => 这里主要关注 initData
      • initProps
      • initMethods
      • initData
      • initComputed
      • initWatch
    4. initData
      • 传入的options对象的data,是函数就调用返回对象,是对象就直接使用
      • 如果props,method,data中有相同的key值就抛出警告
      • proxy(vm,_data, key)
        • 主要的作用就是给data中的属性做一层代理
        • 通过访问 this.name = vm.name = vm._data.name = vm.$options.data.name = vm.data.name
      • observe(data, true)
    5. observe(data, true)
      • 判断data是否具有__ob__属性,该属性表示data是否观察过了,即具有响应式了
      • 没有 __ob__属性,就就行观测,执行 new Observer(value)
    6. new Observer(value)
      • 给data添加__ob__属性,值是当前的 observer 实例
      • data是数组
        • 具有原型执行 protoAugment(value, arrayMethods)
          • 重写数组原型上的 7 种方法
          • push pop unshift shift splice sort reverse 这7种都改变数组
          • push unshift splice 添加的属性包装成数组
              1. 继续执行 ob.observeArray(inserted) 循环遍历添加的每一项属性,执行第5步中的observe(items[i])
              1. 并调用ob.dep.notify()执行watcher种的upate去更新视图,从而是这些重写的数组方法具有响应式
        • 不具有有原型执行 copyAugment(value, arrayMethods, arrayKeys)
        • this.observeArray(value)
          • 循环遍历数组种的每一个成员,执行第5步中的observe(items[i])
      • data是对象
        • this.walk(value)
    7. this.walk(value)
      • defineReactive(obj, keys[i])
    8. defineReactive(obj, keys[i])
      • Object.defineProperty
        • get
          • dep.depend()
        • set
          • dep.notify()
  • 源码

  • initState - src/core/instance/state.js

initState - src/core/instance/state.js
---

export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options // opts 获取vm中的 options 参数
  if (opts.props) initProps(vm, opts.props) // props存在,就初始化 props
  if (opts.methods) initMethods(vm, opts.methods)// methods存在,就初始化 methods

  if (opts.data) {
    // data存在,初始化 data
    initData(vm)
  } else {
    // data不存在
    observe(vm._data = {}, true /* asRootData */)
  }

  if (opts.computed) initComputed(vm, opts.computed) // computed存在,初始化 computed
  if (opts.watch && opts.watch !== nativeWatch) {
      // watch存在,并且不是原生的对象上的watch属性,就初始化 watch
      // 分为三种 watcher
          // render watcher
          // compute watcher
          // user watcher - watch
    initWatch(vm, opts.watch)
  }
}
  • initData - src/core/instance/state.js
initData - src/core/instance/state.js
---

function initData (vm: Component) {
  let data = vm.$options.data
  // 获取传入的options对象上的data属性
  // 注意:这里的vm.$options在不是组件的情况下,是合并后的options

  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  // data是一个函数就调用取返回值,是对象就直接赋值
    // vm._data = vm.$options.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
        )
        // method中存在了该key,所以data中不能再有相同的key
      }
    }
    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
        // props中存在了该key,所以data中不能再有相同的key
      )
    } else if (!isReserved(key)) {
      // props和methods中都不存在该key,就执行代理proxy函数
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  // data的响应式
  observe(data, true /* asRootData */)
}
  • proxy - src/core/instance/state.js
proxy - src/core/instance/state.js
---

export function proxy (target: Object, sourceKey: string, key: string) {
  // proxy(vm, `_data`, key)
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
    // 1. 重写 get
    // 2. 返回 this._data[key]
    // 3. this指向:sharedPropertyDefinition.get方法是通过 vm.key 来调用的,this指向vm
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
    // 1. 重写 set
    // 2. this._data[key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
  // vm[key] = vm._data[key]
  // 因为: vm._data = vm.$options.data
  // 所以:vm[key] = vm._data[key] =  vm.$options.data[key]
}
  • observe - src/core/observer/index.js
observe - src/core/observer/index.js
---

export function  observe (value: any, asRootData: ?boolean): Observer | void  {
  // 1. observe(vm._data = {}, true /* asRootData */)
  // 2. observe(data, true /* asRootData */)
  if (!isObject(value) || value instanceof VNode) {
    // 不是一个对象 或者 是VNode 
    return
  }
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    // 如果value具有__ob__属性 并且 __ob__ 是Observer的实例,就直接赋值
    // 即已经观测过了, 直接赋值
    ob = value.__ob__
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
    // 生成一个ob实例
  }
  if (asRootData && ob) {
    // 如果是根data即new Vue()初始化的时候传入的data
    // 并且 ob 存在
    // vmCount++
      // 即统计被生成的次数
    ob.vmCount++
  }
  return ob
}
  • Observer - src/core/observer/index.js
Observer  - src/core/observer/index.js
---

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value // 赋值传入的对象
    this.dep = new Dep() // dep实例
    this.vmCount = 0
    def(value, '__ob__', this)
    // def
    // 给 ( value ) 添加 ( __ob__ ) 属性,值是 ( observer ) 实例
      // function def (obj: Object, key: string, val: any, enumerable?: boolean) {
      //   Object.defineProperty(obj, key, {
      //     value: val,
      //     enumerable: !!enumerable,
      //     writable: true,
      //     configurable: true
      //   })
      // }
    if (Array.isArray(value)) {
      if (hasProto) {
        // const hasProto = '__proto__' in {}
        // 是数组,并且本身具有原型属性__proto__
        protoAugment(value, arrayMethods)
        // protoAugment 重写原型
          // value.__proto__ = arrayMethods
        // arrayMethods
          // const arrayMethods = Object.create(arrayProto)
          // const arrayProto = Array.prototype
          //  def(arrayMethods, method, function mutator (...args){})
          // 重写 7 种数组原型上的方法
      } else {
        // 是数组,并且本身不具有原型,复制原型上的每一个属性
        copyAugment(value, arrayMethods, arrayKeys)
        // function copyAugment (target: Object, src: Object, keys: Array) {
        //   for (let i = 0, l = keys.length; i < l; i++) {
        //     const key = keys[i]
        //     def(target, key, src[key])
        //   }
        // }

        // const arrayKeys = Object.getOwnPropertyNames(arrayMethods)


      }
      this.observeArray(value)
      // 观测数组
        // 1. 循环数组的每一项, 对每一项执行 observe(items[i])
    } else {
      this.walk(value)
       // 观测对象
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
  • walk - src/core/observer/index.js
walk - src/core/observer/index.js
---

  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
  • defineReactive - src/core/observer/index.js
defineReactive - src/core/observer/index.js
---

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    // 不存在 或者 属性描述对象不可以被修改,就直接返回
    return
  }
  // Object.getOwnPropertyDescriptor(obj, key) 表示获取obj.key属性属性描述对象

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  let childOb = !shallow && observe(val)
  // 继续观测对象的每一项的value值,如果还是对象就继续观察 添加响应Object.defineProperty


  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend() // 循环收集依赖
          if (Array.isArray(value)) {
            // 如果每一项的key对象的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
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify() // ---------------------------- 通知watcher执行更新
    }
  })
}

  • Dep类 - src/core/observer/dep.js
Dep类 - src/core/observer/dep.js
---


export default class Dep {
  static target: ?Watcher;
  // target一个 watcher 类型的静态属性

  id: number;
  // 每次new都使id+1
  // 即每次执行都自增1

  subs: Array;
  // subs数组 用来存放 wacher 

  constructor () {
    this.id = uid++ // id++ uid++ 每次执行都加 +1
    this.subs = [] // 初始化为空数组,存放依赖即watcher
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
    // 添加 watcher
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
    // 删除 watcher
  }

  depend () {
    if (Dep.target) { //--------------------------------------------- 重点注意 Dep.target
      // Dep.target 是当前正在执行的正在计算的 watcher,存在闭包中,相当于全局变量
      // 在下面有初始化

      Dep.target.addDep(this) // ------------------------------------ Dep 和 Watcher 相互关系
      // Dep.target.addDep(this)
        // 向 watcher 中添加 dep 实例
        // this参数就是dep实例
        // Dep.target 就是一个正在计算的watcher
      
      // Watcher中的 addDep
        // addDep (dep: Dep) {
        //   const id = dep.id // dep实例的id属性
        //   if (!this.newDepIds.has(id)) { // ------------ newDepIds中不存在该id
        //     this.newDepIds.add(id) // 向 ( Watcher类 ) 的 newDepIds中添加 id
        //     this.newDeps.push(dep) // 向 ( Watcher类 ) 的 newDeps 中添加 dep
        //     if (!this.depIds.has(id)) { // ------------- depIds 中不存在该id
        //       dep.addSub(this)   // 向 ( Dep类 ) 的 subs 中添加 该watcher
        //     }
        //   }
        // }
      
      // 这里操作有点绕
        // Dep.target.addDep(this) 
            // 1. 调用 Dep.target.addDep(this) = new Watcher().addDep(this)
            // 2. 把 dep 实例添加到 watcher的 newDeps 数组中
            // 3. 把 dep.id 添加到 watcher的 newDepIds 数组中
            // 4. 执行 dep.addSubs 把 watcher 添加到 Dep的 subs 数组中
    }
  }

  notify () {
    const subs = this.subs.slice()
    // 浅拷贝subs数组,缓存一份
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      subs.sort((a, b) => a.id - b.id)
    }

    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
      // 执行subs数组的每一个成员watcher上的update方法
    }
  }
}


Dep.target = null
// 当前watcher

const targetStack = []
// 存放watcher的栈结构,后进先出

export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  // watcher 入栈

  Dep.target = target
  // 赋值最新的watcher
}

export function popTarget () {
  targetStack.pop()
  // watcher 出栈

  Dep.target = targetStack[targetStack.length - 1]
  // 前一个watcher
}
  • Watcher类 - src/core/observer/dep.js
export default class Watcher {
  constructor (
    vm: Component, // vm实例
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean // 是否是renderWatcher
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      // 是renderWatcher, 则把该watcher实例赋值给 vm._watcher
      vm._watcher = this
    }
    vm._watchers.push(this) // push
    
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
      // 是函数赋值 getter
    } else {
      // 不是函数
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get()
  }
  
  get () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }
  
  addDep (dep: Dep) {
    const id = dep.id // dep实例的id属性
    if (!this.newDepIds.has(id)) { // ------------ newDepIds中不存在该id
      this.newDepIds.add(id) // 向 ( Watcher类 ) 的 newDepIds中添加 id
      this.newDeps.push(dep) // 向 ( Watcher类 ) 的 newDeps 中添加 dep
      if (!this.depIds.has(id)) { // ------------- depIds 中不存在该id
        dep.addSub(this)   // 向 ( Dep类 ) 的 subs 中添加 该watcher
      }
    }
  }
  
  update () {
    /* istanbul ignore else */
    if (this.lazy) { // 用于computed watcher
      this.dirty = true
    } else if (this.sync) { // 同步watcher
      this.run()
    } else {
      queueWatcher(this) // nextTick(flushSchedulerQueue)将watcher放入队列,在下一轮tick去更新
    }
  }
}

(2) 初始化渲染

[图片上传失败...(image-251f59-1632444988304)]

(2-1) vue的不同构建版本

  • 独立构建 (包括template编译的过程) - runtime+compiler完整版
    • 渲染过程:template -> render函数 -> vnode -> 真实的dom
  • 运行时候构建(不包括template编译的过程) - runtime版
    • 渲染过程:render函数 -> vnode -> 真实的dom
  • 打包后dist文件夹中的文件会有所不同
    • 独立构建的完整版 vue.js
    • 运行时版本 vue.runtime.js
      • 运行时版本相比完整版体积要小大约 30%

(2-2) dom的初始化渲染 - vm.$mount(vm.$options.el)

  • 执行 new Vue(options) => this._init(options) => Vue.prototype._init => initState(vm) => vm.$mount(vm.$options.el)

    • 即在this._init中先初始化initState()把data变成响应式后,就会执行dom挂载vm.$mount(vm.$options.el)
  • vm.$mount

    • 有两种版本,runtime版和runtime+compiler版本,但最终都会调用 public mount method 的$mount
  • 初始化渲染过程

    1. 执行 vm.$mount(vm.$options.el) 方法
    • 如果是runtime版本就是直接调用 mountComponent(this, el, hydrating) 方法
    • 如果是runtime+compiler版本(即传入new Vue()的参数对象中不存在render方法)就会先处理template,将template通过 compileToFunctions(template, options) 函数编译成render方法,然后调用 mountComponent(this, el, hydrating) 方法
    1. 执行 mountComponent(this, el, hydrating) 函数
    • 实例化render watcher即并把updateComponent=()=>{vm._update(vm._render(), hydrating)}函数作为new Watcher(vm, updateComponent, noop,{})的第二个参数传入
    1. new Watcher(vm, updateComponent, noop,{})
    • 执行get()方法
      • 在get方法中将当前watcher赋值给Dep.target
      • 在get方法中执行updateComponent方法,从而执行vm._update(vm._render(), hydrating)方法
    1. vm._update(vm._render(), hydrating)
    • vm._render()会把template转成 vnode
    • vm._update()则会把vnode挂载到真正的dom上,渲染出页面
  • 源码

  • src/core/instance/index.js

src/core/instance/index.js
---

function Vue (options) {
  this._init(options)
}
  • src/core/instance/init.js
src/core/instance/init.js
---

Vue.prototype._init = function (options?: Object) { // _init方法初始化
    initState(vm)
    if (vm.$options.el) {
        vm.$mount(vm.$options.el) 
        // mount方法 - 负责页面的挂载
        // 传入el,el是 new Vue({el: '#app'}) 这里的el
        
        // vm.$mount(vm.$options.el) 
        // 最终就是调用 mountComponent(this, el, hydrating) 方法
    }
}
  • src/platforms/web/entry-runtime-with-compiler.js
    总结: vm.$mount() 的作用就是:调用 mountComponent(this, el, hydrating) 方法
src/platforms/web/entry-runtime-with-compiler.js
总结:
  - vm.$mount(vm.$options.el) 的作用就是:调用 mountComponent(this, el, hydrating) 方法
---

const mount = Vue.prototype.$mount
// mount
  // mout的主要作用:缓存 runtime 版本的 $mount,代码如下:
    // mount = Vue.prototype.$mount = function (
    //   el?: string | Element,
    //   hydrating?: boolean
    // ): Component {
    //   el = el && inBrowser ? query(el) : undefined
    //   return mountComponent(this, el, hydrating)
    // }

Vue.prototype.$mount = function (
  // 这里是runtime+compiler版本的$mount方法
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  // query(el)
    // 主要作用:就是将el转成Element节点
    // 具体是:
      // 1. 是字符串并且存在,就查找id对应的dom
      // 2. 是字符串但是dom没有对应的元素,开发环境会抛出警告,然后就创建一个空的div返回
      // 3. 不是字符串,而是dom元素直接返回
    // function query (el: string | Element): Element {
    //   if (typeof el === 'string') {
    //     const selected = document.querySelector(el)
    //     if (!selected) {
    //       process.env.NODE_ENV !== 'production' && warn(
    //         'Cannot find element: ' + el
    //       )
    //       return document.createElement('div')
    //     }
    //     return selected
    //   } else {
    //     return el
    //   }
    // }

  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to  or  - mount to normal elements instead.`
    )
    return this
    // el不能是body或html标签,因为会被覆盖
  }

  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    // new Vue(options)时,options中没有render方法,就会去看是不是有template属性
    if (template) {
      if (typeof template === 'string') {
        // -------------------------------------------- template在options对象参数中存在,并且是字符串
        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在options对象参数中存在,是dom结构
        template = template.innerHTML
      } else {
        if (process.env.NODE_ENV !== 'production') {
          warn('invalid template option:' + template, this)
        }
        return this
      }
    } else if (el) {
      // ---------------------------------------------- template在options对象参数中不存在,就寻找el,el存在
      template = getOuterHTML(el)
      // 获取el对应的dom,并且赋值给template
    }
    // 其实上面一大段 if (template) 都是在处理 template

    if (template) {
      // 此时的template是经过上面处理过后的template

      /* istanbul ignore if */
      // mark直接滤过
      // if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      //   mark('compile')
      // }

      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      // compileToFunctions
        // 主要就是将模板 template 编译成 render 函数

      options.render = render 
      options.staticRenderFns = staticRenderFns
      // 将render函数挂载到$options上

      /* istanbul ignore if */
      // if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      //   mark('compile end')
      //   measure(`vue ${this._name} compile`, 'compile', 'compile end')
      // }
    }
  }
  return mount.call(this, el, hydrating)
  // 调用上面缓存的 mount 方法
    // mount 中会调用  mountComponent(this, el, hydrating)

    // mount = Vue.prototype.$mount = function (
    //   el?: string | Element,
    //   hydrating?: boolean
    // ): Component {
    //   el = el && inBrowser ? query(el) : undefined
    //   return mountComponent(this, el, hydrating)
    // }

    // 这里执行 mount 方法时,还会再次执行 query(el) 方法,多了一次,只是runtime版本考虑的
}
  • src/core/instance/lifecycle.js
src/core/instance/lifecycle.js
---

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode
    ...
  }
  callHook(vm, 'beforeMount') // ------------------------ beforeMount 生命周期钩子

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    ...
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
    // updateComponent 的赋值
      // 赋值:是在这里赋值的
      // 执行:是触发了响应式的 set 方法,调用watcher.update()方法时执行的
  }

  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher 这里的watcher就是渲染watcher*/)
  // watcher分为三种
    // 1. computed watcher
    // 2. normal watcher 用户自定义的 watcher - 即 watch 对象中的方法
    // 3. render watcher
  // wathcer的执行顺序是固定的
    // computed watcher -> normal watcher -> render watcher
    // 这样就能保证在渲染时能拿到最新的 computed 和 执行了 watche 中定义的函数

  hydrating = false

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')  // ------------------------ mounted 生命周期钩子
  }
  return vm
}
  • src/core/observer/watcher.js
src/core/observer/watcher.js
---

export default class Watcher {
  constructor (
    vm: Component,
    expOrFn: string | Function,
    cb: Function,
    options?: ?Object,
    isRenderWatcher?: boolean
  ) {
    this.vm = vm
    if (isRenderWatcher) {
      // 如果是渲染watcher - renderWatcher
      vm._watcher = this
      // 就在组件实例上添加 _watcher 属性,值就是该renderWatcher实例
    }
    vm._watchers.push(this)
    // 向 _watchers 数组中添加该renderWatcher

    if (typeof expOrFn === 'function') {
      this.getter = expOrFn
      // expOrFn 是函数就赋值给 getter
        // 1. 如果是renderWatcher的话 this.getter = updateComponent方法
          // updateComponent 方法返回 vm._update(vm._render(), hydrating)
    } else {
     ...
    }
    this.value = this.lazy
      ? undefined
      : this.get()
    // lazy属性只有 computed Watcher 才有
      // 1. render watcher 就会调用 get() 方法并赋值给 this.value
  }

  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
    pushTarget(this)
    // pushTarget 两个作用
    // 1. 向 targetStack 数组中添加该 watcher,这里是render watcher
    // 2. Dep.target = this 将该render watcher 赋值给 Dep.target,表示正在执行的是渲染watcher

    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
      // 调用 getter 函数
      // 这里因为是渲染watcher,所以执行的是 updateComponent
      // updateComponent = vm._update(vm._render(), hydrating)

      // 注意这里是重点
        // 执行 vm._update(vm._render(), hydrating)
        // 1. 因为在执行 vm._update 方法的过程中,会获取响应式data中的属性,触发get进行依赖收集
        // 2. vm._render() 将template转成 vnode
        // 2. vm._update() 对比后将 vnode 转成真正的 DOM
    } catch (e) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }
}

参考

详细 - 目前能找到的写得最好的 https://juejin.im/post/6844903648623853581
三种 watcher https://juejin.im/post/6844904128435470350
大滴滴 https://juejin.im/post/6844903450455588871

你可能感兴趣的:([vue源码01] data响应式 和 初始化渲染)