vue源码学习:new Vue工作原理

71 阅读2分钟

一.解析src/core/index.js

import Vue from './instance/index'
//导入vue实例,包含初始化实例操作,数据响应,实例方法/事件,生命周期,数据渲染  
import { initGlobalAPI } from './global-api/index'
//2. 暴露一些全局方法,如set,nextTick,delete等方法  
import { isServerRendering } from 'core/util/env'
//检测vue服务器渲染器
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
//为ssr运行时助手安装公开FunctionalRenderContext

Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
Object.defineProperty(Vue, 'FunctionalRenderContext', {
  value: FunctionalRenderContext
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

简单总结:初始化vue,暴露属性,方法等,以及isServerRendering等主要用于被调用时获取相关信息

二.解析src/instance/index.js 主要体现

//创建构造函数并且初始化
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue) //初始化实例
stateMixin(Vue)//数据响应
eventsMixin(Vue)//实例方法/事件
lifecycleMixin(Vue)//生命周期
renderMixin(Vue)//数据渲染

export default Vue

initMixin (Vue: Class) 主要内容

  1. 函数里面有一个 Vue.prototype._init方法,vm - 实例自身
    const vm: Component = this
    // a uid
    vm._uid = uid++

内容: 组件this指向,并且对每个组件进行唯一赋值

    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

内容:合并选项,如果有options并且是个组件 优化内部组件实例化 否则走和合并俩个对象

    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, 'created')

内容:暴露自身,并且初始化生命周期,初始化事件,初始化渲染,并且执行beforeCreate,和created,在两者之间渲染state,依赖注入

    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

内容:如果发Vue实例使用的根 DOM 元素,则进行挂载
举例说明

var MyComponent = Vue.extend({  
    template: '<div>Hello!</div>'  
})  
  
// 创建并挂载到 #app (会替换 #app)  
new MyComponent().$mount('#app')  
  
// 同上  
new MyComponent({ el: '#app' })  
  
// 或者,在文档之外渲染并且随后挂载  
var component = new MyComponent().$mount()  
document.getElementById('app').appendChild(component.$el)

单元总结

  1. 刚进来先进行init方法,给组件复制uid

  2. 合并options对象

  3. 进行初始化渲染:实列数据/方法、事件,渲染函数的执行,执行beforeCreate 和 created期间,在两者之间渲染state,依赖注入

  4. 最后挂载到Dom节点

stateMixin (Vue: Class)

  Object.defineProperty(Vue.prototype, '$data', dataDef)
  Object.defineProperty(Vue.prototype, '$props', propsDef)

  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      const info = `callback for immediate watcher "${watcher.expression}"`
      pushTarget()
      invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
      popTarget()
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }

单元总结

Vue 实例代理了对其 data、props 对象 property 的访问,添加setset,delete,$watch实例方法

eventsMixin (Vue: Class)

Vue.prototype.$on  = function (event: string | Array<string>, fn: Function): Component
Vue.prototype.$once = function (event: string, fn: Function): Component
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component
Vue.prototype.$emit = function (event: string): Component

单元总结:创建onon,once,offoff,emit实例事件方法,这里就不细讲了

lifecycleMixin (Vue: Class

 Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
    const vm: Component = this
    const prevEl = vm.$el
    const prevVnode = vm._vnode
    const restoreActiveInstance = setActiveInstance(vm)
    vm._vnode = vnode
    // Vue.prototype.__patch__ is injected in entry points
    // based on the rendering backend used.
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    restoreActiveInstance()
    // update __vue__ reference
    if (prevEl) {
      prevEl.__vue__ = null
    }
    if (vm.$el) {
      vm.$el.__vue__ = vm
    }
    // if parent is an HOC, update its $el as well
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el
    }
    // updated hook is called by the scheduler to ensure that children are
    // updated in a parent's updated hook.
  }

内容:_update首次渲染以及更新时都会被调用,核心是vm.path,全局需要你会看到Vue.prototype.__patch__ = inBrowser ? patch : noop 如果是在浏览器环境就会调用内置模块createPatchFunction

单元总结:创建forceUpdate,forceUpdate,destroy生命周期,以及更新时触发_update方法

renderMixin (Vue: Class)

installRenderHelpers(Vue.prototype)//安装时的工具

Vue.prototype.$nextTick = function (fn: Function) {
    return nextTick(fn, this)
  }
Vue.prototype._render =function (): VNode {

单元总结:渲染时,先调用辅助工具,创建$nextTick,返回虚拟节点

三.解析'src/global-api/index'

initGlobalAPI(Vue) 内容:导入定义好的Vue,已经完成——init方法

  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

内容:Vue.config 定义的全局配置。可以在启动应用之前修改下列 property
如图

image.png

Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

内容:暴露一些方法,不是公共API的一部分,基本用不到

     Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  Vue.options = Object.create(null)
    
   Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

内容:Vue.observable实现响应对象,ASSET_TYPES实际指向component,directive,filter

如图

image.png

总结:

实现Vue配置,以及相关的配置属性

其他文件`

import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})

Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

内容:主要用于被调用时获取相关信息