导航

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

[[react] Hooks](https://juejin.im/post/684490...)

[[部署01] Nginx](https://juejin.im/post/684490...)
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...)
[[部署03] gitlab-CI](https://juejin.im/post/684490...)

[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...)
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...)
[[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...)
[[源码] Redux React-Redux01](https://juejin.im/post/684490...)
[[源码] axios ](https://juejin.im/post/684490...)
[[源码] vuex ](https://juejin.im/post/684490...)
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...)
[[源码-vue02] computed 响应式 - 初始化,拜访,更新过程 ](https://juejin.im/post/684490...)
[[源码-vue03] watch 侦听属性 - 初始化和更新 ](https://juejin.im/post/684490...)
[[源码-vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490...)
[[源码-vue05] Vue.extend ](https://juejin.im/post/684490...)

[[源码-vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790...)

前置常识

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响应式

  • <font color=blue size=5>data响应式具体过程</font>

    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存在,就初始化 propsif (opts.methods) initMethods(vm, opts.methods)// methods存在,就初始化 methodsif (opts.data) {  // data存在,初始化 data  initData(vm)} else {  // data不存在  observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed) // computed存在,初始化 computedif (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在不是组件的状况下,是合并后的optionsdata = vm._data = typeof data === 'function'  ? getData(data, vm)  : data || {}// data是一个函数就调用取返回值,是对象就间接赋值  // vm._data = vm.$options.dataif (!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 instanceconst keys = Object.keys(data)const props = vm.$options.propsconst methods = vm.$options.methodslet i = keys.lengthwhile (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 | voidif (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 $dataconstructor (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<string>) {      //   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<any>) {  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/settersconst getter = property && property.getconst setter = property && property.setif ((!getter || setter) && arguments.length === 2) {  val = obj[key]}let childOb = !shallow && observe(val)// 持续观测对象的每一项的value值,如果还是对象就持续察看 增加响应Object.definePropertyObject.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// 即每次执行都自增1subs: Array<Watcher>;// 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// 以后watcherconst 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) 初始化渲染

(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)
  • <font color=red>vm.$mount</font>

    • 有两种版本,runtime版和runtime+compiler版本,但最终都会调用 public mount method 的$mount
  • <font color=blue size=5>初始化渲染过程</font>

    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) 办法
    2. 执行 mountComponent(this, el, hydrating) 函数

      • 实例化render watcher即并把updateComponent=()=>{vm._update(vm._render(), hydrating)}函数作为new Watcher(vm, updateComponent, noop,{})的第二个参数传入
    3. new Watcher(vm, updateComponent, noop,{})

      • 执行get()办法
      • 在get办法中将以后watcher赋值给Dep.target
      • 在get办法中执行updateComponent办法,从而执行vm._update(vm._render(), hydrating)办法
    4. 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 <html> or <body> - mount to normal elements instead.`  )  return this  // el不能是body或html标签,因为会被笼罩}const options = this.$options// resolve template/el and convert to render functionif (!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 = elif (!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 hookif (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/684490...
三种 watcher https://juejin.im/post/684490...
大滴滴 https://juejin.im/post/684490...