current position:Home>[Vue source code 01] data responsive and initialization rendering

[Vue source code 01] data responsive and initialization rendering

2021-08-25 09:33:35 woow_ wu7



Navigation

[[ thorough 01] Execution context ](https://juejin.im/post/684490...)
[[ thorough 02] Prototype chain ](https://juejin.im/post/684490...)
[[ thorough 03] Inherit ](https://juejin.im/post/684490...)
[[ thorough 04] The event loop ](https://juejin.im/post/684490...)
[[ thorough 05] currying Partial function Function memory ](https://juejin.im/post/684490...)
[[ thorough 06] Implicit conversion and Operator ](https://juejin.im/post/684490...)
[[ thorough 07] Browser caching mechanism (http Caching mechanisms )](https://juejin.im/post/684490...)
[[ thorough 08] Front end security ](https://juejin.im/post/684490...)
[[ thorough 09] Depth copy ](https://juejin.im/post/684490...)
[[ thorough 10] Debounce Throttle](https://juejin.im/post/684490...)
[[ thorough 11] Front-end routing ](https://juejin.im/post/684490...)
[[ thorough 12] Front-end modularization ](https://juejin.im/post/684490...)
[[ thorough 13] Observer mode Publish subscribe mode Two way data binding ](https://juejin.im/post/684490...)
[[ thorough 14] canvas](https://juejin.im/post/684490...)
[[ thorough 15] webSocket](https://juejin.im/post/684490...)
[[ thorough 16] webpack](https://juejin.im/post/684490...)
[[ thorough 17] http and https](https://juejin.im/post/684490...)
[[ thorough 18] CSS-interview](https://juejin.im/post/684490...)
[[ thorough 19] Handwriting Promise](https://juejin.im/post/684490...)
[[ thorough 20] Handwritten functions ](https://juejin.im/post/684490...)

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

[[ Deploy 01] Nginx](https://juejin.im/post/684490...)
[[ Deploy 02] Docker Deploy vue project ](https://juejin.im/post/684490...)
[[ Deploy 03] gitlab-CI](https://juejin.im/post/684490...)

[[ Source code -webpack01- Pre knowledge ] AST Abstract syntax tree ](https://juejin.im/post/684490...)
[[ Source code -webpack02- Pre knowledge ] Tapable](https://juejin.im/post/684490...)
[[ Source code -webpack03] Handwriting webpack - compiler Simple compilation process ](https://juejin.im/post/684490...)
[[ Source code ] Redux React-Redux01](https://juejin.im/post/684490...)
[[ Source code ] axios ](https://juejin.im/post/684490...)
[[ Source code ] vuex ](https://juejin.im/post/684490...)
[[ Source code -vue01] data Response type and Initialize rendering ](https://juejin.im/post/684490...)
[[ Source code -vue02] computed Response type - initialization , visit , The update process ](https://juejin.im/post/684490...)
[[ Source code -vue03] watch Listening properties - Initialization and update ](https://juejin.im/post/684490...)
[[ Source code -vue04] Vue.set and vm.$set ](https://juejin.im/post/684490...)
[[ Source code -vue05] Vue.extend ](https://juejin.im/post/684490...)

[[ Source code -vue06] Vue.nextTick and vm.$nextTick ](https://juejin.im/post/684790...)

Pre knowledge

Watcher class

  • Watcher Divided into three

    • computed watcher - Is responsible for updating computed
    • user-watcher - watch A function
    • render-watcher - To render
  • Three watcher There is a fixed execution sequence

    • computed watcher -> user watcher -> render watcher
    • There are three kinds of watcher The reason for the order of ?

      • computed watcher stay render watcher Go ahead , You can ensure that you get the latest when rendering the view computed

(1) data Response type

  • <font color=blue size=5>data Responsive specific process </font>

    1. stay new Vue(options) Invocation in constructor this._init(options) Method , and this._init(options) The method is in initMixin(Vue) As defined in
    2. Vue.prototype._init => The main concern here is initState(vm)

      • Merge options object
      • call initProxy
      • call initState(vm)
      • Initialization of other data
      • vm.$mount(vm.$options.el) Initialize rendering
    3. initState(vm) => The main concern here is initData

      • initProps
      • initMethods
      • initData
      • initComputed
      • initWatch
    4. initData

      • Incoming options Object's data, If it is a function call, it returns the object , If it's an object, use it directly
      • If props,method,data There is the same in key Value throws a warning
      • proxy(vm, _data, key)

        • The main function is to give data The properties in are used as a layer of proxy
        • By visiting this.name = vm.name = vm._data.name = vm.$options.data.name = vm.data.name
      • observe(data, true)
    5. observe(data, true)

      • Judge data Whether they have __ob__ attribute , The attribute represents data Have you observed , That is, it has a response
      • No, __ob__ attribute , Just observe , perform new Observer(value)
    6. new Observer(value)

      • to data add to __ob__ attribute , Value is current observer example
      • data It's an array

        • With prototype execution protoAugment(value, arrayMethods)

          • Rewrite the... On the array prototype 7 Methods
          • push pop unshift shift splice sort reverse this 7 Both change the array
          • push unshift splice Wrap the added attributes into an array

              1. Carry on ob.observeArray(inserted) Loop through each attribute added , Execution section 5 In step observe(items[i])
              1. And call ob.dep.notify() perform watcher Species upate To update the view , Instead, these overridden array methods are responsive
        • No prototype execution copyAugment(value, arrayMethods, arrayKeys)
        • this.observeArray(value)

          • Loop through each member of the array , Execution section 5 In step observe(items[i])
      • data It's the object

        • this.walk(value)
    7. this.walk(value)

      • defineReactive(obj, keys[i])
    8. defineReactive(obj, keys[i])

      • Object.defineProperty

        • get

          • dep.depend()
        • set

          • dep.notify()
  • Source code
  • initState - src/core/instance/state.js

    initState - src/core/instance/state.js
    ---
    
    export function initState (vm: Component) {
    vm._watchers = []
    const opts = vm.$options // opts  obtain vm Medium  options  Parameters 
    if (opts.props) initProps(vm, opts.props) // props There is , Just initialize  props
    if (opts.methods) initMethods(vm, opts.methods)// methods There is , Just initialize  methods
    
    if (opts.data) {
      // data There is , initialization  data
      initData(vm)
    } else {
      // data non-existent 
      observe(vm._data = {}, true /* asRootData */)
    }
    
    if (opts.computed) initComputed(vm, opts.computed) // computed There is , initialization  computed
    if (opts.watch && opts.watch !== nativeWatch) {
        // watch There is , And not on native objects watch attribute , Just initialize  watch
        //  Divided into three  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
    //  Get incoming options On the object data attribute 
    //  Be careful : there vm.$options In the case of non components , It's a merged options
    
    data = vm._data = typeof data === 'function'
      ? getData(data, vm)
      : data || {}
    // data Is a function call to get the return value , If it is an object, it is directly assigned 
      // vm._data = vm.$options.data
    
    if (!isPlainObject(data)) {
      //  Not an object , Not a pure object assignment, empty object 
      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 The... Exists in key, therefore data There can no longer be the same 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 The... Exists in key, therefore data There can no longer be the same key
        )
      } else if (!isReserved(key)) {
        // props and methods There is no such thing as key, Just execute the agent proxy function 
        proxy(vm, `_data`, key)
      }
    }
    // observe data
    // data In response 
    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.  rewrite  get
      // 2.  return  this._data[key]
      // 3. this Point to :sharedPropertyDefinition.get The way is through  vm.key  To invoke the ,this Point to vm
    }
    sharedPropertyDefinition.set = function proxySetter (val) {
      this[sourceKey][key] = val
      // 1.  rewrite  set
      // 2. this._data[key] = val
    }
    Object.defineProperty(target, key, sharedPropertyDefinition)
    // vm[key] = vm._data[key]
    //  because : vm._data = vm.$options.data
    //  therefore :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) {
      //  Not an object   perhaps   yes VNode 
      return
    }
    let ob: Observer | void
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
      //  If value have __ob__ attribute   also  __ob__  yes Observer Example , Just assign it directly 
      //  That is, it has been observed ,  Direct assignment 
      ob = value.__ob__
    } else if (
      shouldObserve &&
      !isServerRendering() &&
      (Array.isArray(value) || isPlainObject(value)) &&
      Object.isExtensible(value) &&
      !value._isVue
    ) {
      ob = new Observer(value)
      //  Generate a ob example 
    }
    if (asRootData && ob) {
      //  If it's a root data namely new Vue() It was passed in during initialization data
      //  also  ob  There is 
      // vmCount++
        //  That is, count the number of times generated 
      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 //  Assign the passed in object 
      this.dep = new Dep() // dep example 
      this.vmCount = 0
      def(value, '__ob__', this)
      // def
      //  to  ( value )  add to  ( __ob__ )  attribute , The value is  ( observer )  example 
        // 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 {}
          //  It's an array , And it has prototype properties __proto__
          protoAugment(value, arrayMethods)
          // protoAugment  Rewriting prototype 
            // value.__proto__ = arrayMethods
          // arrayMethods
            // const arrayMethods = Object.create(arrayProto)
            // const arrayProto = Array.prototype
            //  def(arrayMethods, method, function mutator (...args){})
            //  rewrite  7  A method on the array prototype 
        } else {
          //  It's an array , And it doesn't have a prototype , Copy every attribute on the prototype 
          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)
        //  Observation array 
          // 1.  Loop through each item of the array ,  Carry out... For each item  observe(items[i])
      } else {
        this.walk(value)
         //  Observation object 
      }
    }
    
    /**
     * 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) {
        //  non-existent   perhaps   Property description object cannot be modified , Go straight back 
      return
    }
    // Object.getOwnPropertyDescriptor(obj, key)  Said to get obj.key Property describes the object 
    
    // 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)
    //  Continue to observe each item of the object value value , If it's still an object, continue to observe   Add response 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() //  Circular collection dependency 
            if (Array.isArray(value)) {
              //  If each item key Object's value Is an array 
    
              dependArray(value)
              //  Collection dependency 
            }
          }
        }
        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() // ----------------------------  notice watcher Perform the update 
      }
    })
    }
    
  • Dep class - src/core/observer/dep.js

    Dep class  - src/core/observer/dep.js
    ---
    
    
    export default class Dep {
    static target: ?Watcher;
    // target One  watcher  Static properties of type 
    
    id: number;
    //  Every time new All make id+1
    //  That is, each execution is self incremented 1
    
    subs: Array<Watcher>;
    // subs Array   For storage  wacher 
    
    constructor () {
      this.id = uid++ // id++ uid++  Add... Every time  +1
      this.subs = [] //  Initialize to an empty array , Storage dependency is watcher
    }
    
    addSub (sub: Watcher) {
      this.subs.push(sub)
      //  add to  watcher
    }
    
    removeSub (sub: Watcher) {
      remove(this.subs, sub)
      //  Delete  watcher
    }
    
    depend () {
      if (Dep.target) { //---------------------------------------------  Focus on  Dep.target
        // Dep.target  Is currently executing and calculating  watcher, Exist in closures , Equivalent to a global variable 
        //  There is initialization below 
    
        Dep.target.addDep(this) // ------------------------------------ Dep  and  Watcher  interrelation 
        // Dep.target.addDep(this)
          //  towards  watcher  Add  dep  example 
          // this The parameter is dep example 
          // Dep.target  It's a calculation watcher
        
        // Watcher Medium  addDep
          // addDep (dep: Dep) {
          //   const id = dep.id // dep Example of id attribute 
          //   if (!this.newDepIds.has(id)) { // ------------ newDepIds There is no such thing as id
          //     this.newDepIds.add(id) //  towards  ( Watcher class  )  Of  newDepIds Add  id
          //     this.newDeps.push(dep) //  towards  ( Watcher class  )  Of  newDeps  Add  dep
          //     if (!this.depIds.has(id)) { // ------------- depIds  There is no such thing as id
          //       dep.addSub(this)   //  towards  ( Dep class  )  Of  subs  Add   The watcher
          //     }
          //   }
          // }
        
        //  The operation here is a little around 
          // Dep.target.addDep(this) 
              // 1.  call  Dep.target.addDep(this) = new Watcher().addDep(this)
              // 2.  hold  dep  Instance added to  watcher Of  newDeps  Array 
              // 3.  hold  dep.id  Add to  watcher Of  newDepIds  Array 
              // 4.  perform  dep.addSubs  hold  watcher  Add to  Dep Of  subs  Array 
      }
    }
    
    notify () {
      const subs = this.subs.slice()
      //  Shallow copy subs Array , Cache a copy 
      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()
        //  perform subs Every member of the array watcher Upper update Method 
      }
    }
    }
    
    
    Dep.target = null
    //  At present watcher
    
    const targetStack = []
    //  Deposit watcher Stack structure of , Last in, first out 
    
    export function pushTarget (target: ?Watcher) {
    targetStack.push(target)
    // watcher  Push 
    
    Dep.target = target
    //  Assign the latest watcher
    }
    
    export function popTarget () {
    targetStack.pop()
    // watcher  Out of the stack 
    
    Dep.target = targetStack[targetStack.length - 1]
    //  Previous watcher
    }
  • Watcher class - src/core/observer/dep.js

    export default class Watcher {
    constructor (
      vm: Component, // vm example 
      expOrFn: string | Function,
      cb: Function,
      options?: ?Object,
      isRenderWatcher?: boolean //  Whether it is renderWatcher
    ) {
      this.vm = vm
      if (isRenderWatcher) {
        //  yes renderWatcher,  Then put the watcher Instance assigned to  vm._watcher
        vm._watcher = this
      }
      vm._watchers.push(this) // push
      
      // parse expression for getter
      if (typeof expOrFn === 'function') {
        this.getter = expOrFn
        //  Is a function assignment  getter
      } else {
        //  It's not a function 
        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 Example of id attribute 
      if (!this.newDepIds.has(id)) { // ------------ newDepIds There is no such thing as id
        this.newDepIds.add(id) //  towards  ( Watcher class  )  Of  newDepIds Add  id
        this.newDeps.push(dep) //  towards  ( Watcher class  )  Of  newDeps  Add  dep
        if (!this.depIds.has(id)) { // ------------- depIds  There is no such thing as id
          dep.addSub(this)   //  towards  ( Dep class  )  Of  subs  Add   The watcher
        }
      }
    }
    
    update () {
      /* istanbul ignore else */
      if (this.lazy) { //  be used for computed watcher
        this.dirty = true
      } else if (this.sync) { //  Sync watcher
        this.run()
      } else {
        queueWatcher(this) // nextTick(flushSchedulerQueue) take watcher Put in queue , In the next round tick To update 
      }
    }
    }

(2) Initialize rendering

 Initialize rendering .png

(2-1) vue Different build versions of

  • Build... Independently ( Include template Compilation process ) - runtime+compiler Full version

    • Rendering process :template -> render function -> vnode -> Actual dom
  • Build at run time ( barring template Compilation process ) - runtime edition

    • Rendering process :render function -> vnode -> Actual dom
  • After packing dist The files in the folder will be different

    • A full version built independently vue.js
    • Runtime version vue.runtime.js

      • The runtime version is about... Smaller than the full version 30%

(2-2) dom Initial rendering of - vm.$mount(vm.$options.el)

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

    • That is to say this._init Initialize first initState() hold data When it becomes responsive , Will execute dom mount vm.$mount(vm.$options.el)
  • <font color=red>vm.$mount</font>

    • There are two versions ,runtime Version and runtime+compiler edition , But eventually it will call public mount method Of $mount
  • <font color=blue size=5> Initialize the render pass </font>

    1. perform vm.$mount(vm.$options.el) Method

      • If it is runtime Version is a direct call to mountComponent(this, el, hydrating) Method
      • If it is runtime+compiler edition ( The incoming new Vue() Does not exist in the parameter object of render Method ) Will deal with... First template, take template adopt compileToFunctions(template, options) The function is compiled to render Method , And then call mountComponent(this, el, hydrating) Method
    2. perform mountComponent(this, el, hydrating) function

      • Instantiation render watcher That is, and put updateComponent=()=>{vm._update(vm._render(), hydrating)} Function as new Watcher(vm, updateComponent, noop,{}) The second parameter of is passed in
    3. new Watcher(vm, updateComponent, noop,{})

      • perform get() Method
      • stay get The current... In the method watcher Assign a value to Dep.target
      • stay get Method execution updateComponent Method , To carry out vm._update(vm._render(), hydrating) Method
    4. vm._update(vm._render(), hydrating)

      • vm._render() Will be able to template Turn into vnode
      • vm._update() It will put vnode Mount to the real dom On , Render the page
  • Source code
  • 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 Method initialization 
      initState(vm)
      if (vm.$options.el) {
          vm.$mount(vm.$options.el) 
          // mount Method  -  Be responsible for page mounting 
          //  Pass in el,el yes  new Vue({el: '#app'})  there el
          
          // vm.$mount(vm.$options.el) 
          //  The end result is a call  mountComponent(this, el, hydrating)  Method 
      }
    }
  • src/platforms/web/entry-runtime-with-compiler.js
    summary : vm.$mount() Is that : call mountComponent(this, el, hydrating) Method

    src/platforms/web/entry-runtime-with-compiler.js
     summary :
    - vm.$mount(vm.$options.el)  Is that : call  mountComponent(this, el, hydrating)  Method 
    ---
    
    const mount = Vue.prototype.$mount
    // mount
    // mout Main role of : cache  runtime  Version of  $mount, The code is as follows :
      // 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 (
    //  Here is runtime+compiler Version of $mount Method 
    el?: string | Element,
    hydrating?: boolean
    ): Component {
    el = el && query(el)
    // query(el)
      //  The main role : Will be el Turn into Element node 
      //  The concrete is :
        // 1.  Is a string and exists , Just look for it id Corresponding dom
        // 2.  It's a string, but dom There is no corresponding element , The development environment will throw a warning , Then create an empty div return 
        // 3.  It's not a string , It is dom Element directly returns 
      // 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 It can't be body or html label , Because it will be covered 
    }
    
    const options = this.$options
    // resolve template/el and convert to render function
    if (!options.render) {
      let template = options.template
      // new Vue(options) when ,options There is no render Method , Will see if there is template attribute 
      if (template) {
        if (typeof template === 'string') {
          // -------------------------------------------- template stay options There is... In the object parameter , And it's a 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 stay options There is... In the object parameter , yes dom structure 
          template = template.innerHTML
        } else {
          if (process.env.NODE_ENV !== 'production') {
            warn('invalid template option:' + template, this)
          }
          return this
        }
      } else if (el) {
        // ---------------------------------------------- template stay options Object parameter does not exist , Just look el,el There is 
        template = getOuterHTML(el)
        //  obtain el Corresponding dom, And assigned to template
      }
      //  In fact, there is a large section above  if (template)  It's all dealing with  template
    
      if (template) {
        //  At this time template After the above treatment template
    
        /* istanbul ignore if */
        // mark Direct filtration 
        // 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
          //  The main thing is to put the template  template  Translate it into  render  function 
    
        options.render = render 
        options.staticRenderFns = staticRenderFns
        //  take render Function mounted to $options On 
    
        /* 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)
    //  Call the cache above  mount  Method 
      // mount  Will call   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)
      // }
    
      //  Execute here  mount  When the method is used , It will be executed again  query(el)  Method , One more time , It's just runtime Version considered 
    }
  • 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  Lifecycle hook 
    
    let updateComponent
    /* istanbul ignore if */
    if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
      ...
    } else {
      updateComponent = () => {
        vm._update(vm._render(), hydrating)
      }
      // updateComponent  Assignment 
        //  assignment : It's assigned here 
        //  perform : It triggered a responsive  set  Method , call watcher.update() Method 
    }
    
    new Watcher(vm, updateComponent, noop, {
      before () {
        if (vm._isMounted && !vm._isDestroyed) {
          callHook(vm, 'beforeUpdate')
        }
      }
    }, true /* isRenderWatcher  there watcher It's rendering watcher*/)
    // watcher Divided into three 
      // 1. computed watcher
      // 2. normal watcher  User defined  watcher -  namely  watch  Methods in objects 
      // 3. render watcher
    // wathcer The order of execution is fixed 
      // computed watcher -> normal watcher -> render watcher
      //  This ensures that you can get the latest... When rendering  computed  and   Yes  watche  The function defined in 
    
    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  Lifecycle hook 
    }
    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) {
        //  If it's rendering watcher - renderWatcher
        vm._watcher = this
        //  Just add... On the component instance  _watcher  attribute , Value is the right renderWatcher example 
      }
      vm._watchers.push(this)
      //  towards  _watchers  Add this to the array renderWatcher
    
      if (typeof expOrFn === 'function') {
        this.getter = expOrFn
        // expOrFn  If it's a function, assign it to  getter
          // 1.  If it is renderWatcher Words  this.getter = updateComponent Method 
            // updateComponent  Method returns  vm._update(vm._render(), hydrating)
      } else {
       ...
      }
      this.value = this.lazy
        ? undefined
        : this.get()
      // lazy Properties are only  computed Watcher  Only then 
        // 1. render watcher  Will call  get()  Method and assign it to  this.value
    }
    
    /**
     * Evaluate the getter, and re-collect dependencies.
     */
    get () {
      pushTarget(this)
      // pushTarget  Two functions 
      // 1.  towards  targetStack  Add this to the array  watcher, Here is render watcher
      // 2. Dep.target = this  Will be render watcher  Assign a value to  Dep.target, Indicates that the rendering is in progress watcher
    
      let value
      const vm = this.vm
      try {
        value = this.getter.call(vm, vm)
        //  call  getter  function 
        //  Because it's rendering watcher, So the execution is  updateComponent
        // updateComponent = vm._update(vm._render(), hydrating)
    
        //  Notice here's the point 
          //  perform  vm._update(vm._render(), hydrating)
          // 1.  Because it's executing  vm._update  In the process of method , Will get the response data Properties in , Trigger get Do dependency collection 
          // 2. vm._render()  take template Turn into  vnode
          // 2. vm._update()  After comparison, it will  vnode  Turn into real  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
    }
    }

Reference resources

detailed - The best written one you can find so far https://juejin.im/post/684490...
Three watcher https://juejin.im/post/684490...
Big Didi https://juejin.im/post/684490...

copyright notice
author[woow_ wu7],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210825093330077t.html

Random recommended