Vue2.x源码:new Vue()做了啥

例子1

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

更多详细内容,请微信搜索“前端爱好者戳我 查看

new Vue()做了啥?

在Vue.js 2.x的源码中,new Vue()的主要作用是创建一个Vue实例。当我们调用new Vue()时,会经过以下几个步骤:

  1. 初始化Vue实例的配置:Vue会将我们传入的配置对象进行初始化,包括数据、计算属性、方法等。
  2. 初始化生命周期钩子函数:Vue会在初始化过程中执行一系列的生命周期钩子函数,例如beforeCreatecreatedbeforeMountmounted等。
  3. 初始化事件系统:Vue会为实例创建一个事件总线,用于处理事件的监听和触发。
  4. 解析模板:如果配置中指定了template选项,Vue会将模板解析成渲染函数,以便后续的渲染过程中使用。
  5. 初始化渲染:Vue会创建一个虚拟DOM,并将之前解析的渲染函数进行渲染,生成真实DOM,并将其挂载到页面上。
  6. 数据响应式处理:Vue会对实例中的数据进行响应式处理,即通过劫持数据的访问和修改,实现数据的双向绑定。

在这个过程中,Vue还会做很多其他的工作,例如处理指令、组件注册、依赖收集等,以确保整个应用的正常运行和响应式更新

以上只是对new Vue()的简单概述,具体的实现细节涉及到很多源码内容,在Vue.js的源码中可以详细了解其中的实现原理。

new Vue()做了啥,源码解析

vue源码版本vue2.5.2

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

src/core/instance/index.js文件中定义了Vue

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

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

export default Vue

initMixin函数 初始化 – 初始化Vue实例的配置

  • const vm: Component = this;定义vm等于Vue;
  • vm._uid = uid++; vm存在_uid,自增
  • vm._isVue = true;只有是经过vue实例化的。vm都存在_isVue属性为true值
  • options && options._isComponent 当options存在_isComponent属性时,执行initInternalComponent(vm, options)
  • 当组件options._isComponen不存在时,执行 mergeOptions( resolveConstructorOptions(vm.constructor), options || {},vm )
  • m._renderProxy = vm;给vm定义一个_renderProxy 属性等于自身
  • vm._self = vm;给vm定义一个_self 属性等于自身
  • initLifecycle(vm);初始化生命周期相关实例属性
  • initEvents(vm);初始化事件
  • initRender(vm);定义$createElement() createElement的执行过程
  • callHook(vm, ‘beforeCreate’);执行beforeCreate生命周期钩子
  • initInjections(vm);
  • initState(vm);将data代理至Vue._data data的代理
  • initProvide(vm);初始化provide
  • callHook(vm, ‘created’);执行created生命周期钩子
  • vm. m o u n t ( v m . mount(vm. mount(vm.options.el)执行挂载

定义的 Vue.prototype._init

 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函数 – 初始化生命周期钩子函数

initLifecycle 初始化生命周期相关变量即在vue实例上挂载一些属性并设置默认值,如

  • 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
}

合并options之后

initEvents – 初始化事件系统

initEvents 初始化事件。 初始化的是父组件在模板中使用v-on或@注册的监听子组件内触发的事件。

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)
  }
}

初始化渲染 initRender

initRender函数 初始化渲染.。

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)

}

初始化inject选项

inject 选项中的每一个数据key都是由其上游父级组件提供的,所以我们应该把每一个数据key从当前组件起,不断的向上游父级组件中查找该数据key对应的值,直到找到为止。

如果在上游所有父级组件中没找到,那么就看在inject 选项是否为该数据key设置了默认值,如果设置了就使用默认值,如果没有设置,则抛出异常。

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)
  }
}

注意,在把result中的键值添加到当前实例上之前,会先调用toggleObserving(false),而这个函数内部是把shouldObserve = false,这是为了告诉defineReactive函数仅仅是把键值添加到当前实例上而不需要将其转换成响应式

resolveInject函数内部是如何把inject 选项中数据转换成键值对的。

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
  }
}

官方文档中说inject 选项可以是一个字符串数组,也可以是一个对象,在上面的代码中只看见了处理当为对象的情况,那如果是字符串数组呢?怎么没有处理呢?

其实在初始化阶段_init函数在合并属性的时候还调用了一个将inject 选项数据规范化的函数normalizeInject

参考文档:

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

猜你喜欢

转载自blog.csdn.net/BradenHan/article/details/134916771