1. vue 目录结构 (src)
|-- dist 打包后的vue版本 |-- flow 类型检测,3.0换了typeScript // 使用flow定义和检测类型,增强代码健壮性 |-- script 构建不同版本vue的相关配置 |-- src 源码 |-- compiler 编译器 // 将template编译成render函数 |-- core 不区分平台的核心代码 |-- components 通用的抽象组件 |-- global-api 全局API |-- instance 实例的构造函数和原型方法 |-- observer 数据响应式 // vue检测数据变化并更新视图的实现 |-- util 常用的工具方法 |-- vdom 虚拟dom相关 // 将render函数转为vnode从而patch为真实dom以及diff算法的代码实现 |-- platforms 不同平台不同实现 |-- server 服务端渲染 |-- sfc .vue单文件组件解析 |-- shared 全局通用工具方法 |-- test 测试
和运行时版本的关键所在,完整版就自带这个编译器,而运行时版本就没有,
(面试题) 请问 runtime compiler 和 runtime-only 这两个版本的区别?
文件大小的区别,带编译的版本要比不带的版本文件要大6kb左右
编译时机不同:编译运行时版本会在运行时进行编译,性能会有一定损耗;运行时版本借助于 vue-loader 做的离线编译,运行速度更快。
2. vue入口 首先是构造函数 core/instance/index.js
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') } this._init(options) } initMixin(Vue):定义_init方法。 stateMixin(Vue):定义数据相关的方法$set,$delete,$watch方法。 eventsMixin(Vue):定义事件相关的方法$on,$once,$off,$emit。 lifecycleMixin(Vue):定义_update,及生命周期相关的$forceUpdate和$destroy。 renderMixin(Vue):定义$nextTick,_render将render函数转为vnode。
这些初始化后还会定义一些全局的API,initGlobalAPI(),里面包括
initUse(Vue) // Vue.use() initMixin(Vue) // Vue.mixins() initExtend(Vue) // Vue.extends() initAssetRegisters(Vue) // Vue.component,Vue.directive,Vue.filter方法
this._init(options) 是 core/instance/init.js中的挂载到vue的proptype原型链上的。
此处的options是传进来的Vue实例,eg:
new Vue( { el: '#app', data: { message: 'hello world' }, components: {} } ) Vue.prototype._init = function (options?: Object) { // 如果options里没有传components就开始解析构造器,传了就初始化这个组件 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) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created')
所以在beforeCreate生命周期前vue 初始化了生命周期、事件处理机制
在created之前vue 初始化事件通信、options相关的
vue/src/core/instance/index.js stateMixin(Vue)
state.js
function initData (vm: Component) { let data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} if (!isPlainObject(data)) { data = {} process.env.NODE_ENV !== 'production' && warn( 'data functions should return an object:\n' + 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm ) } // proxy data on instance const keys = Object.keys(data) const props = vm.$options.props const methods = vm.$options.methods let i = keys.length while (i--) { const key = keys[i] if (process.env.NODE_ENV !== 'production') { if (methods && hasOwn(methods, key)) { warn( `Method "${key}" has already been defined as a data property.`, vm ) } } if (props && hasOwn(props, key)) { process.env.NODE_ENV !== 'production' && warn( `The data property "${key}" is already declared as a prop. ` + `Use prop default value instead.`, vm ) } else if (!isReserved(key)) { proxy(vm, `_data`, key) } } // observe data observe(data, true /* asRootData */) }
这是初始化data函数的方法,其中将props和methods中的对象和函数与data中的比较,避免重复。这里只在data里传了一个message字段,走到until/lang.js isReserved()
export function isReserved (str: string): boolean { const c = (str + '').charCodeAt(0) return c === 0x24 || c === 0x5F }
0x24和0x5F是$和_对应的ASCII码值,isReserved函数是判断data里的字段是否带有$和_,为true会走到 proxy(vm, `_data`, key)这里,这里使用了代理模式
export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) }
这里是Object.defineProperty的实现,set和get, 然后实现observe(data, true /* asRootData */)
../observer/index.js
xport function observe (value: any, asRootData: ?boolean): Observer | void { if (!isObject(value) || value instanceof VNode) { return } let ob: Observer | void if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value) } if (asRootData && ob) { ob.vmCount++ } return ob }