大家好,我是鼠目。「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」
通过本章,你可以学到一点点esm和CommonJs的相关知识,以及vue初始化相关的知识。
本章来给大家简单介绍,vue框架中应该掌握的原理---vue的初始化做了哪些操作,同时这也是面试中常会问到的问题之一。
vue的初始化流程
new Vue({
el:"#app",
....
})
new Vue究竟做了些什么? 相信js本身有所了解就可以看出来,这其实是一个new构造函数的行为。那它在new的过程中究竟做了些什么呢?
先看源码src/core/index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
// 1.初始化全局静态API:Vue.set/delete/component/use/....
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get () {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = '__VERSION__'
export default Vue
我们先对源码进行分析,然后判断它的执行顺序。
首先它是import导入的,es6中的import是编译时加载或者叫静态加载。在import的时候,并没有执行对应的代码块,而是做了一个指针指向。而CommonJs不同,它是会直接执行对应的代码块,得到一份代码的拷贝导出。
所以,当我们new Vue的时候,它会根据指针指向。执行core/index.js中的代码。
- 所有import是指针指向并未执行。
- 遇到initGlobalAPI(Vue)开始执行,然后去寻找initGlobalApi函数对应的文件和Vue函数对应的文件进行执行。
具体行为
- 遇到initGlobalAPI(Vue),开始执行全局Api的初始化
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
initUse(Vue) // vue.use 用于插件挂载
initMixin(Vue) // vue.mixin 用于mixin混入
initExtend(Vue) // vue.extend
initAssetRegisters(Vue) // 注册实现vue.component/directive/filter
- Vue构造函数,定义实例的Api 位置:/instance/index
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
function Vue(options) {
// 构造函数本身只是执行了_init函数
this._init(options);
}
initMixin(Vue) //实现init函数
stateMixin(Vue) //状态相关api $data $props, $set, $delete, $watch
eventsMixin(Vue) //事件相关api $on, $once, $off, $emit
lifecycleMixin(Vue) //生命周期相关api, $forceUpdate, $destroy _update,
renderMixin(Vue) //渲染相关api render, $nextTick
- 而在initMixin(Vue)中的init,进行了vue的数据初始化以及挂载的操作。
export function initMixin(Vue) {
//实现了_init函数
Vue.prototype._init = function(options) {
// 保存上下文关系
const vm = this;
// merge options
// 1.选项合并:用户选项和系统默认选项需要合并
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
//省略其他
//执行初始化
}
initLifecycle(vm) // 生命周期相关的属性初始化$parent,$root,$children,$refs等
initEvents(vm) // 自定义组件事件监听,处理父组件传递的事件和回调
initRender(vm) // 插槽处理,$slots,$scopedSlots,_c,$createElement === render(h)
// 调用生命周期的钩子函数,beforeCreate
callHook(vm, 'beforeCreate')
// 组件数据和状态初始化
initInjections(vm) // resolve injections before data/props inject
initState(vm) // 按照顺序初始化props, methods,data,computed,watcher
initProvide(vm) // resolve provide after data/props provide
callHook(vm, 'created')
// 省略
// 如果存在el,将会自动挂载
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
- 第一步合并选项,将传入的options跟默认的参数进行合并
- 初始化数据和生命周期
- 挂载。
总结一下,初始化过程其实就是创建了一个Vue构造函数的实例对象,实例对象上初始化了很多api和数据。然后将该实例对象进行了挂载。
除了了解vue初始化做了哪些工作,其实我们还应该在vue的源码中学到一些编程的技巧
- 将构造函数作为参数传入,在函数内部处理时,挂载原型链方法。
- 对一些函数的功能尽量详细地拆分,做到函数功能的单一,方便后期维护。