我们都知道页面渲染从new Vue开始,但是实际上代码在这之前先注册了Vue构造函数和各种能力才能保证new Vue的正常运作。从开发者角度看,可以看到Vue的大致全貌,其暴露了哪些东西。
注意:下面代码分析路径是按照构建入口按照引用关系倒着分析的,实际脚本的执顺序和下面分析顺序是相反的
代码语言:javascript复制// src/platforms/web/entry-runtime-with-compiler.js: 构建入口(runtime compile version)
import Vue from './runtime/index'
// src/platforms/web/runtime/index.js
import Vue from 'core/index'
// src/core/index.js
import Vue from './instance/index' // Vue类函数的定义
js文件加载过程
构建入口
从构建入口开始(runtime compile version)
- 通常我们使用vue-loader webpack,在.vue文件中编写代码,这种情况构建vue文件时会将template直接转成 render 函数
- 下面构建版本是带有模板编译能力的运行时,可以在运行时进行模板编译 ```js // src/platforms/web/entry-runtime-with-compiler.js
import Vue from './runtime/index'
const mount = Vue.prototype.$mount Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean ): Component { // 如果开发者没有提供 render,则将从 el -> render 函数 if (!options.render) { //... 核心逻辑,通过el获取 template(innerHTMLouterHTML),在调用模板编译方法 compileToFunctions 将 template 转为 render 函数 } return mount.call(this, el, hydrating) // hydrating 和 ssr 相关,后面不介绍该场景。 }
Vue.compile = compileToFunctions // Vue.compile,用户可以手动调用该方法在运行时进行模板构建 export default Vue
代码语言:javascript复制该文件主要通过拦截 Vue.prototype.$mount 方法添加运行时编译模板能力。
## 运行时构建入口
src/platforms/web/runtime/index.js
```js
// src/platforms/web/runtime/index.js
import Vue from 'core/index'
import { mountComponent } from 'core/instance/lifecycle'
import { patch } from './patch' //
Vue.config.xxx = xxx // 都有哪些作用呢❓
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives) // 内置命令:model、show
extend(Vue.options.components, platformComponents) // 内置组件:Transition、TransitionGroup
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop // 从vnode到dom的核心方法
// public mount method
Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean): Component {
el = el && inBrowser ? query(el) : undefined // 转成dom
return mountComponent(this, el, hydrating) // 组件渲染的入口方法
}
export default Vue
src/core/vdom/patch.js
代码语言:javascript复制// src/core/vdom/patch.js
import { createPatchFunction } from 'core/vdom/patch'
export const patch: Function = createPatchFunction({ nodeOps, modules })
core
src/core/index.js
看到在 src/core下,说明是平台复用的,并且也是vue运行时的核心入口。静态方法(全局方法)和实例方法分开定义,静态方法放在initGlobalApi中定义,Vue类函数放在 core/instance/index中定义
代码语言:javascript复制// src/core/index.js
// 定义Vue构造函数何其实例方法(定义在原型上)
import Vue from './instance/index'
initGlobalAPI(Vue) // 静态方法和选项的定义
export default Vue
- Vue构造函数何其实例方法(定义在原型上)
- 静态方法和选项的定义
Vue类和实例方法定义
Vue构造函数定义
代码语言:javascript复制// src/core/instance/index.js
function Vue (options) {
this._init(options)
}
initMixin(Vue) // Vue.prototype._init
stateMixin(Vue) // Vue.prototype.$data | $props | $set | $get | $watch
eventsMixin(Vue) // Vue.prototype.$on | $once | $off | $emit
lifecycleMixin(Vue) // Vue.prototype._update | $forceUpdate | $destroy
// Vue.prototype.$nextTick | _render,
// 另外这里提供了 render 函数中可能会用到的一些工具函数等
// (见 installRenderHelpers 方法,如_v = createTextVNode、_n = toNmber等等 )
renderMixin(Vue)
export default Vue
实例方法定义
Vue.prototype
- 初始化入口:
_init
- 状态相关:data | props | set | get |
- 事件相关:on | once | off | emit
- 生命周期相关: _update | forceUpdate | destroy
- 渲染相关:
$nextTick
|_render
其中 下划线开头的方法是实例私有方法(_init
、_update
、_render
),不允许外部调用,这三个私有方法也是组件实例化过程的三个核心步骤
静态方法(Vue.xxx)、选项(Vue.options.xxx)
initGlobalApi()
代码语言:javascript复制// src/core/global-api/index.js
import { observe } from 'core/observer/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
configDef.get = () => config
Object.defineProperty(Vue, 'config', configDef) // 1. Vue.config
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
// 2. Vue.util,其中 extend(to, from): 合并from对象的键值到to对象上
Vue.util = { warn, extend, mergeOptions, defineReactive }
// 3. Vue.set/get./nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 4. Vue.observable
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
// 5. Vue.options
Vue.options = Object.create(null) // 【关键】:创建 Vue.options
ASSET_TYPES.forEach(type => { // ASSET_TYPES: 'component', 'directive', 'filter' => Vue.options.components/.directives/.filters
Vue.options[type 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue // 【关键】
extend(Vue.options.components, builtInComponents) // 添加内置组件:Keep-Alive 到 Vue.options.components
initUse(Vue) // 6. Vue.use
initMixin(Vue) // 7. 【关键】:Vue.mixin:通过mergeOptions将mixin和 Vue.options 进行策略合并
initExtend(Vue) // 8. 【关键】: Vue.extend: 原型继承Vue类,返回一个构造函数Sub,核心点是合并子组件的options的处理 => 相当于每个组件都会生成一个构造函数,并且有一个cid标识
initAssetRegisters(Vue) // 9. 【关键】*:全局组件、全局指令、全局过滤器的注册:Vue.component、Vue.filter、Vue.directive,全局注册的对象如组件会被保存到 Vue.options.component**s**/filters/directives 中
}
这里的核心有两类
- 全局API
- Vue.config
- Vue.set、Vue.get、Vue.nextTick
- Vue.use
- Vue.mixin:策略合并 Vue.options 和 mixin 中的选项
- Vue.extend:开发者提供选项,通过原型继承来生成组件的构造函数,
- Vue.component、Vue.componentfilter、Vue.componentdirective:如注册的全局组件会被保存到 Vue.options.components中
- 全局选项:Vue.options
- Vue.options.components、Vue.options.filters、Vue.options.directives
- Vue.options._base = Vue
小结
- 定义了 Vue 类函数 挂载类实例方法
- 挂载类全局Api和全局Options,API - Vue.js
另外,看到有部分内容放在platforms下提供,如Vue.prototype.$mount,这是因为和平台和构建版本有关