vue源码目录结构
简单,干净,明了,清晰
src
├── compiler # 编译相关
├── core # 核心代码
├── platforms # 不同平台的支持
├── server # 服务端渲染
├── sfc # .vue 文件解析
├── shared # 共享代码
vue是什么?
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) {
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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
vue是一个function类,为什么不用es6的class呢?因为下面有多个xxMixin(Vue)的调用,并把Vue当作参数传入,它们的作用都是为Vue的prototype扩展一些方法,这些方法都是Vue按功能将各个扩展分散到各个模块中去实现,而使用class是难以实现的。
全局 initGlobalAPI
Vue在整个初始化的过程中,除了给它的原型扩展方法,还会给Vue本身扩展全局的静态方法;
在Vue官网上关于全局的api在这个下main都能找到
/* @flow */
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
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)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
数据驱动
Vue和react的核心思想都是数据驱动,而什么是数据驱动呢?所谓的数据驱动就是不直接操作dom,而是通过数据的修改来改变视图的状态。这样做的好处在于避免了直接操作dom,使代码更加的简洁易懂,同时避免了浏览器的重绘重排等性能的消耗,能够更快(不是说直接操作dom慢,而是这样更快)。所以当交互复杂时,只关心数据的修改使得代码的逻辑更加清晰,性能也更好。那么关于dom,vue是怎么将数据和模板渲染成dom的呢?
new Vue发生了什么
之前提到,vue的本质是一个function类,通过new来实例化。
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)
}
从这里可以看出,Vue只能通过new来实例化,如果不是生产环境并且不在Vue原型链上,那么就会warn应该使用new 关键字;
当是通过new关键字来实例化一个对象的时候,vue会执行Vue原型链上的_init方法来初始化vue,并将节点(字符串)作为参数传入(options是new Vue('#app')时传入的字符串);在_init下会初始化生命周期、事件、render、props、data等
vm._self = 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')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
在最后,如果有el属性,那么就调用vm.$mount方法挂载vm,挂载vm的最终目的就是为了将模板渲染成最终的DOM;
明天计划:Vue实例挂载的实现。
网友评论