Vue2.x source code: What does new Vue() do?

Example 1

<div id="app">
  <div class="home">
    <h1>{
    
    {
    
    title}}</h1>
  </div>
</div>
new Vue({
    
    
  el: '#app',
  data: () => {
    
    
    return {
    
    
      title: 'Home Page'
    }
  }
})

For more details, please search " " on WeChat前端爱好者 and click me to view .

What does new Vue() do?

In the source code of Vue.js 2.x, new Vue()its main function is to create a Vue instance. When we call new Vue(), we will go through the following steps:

  1. Initialize the configuration of the Vue instance : Vue will initialize the configuration object we passed in, including data, calculated properties, methods, etc.
  2. Initialization life cycle hook function : Vue will execute a series of life cycle hook functions during the initialization process, such as beforeCreate, created, beforeMount, mountedetc.
  3. Initialize the event system : Vue will create an event bus for the instance to handle event monitoring and triggering.
  4. Parse the template : If the option is specified in the configuration template, Vue will parse the template into a rendering function for use in subsequent rendering processes.
  5. Initial rendering : Vue will create a virtual DOM and render the previously parsed rendering function to generate a real DOM and mount it on the page.
  6. Data responsive processing : Vue will perform responsive processing on the data in the instance, that is, by hijacking the access and modification of data to achieve two-way binding of data.

During this process, Vue will also do a lot of other work, such as processing instructions, component registration, dependency collection, etc., to ensure the normal operation and responsive update of the entire application .

The above is just new Vue()a brief overview. The specific implementation details involve a lot of source code content. You can learn more about the implementation principles in the source code of Vue.js.

What does new Vue() do, source code analysis

vue source code version vue2.5.2

new Vue()会执行_init方法,而_init方法在initMixin函数中定义。

Vue is defined in the src/core/instance/index.js file

function Vue (options) {
    
    
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

initMixin function initialization – initializes the configuration of the Vue instance

  • const vm: Component = this; define vm equal to Vue;
  • vm._uid = uid++; vm existence_uid, self-increase
  • vm._isVue = true; only those instantiated by vue. The _isVue attribute exists in vm and has a true value
  • options && options._isComponent When the _isComponent attribute exists in options, execute initInternalComponent(vm, options)
  • When component options._isComponen does not exist, execute mergeOptions( resolveConstructorOptions(vm.constructor), options || {},vm )
  • m._renderProxy = vm; define a _renderProxy attribute for vm equal to itself
  • vm._self = vm; define a _self attribute for vm equal to itself
  • initLifecycle(vm); initialize life cycle related instance attributes
  • initEvents(vm); initialization event
  • initRender(vm); define the execution process of $createElement() createElement
  • callHook(vm, 'beforeCreate'); execute the beforeCreate life cycle hook
  • initInjections(vm);
  • initState(vm); proxy data to Vue._data data proxy
  • initProvide(vm); initialize provide
  • callHook(vm, 'created'); execute created life cycle hook
  • vm. m o u n t ( v m . mount(vm. m o u n t ( v m . options.el) executes the mount

Vue.prototype._init defined

 export function initMixin (Vue: Class<Component>) {
    
    
  Vue.prototype._init = function (options?: Object) {
    
    
    const vm: Component = this
    // a uid,自增ID
    vm._uid = uid++

    vm._isVue = true
    // merge options 合并options
    if (options && options._isComponent) {
    
    
     
      initInternalComponent(vm, options)
    } else {
    
    
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {
    
    },
        vm
      )
    }

      vm._renderProxy = vm
   
    // expose real self
    vm._self = vm
    // 初始化生命周期相关实例属性
    initLifecycle(vm)
    
    //初始化事件
    initEvents(vm)
    
    //定义$createElement() createElement的执行过程
    initRender(vm)

    // 执行beforeCreate生命周期钩子
    callHook(vm, 'beforeCreate')

    initInjections(vm) // resolve injections before data/props
    
    //将data代理至Vue._data data的代理
    initState(vm)

    // 初始化provide
    initProvide(vm) // resolve provide after data/props

    // 执行created生命周期钩子
    callHook(vm, 'created')

      // 当options中存在el属性,则执行挂载
    if (vm.$options.el) {
    
    
      //挂载#app $mount执行过程
      vm.$mount(vm.$options.el)
    }
  }
}

initLifecycle function – initializes the life cycle hook function

initLifecycle initializes life cycle related variables, that is, mounts some properties on the vue instance and sets default values, such as

  • p a r e n t , parent, parent,root,
  • $children,
  • $ref,vm._watcher,
  • vm.__inactive,
  • vm._directInactive,
  • vm._isMounted,
  • vm._isDestroyed,
  • vm._isBeingDestroyed
export function initLifecycle (vm: Component) {
    
    
  const options = vm.$options

  // locate first non-abstract parent
  let parent = options.parent
  //当前组件存在父级并且当前组件不是抽象组件
  if (parent && !options.abstract) {
    
    
  //通过while循环来向上循环,如果当前组件的父级是抽象组件并且也存在父级,那就继续向上查找当前组件父级的父级
    while (parent.$options.abstract && parent.$parent) {
    
    
    //更新parent
      parent = parent.$parent
    }
    //把该实例自身添加进找到的父级的$children属性中
    parent.$children.push(vm)
  }
  //直到找到第一个不是抽象类型的父级时,将其赋值vm.$parent
  vm.$parent = parent
  //设置实例根元素。判断如果当前实例存在父级,那么当前实例的根实例$root属性就是其父级的根实例$root属性,如果不存在,那么根实例$root属性就是它自己
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {
    
    }

  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  
  //实例是否已挂载
  vm._isMounted = false
  
  //实例是否已销毁
  vm._isDestroyed = false
  
  //实例是否正准备销毁
  vm._isBeingDestroyed = false
}

After merging options

initEvents – Initialize the event system

initEvents initialization events. What is initialized is the parent component registered using v-on or @ in the template to listen for events triggered in the child component.

export function initEvents (vm: Component) {
    
    
//在vm上新增_events属性并将其赋值为空对象,用来存储事件。
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  //获取父组件注册的事件赋给listeners,
  const listeners = vm.$options._parentListeners
  //如果listeners不为空,则调用updateComponentListeners函数,将父组件向子组件注册的事件注册到子组件的实例中
  if (listeners) {
    
    
    updateComponentListeners(vm, listeners)
  }
}

Initialize rendering initRender

The initRender function initializes rendering.

export function initRender (vm: Component) {
    
    
  vm._vnode = null // the root of the child tree
  vm._staticTrees = null // v-once cached trees
  const options = vm.$options
  const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
  const renderContext = parentVnode && parentVnode.context
  //实例上插槽
  vm.$slots = resolveSlots(options._renderChildren, renderContext)
  vm.$scopedSlots = emptyObject
  //在实例上定义_c函数和$_createElement函数
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
  
  const parentData = parentVnode && parentVnode.data

  defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
  defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)

}

Initialize inject options

Each data key in the inject option is provided by its upstream parent component, so we should start from the current component and continue to search for the value corresponding to the data key in the upstream parent component until we find it. until.

If it is not found in all upstream parent components, then it depends on whether the inject option has a default value set for the data key. If it is set, the default value will be used. If it is not set, an exception will be thrown.

export function initInjections (vm: Component) {
    
    
//调用resolveInject把inject选项中的数据转化成键值对的形式赋给result
  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    
    
    toggleObserving(false)
    //遍历result中的每一对键值
    Object.keys(result).forEach(key => {
    
    
        //调用defineReactive函数将其添加当前实例上
        defineReactive(vm, key, result[key])
    })
    toggleObserving(true)
  }
}

