Vue源码分析-全局脚本初始化

200 阅读1分钟

拉取 vuejs v2.6.2 版本,安装好依赖之后,查看 package.json 文件 script 属性下的 dev 命令:

"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev"

这个脚本就是构建 vuejs 全代码(模版编译器 + runtime)的开发版本,打开 scripts/config.js 文件,搜索 web-full-dev,可看到:

// Runtime+compiler development build (Browser)
'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
}

入口文件在 web/entry-runtime-with-compiler.js,这个文件主要是扩展了 Vue.prototype.$mount 方法,将 template 或者 el 编译为 render 函数,以下为核心代码:

import Vue from './runtime/index'

// 扩展$mount方法
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  const options = this.$options
  // 将template或者el属性转化为reder function
  if (!options.render) {
    let template = options.template
    if (template) {
      // 一些特殊处理
    } else if (el) {
      // el参数转为template
      template = getOuterHTML(el)
    }
    // 如果有template就转为render函数挂在options上
    if (template) {
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
    }
  }
  return mount.call(this, el, hydrating)
}

继续看,上述文件里的 Vue 是引用自./runtime/index 文件,查看该文件,核心代码如下:

import Vue from 'core/index'
import { mountComponent } from 'core/instance/lifecycle'

import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'

// 加入内置v-model,v-show指令
extend(Vue.options.directives, platformDirectives)
// 加入内置的keep-alive,transition[-group]组件
extend(Vue.options.components, platformComponents)

// vnode对比方法,边对比边替换dom
Vue.prototype.__patch__ = inBrowser ? patch : noop

// 定义$mount方法
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

export default Vue

上述文件里的 Vue 是引用自 core/index 文件,查看该文件,核心代码:

// 此处就是Vue的构造函数定义的文件
import Vue from './instance/index'

import { initGlobalAPI } from './global-api/index'
// 从名字可以看出,该方法是添加Vue的全局api(文档中全局api中的所有api都在这里定义)
// Vue.extend/nextTick/set/delete/directive/filter/component/use/mixin/compile/observable/version
initGlobalAPI(Vue)

export default Vue

看下 Vue 构造函数的文件./instance/index:

import { initMixin } from "./init";
import { stateMixin } from "./state";
import { renderMixin } from "./render";
import { eventsMixin } from "./events";
import { lifecycleMixin } from "./lifecycle";

function Vue(options) {
  this._init(options);
}
// 定义Vue.prototype._init方法
initMixin(Vue);
// 初始化Vue实例的一些属性或方法:vm.$props/$data/$set/$delete/$watch
stateMixin(Vue);
// 初始化Vue实例的事件方法:vm.$on/$emit/$off/$once
eventsMixin(Vue);
// 初始化Vue实例的生命周期相关的方法:vm._update/$forceUpdate/$destory
lifecycleMixin(Vue);
// 初始化Vue实例的渲染相关的方法:vm._render/$nextTick
renderMixin(Vue);

export default Vue;

至此整个 Vue 代码初始执行过程已经结束了,总结一下主要就是给 Vue 及 Vue.prototype 添加了很多属性和方法。