Vue 的不同构建版本
完整版:同时包含编译器和运行时的版本。
编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码,体积大、效率低。
运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码,体积小(小大约 30%)、效率高。基本上就是除去编译器的代码。
UMD:UMD 版本通用的模块版本,支持多种模块方式。 vue.js 默认文件就是运行时 + 编译器的UMD 版本。
CommonJS(cjs):CommonJS 版本用来配合老的打包工具比如 Browserify 或 webpack 1。
ES Module:从 2.6 开始 Vue 会提供两个 ES Modules (ESM) 构建文件,为现代打包工具提供的版本。
- ESM 格式被设计为可以被静态分析,所以打包工具可以利用这一点来进行“tree-shaking”并将用不到的代码排除出最终的包。
- ES6 模块与 CommonJS 模块的差异
基于Vue 2.6版本
入口
- el 不能是 body 或者 html 标签
- 如果没有 render,把 template 转换成 render 函数
- 如果有 render 方法,直接调用 mount 挂载 DOM
// 1. el 不能是 body 或者 html
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn( `Do not mount Vue to
<html> or <body> - mount to normal elements instead.` )
return this
}
const options = this.$options if (!options.render) {
// 2. 把 template/el 转换成 render 函数 ……
}
// 3. 调用 mount 方法,挂载 DOM
return mount.call(this, el, hydrating)
初始化流程
- src/platform/web/entry-runtime-with-compiler.js 中引用了 './runtime/index'
- src/platform/web/runtime/index.js
- 设置 Vue.config
- 设置平台相关的指令和组件
指令 v-model、v-show
组件 transition、transition-group - 设置平台相关的 patch 方法(打补丁方法,对比新旧的 VNode)
- 设置 $mount 方法,挂载 DOM
// install platform runtime directives & components extend(Vue.options.directives, platformDirectives) extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean ): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
- src/platform/web/runtime/index.js 中引用了 'core/index'
- src/core/index.js
- 定义了 Vue 的静态方法
- initGlobalAPI(Vue)
- src/core/index.js 中引用了 './instance/index'
- src/core/instance/index.js
- 定义了 Vue 的构造函数
// 此处不用 class 的原因是因为方便,后续给 Vue 实例混入实例成员
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')
}
// 调用 _init() 方法
this._init(options)
}
// 注册 vm 的 _init() 方法,初始化
vm initMixin(Vue)
// 注册 vm 的 $data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化事件相关方法
// $on/$once/$off/$emit
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
// _update/$forceUpdate/$destroy
lifecycleMixin(Vue)
// 混入 render
// $nextTick/_render
renderMixin(Vue)
导出Vue涉及的四个模块
- src/platforms/web/entry-runtime-with-compiler.js
- web 平台相关的入口
- 重写了平台相关的 $mount() 方法
- 注册了 Vue.compile() 方法,传递一个 HTML 字符串返回 render 函数
- src/platforms/web/runtime/index.js
- web 平台相关
- 注册和平台相关的全局指令:v-model、v-show
- 注册和平台相关的全局组件: v-transition、v-transition-group
- 全局方法:
patch:把虚拟 DOM 转换成真实 DOM
$mount:挂载方法
- src/core/index.js
- 与平台无关
- 设置了 Vue 的静态方法,initGlobalAPI(Vue)
- src/core/instance/index.js
- 与平台无关
- 定义了构造函数,调用了 this._init(options) 方法
- 给 Vue 中混入了常用的实例成员
首次渲染过程
- 实例化Vue对象的时候。会调用this._init()进行一些初始化, 同时会调用
vm.$mount
函数。
这里的
vm.$mount
函数 首先调用src/platforms/web/entry-runtime-with-compiler.js 里面重写的$mount()
方法。
- 重写的
vm.$mount
函数: 通过Vue.compile() 方法,返回 render 和 staticRenderFns 函数, 存储在$options
里面。 然后调用原始的vm.$mount
函数。
- 这里的render:如果用户定义,返回用户定义的; 否则返回模板生成的。
- 原始的
vm.$mount
函数 是src/platforms/web/runtime/index.js 里面定义的- src/platforms/web/runtime/index.js里面同时定义了vm.patch方法把虚拟Dom转成真实Dom
- 原始的
vm.$mount
函数:调用 mountComponent方法。
mountComponent定义在/src/core/instance/lifecycle.js
- mountComponent方法主要做了4件事:
- 触发beforeMount钩子
- 定义updateComponent方法, 注意这里只是定义, 触发是Watcher触发的。
- 创建Watcher实例, 把updateComponent方法作为参数传入。
- 触发mounted钩子
4.1 updateComponent方法比较重要, 主要做了2件事:
vm._rander渲染虚拟Dom。
vm._rander其实就是调用第二步$options中的render方法
vm._update更新,把虚拟Dom转成真实Dom 。
vm._update其实就是调用src/platforms/web/runtime/index.js中的定义的vm.__patch__方法, 同时记录vm.$el
4.2 Watcher构造方法
会把传入的updateComponent参数赋值给this.getter。
this.get()方法里面调用this.getter。
立即调用this.get(), 进而触发渲染。