Note that before adding the key value in the result to the current instance, toggleObserving(false) will be called first, and inside this function, shouldObserve = false is used to tell the defineReactive function to only add the key value to the current instance. without converting it to responsive

How the resolveInject function converts the data in the inject option into key-value pairs.

export function resolveInject (inject: any, vm: Component): ?Object {
    
    
  if (inject) {
    
    
    // inject is :any because flow is not smart enough to figure out cached
    //创建一个空对象result,用来存储inject 选项中的数据key及其对应的值,作为最后的返回结果。
    const result = Object.create(null)
    const keys = hasSymbol
      ? Reflect.ownKeys(inject).filter(key => {
    
    
        /* istanbul ignore next */
        return Object.getOwnPropertyDescriptor(inject, key).enumerable
      })
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
    
    
      const key = keys[i]
      //provideKey就是上游父级组件提供的源属性
      const provideKey = inject[key].from
      let source = vm
      //while循环,从当前组件起,不断的向上游父级组件的_provided属性中(父级组件使用provide选项注入数据时会将注入的数据存入自己的实例的_provided属性中)查找,直到查找到源属性的对应的值,将其存入result中
      while (source) {
    
    
        if (source._provided && hasOwn(source._provided, provideKey)) {
    
    
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      if (!source) {
    
    
      //是否有default属性,如果有的话,则拿到这个默认值,官方文档示例中说了,默认值可以为一个工厂函数,所以当默认值是函数的时候,就去该函数的返回值,否则就取默认值本身。如果没有设置默认值,则抛出异常。
        if ('default' in inject[key]) {
    
    
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
    
    
          warn(`Injection "${
      
      key}" not found`, vm)
        }
      }
    }
    return result
  }
}

The official documentation says that the inject option can be a string array or an object. In the above code, we only see the case where it is treated as an object. What if it is a string array? Why wasn't it dealt with?

In fact, during the initialization phase, the _init function also calls a function normalizeInject that normalizes the inject option data when merging attributes.

Reference documentation:

https://blog.csdn.net/weixin_40119412/article/details/128880336

Supongo que te gusta

Origin blog.csdn.net/BradenHan/article/details/134916771
Recomendado
Clasificación