Vue2.0 源码学习#0

近来没什么事,准备把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
}
总结:
  1. Vue中大量使用Object.property,包括利用它生成访问器,实现数据绑定。
  2. Vue使用了Flow做静态类型检查,有助于提前发现bug
  3. 实现Vue类的过程中使用了Mixin这种设计模式,基于JavaScript原型继承的特性,可以方便的扩展Vue实例的功能。

参考链接:
Object.defineProperty()
Flow | A Static Type Checker For JavaScript