近来没什么事,准备把Vue的源码通读一遍,2.0中加入了Serve-Side-Render、Virtual-Dom等新特性,看一看实现原理,顺便记录一下学习的过程,毕竟好记性不如烂笔头。
作为源码解读系列第一篇文章,先从整个框架的入口开始看吧。Vue使用了部分ES6语法,比如module机制。
import config from './config' //加载初始全局API import { initGlobalAPI } from './global-api/index' //类的初始化入口 import Vue from './instance/index'
initGlobalAPI(Vue) //在Vue类的原型上定义$isServer属性,用于判断是否支持服务端渲染 //这样在Vue实例上调用vue.$isServer就可以知道是否开启了SSR //关于Object.defineProperty,这是一个ES5的方法,可以直接在对象上定义属性 //Vue的数据绑定机制就是用的Object.defineProperty,这也是Vue不支持IE8及以下版本的原因 //将data对象的属性转成getter和setter,具体原理见http://cn.vuejs.org/guide/reactivity.html Object.defineProperty(Vue.prototype, '$isServer', { get: () => config._isServer }) //定义Vue版本号 Vue.version = '2.0.0-rc.4'
export default Vue
|
以下就是config的源码
//Vue使用了Flow做静态类型检查 //Flow可以在代码运行前检查出参数、返回值、变量等类型异常 //方便我们在代码运行前进行部分debug /* @flow */ import { no, noop } from 'shared/util' export type Config = { optionMergeStrategies: { [key: string]: Function }; silent: boolean; devtools: boolean; errorHandler: ?Function; ignoredElements: ?Array<string>; keyCodes: { [key: string]: number }; // platform isReservedTag: (x?: string) => boolean; isUnknownElement: (x?: string) => boolean; getTagNamespace: (x?: string) => string | void; mustUseProp: (x?: string) => boolean; // internal _assetTypes: Array<string>; _lifecycleHooks: Array<string>; _maxUpdateCount: number; _isServer: boolean; } const config: Config = { optionMergeStrategies: Object.create(null), //是否显示warning,默认显示 silent: false, //是否启动devtool devtools: process.env.NODE_ENV !== 'production', errorHandler: null, ignoredElements: null, keyCodes: Object.create(null), isReservedTag: no, isUnknownElement: no, getTagNamespace: noop, mustUseProp: no, //组件的默认类型 _assetTypes: [ 'component', 'directive', 'filter' ], //定义组件的各种生命周期 _lifecycleHooks: [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed', 'activated', 'deactivated' ], //规定组件在一次刷新中的最大循环更新次数 _maxUpdateCount: 100, //判断全局环境是否为server _isServer: process.env.VUE_ENV === 'server' } export default config
|
再来看Vue实例由哪些部分构成,这部分代码在src/instance
,这个文件夹下有events.js,init.js,lifecycle.js,proxy.js,render.js,state.js`等文件,可以看到,一个Vue instance由这几部分构成。为了将这些模块组织在一起,Vue使用了Mixin模式。以下是index.js的代码:
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) }
initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue)
export default Vue
|
这里的Mixin利用了每个JavaScript对象都有一个原型,通过原型可以继承更多的属性,实现Vue类上方法的重用。在每个mixin方法中,将Vue类作为参数传递进去,并在其原型上定义各种内置方法。以eventsMixin方法为例:
//为了说明,这里是其简化版 export function eventsMixin (Vue: Class<Component>) { //实现事件绑定的方法,将其添加到原型上 Vue.prototype.$on = function (event: string, fn: Function): Component { const vm: Component = this //支持绑定多个事件 ;(vm._events[event] || (vm._events[event] = [])).push(fn) return vm }
|
总结:
- Vue中大量使用Object.property,包括利用它生成访问器,实现数据绑定。
- Vue使用了Flow做静态类型检查,有助于提前发现bug
- 实现Vue类的过程中使用了Mixin这种设计模式,基于JavaScript原型继承的特性,可以方便的扩展Vue实例的功能。
参考链接:
Object.defineProperty()
Flow | A Static Type Checker For JavaScript