vue源码初始化流程

291 阅读2分钟

源码调试采坑记录:vue源码下载下来,出现如下错误。

Error: Could not load C:\vue\src\core/config (imported by C:\vue\src\platforms\web\entry-runtime-with-compiler.js): ENOENT: no such file
or directory, open 'C:\vue\src\core\config'

vue使用rollup打包,rollup路径在windows下会出错,解决办法:
rollup-plugin-alias插件中路径'/'应该改为'',将node_modules/rollup-plugin-alias/dist/rollup-plugin-alias.js 第70行: const entry = options[toReplace]; 改为 const entry = normalizeId(options[toReplace]);

打开package.json

当我们运行npm run dev,看看干了啥,rollup也是类似webpack的打包工具,根据

TARGET=web-full-dev

去scripts\config.js找"web-full-dev”

'web/entry-runtime-with-compiler.js'就是入口文件

入口文件:src\platforms\web\entry-runtime-with-compiler.js

// 扩展$mount
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 el = el && query(el)
}

// 获取选项
const options = this.$options
// 若不存在render选项则将template/el的设置转换为render函数
if (!options.render) {
   let template = options.template
   if (template) {
     // 解析template选项
   } else if (el) {
     // 否则解析el选项
     template = getOuterHTML(el)
   }
}
if (template) {
 // 编译得到render函数
 const { render, staticRenderFns } = compileToFunctions(template, {..}, this)
 options.render = render
}

跟着import进到src\platforms\web\runtime\index.js

// 定义组件实例补丁方法
Vue.prototype.__patch__ = inBrowser ? patch : noop
//实现$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  // $mount挂载时执行mountComponent(挂载组件)
  return mountComponent(this, el, hydrating)
}
  • mountComponent执行首次渲染、更新 (src\core\instance\lifecycle.js)
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
//获取根元素
  vm.$el = el
//调用beforeMount钩子函数
  callHook(vm, 'beforeMount')
//创建组件更新函数
  const updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
//创建组件实例对应的watcher
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)

//$vode不存在
  if (vm.$vnode == null) {
    vm._isMounted = true
    //调用mounted钩子函数
    callHook(vm, 'mounted')
  }
  return vm
}

接着进到src\core\index.js 这里定义了全局API

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
// 初始化全局api:Vue.set/delete/nextTick等
initGlobalAPI(Vue)
export default Vue
  • initGlobalAPI(Vue)全局API实现(src\core\global-api\index.js)
export function initGlobalAPI (Vue: GlobalAPI) {
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  initUse(Vue) // 实现Vue.use函数
  initMixin(Vue) // 实现Vue.mixin函数
  initExtend(Vue) // 实现Vue.extend函数
  initAssetRegisters(Vue) // 注册实现Vue.component/directive/filter
}

最后找到Vue构造函数 src\core\instance\index.js

function Vue (options) {
  // 构造函数仅执行了_init
  this._init(options)
}
// 实现init函数
initMixin(Vue)
stateMixin(Vue) 
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
  • initMixin(Vue) 实现_init src\core\instance\init.js
export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this

    initLifecycle(vm) // 设置$parent$root$children等组件关系属性
    initEvents(vm)    // 监听附加在组件上的事件
    initRender(vm)    // 初始化组件插槽、声明createElement方法    
    callHook(vm, 'beforeCreate') // 调用beforeCreate钩子
    initInjections(vm)// 初始化注入数据
    initState(vm)     // 初始化组件data,methods,computed,watch
    initProvide(vm)   // 为后代提供数据
    callHook(vm, 'created')      // 调用created钩子

    // 如果new的时候有el,就直接调用$mount
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}
  • stateMixin(Vue) 定义了data,props,set,delete,$watch
    src\core\instance\state.js
export function stateMixin (Vue: Class<Component>) {
  // 定义只读属性$data$props
  const dataDef = {}
  dataDef.get = function () { return this._data }
  const propsDef = {}
  propsDef.get = function () { return this._props }

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

  // 定义$set$delete
  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  // 定义$watch
  Vue.prototype.$watch = function (): Function {...}
}
  • eventsMixin(Vue)定义了on,emit,off,once
    src\core\instance\events.js
export function eventsMixin (Vue: Class<Component>) {
  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 {}
}
  • lifecycleMixin(Vue)定义了_update,forceUpdate,destroy
    src\core\instance\lifecycle.js
export function lifecycleMixin (Vue: Class<Component>) {
    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
    Vue.prototype.$forceUpdate = function () {}
    Vue.prototype.$destroy = function () {}
}
  • renderMixin(Vue)定义两个实例方法$nextTick和_render
    src\core\instance\render.js
export function renderMixin (Vue: Class<Component>) {
  Vue.prototype.$nextTick = function (fn: Function) {
    return nextTick(fn, this)
  }
  Vue.prototype._render = function (): VNode {}
}