src/core/instance/index.js
通过概述分析得知,Vue源码最先执行的就src/core/instance/index.js文件,此文件定义了Vue的构造方法
此文件主要定义了许多Vue相关原型方法,主要调用以下方法给Vue的原型链扩展方法,接下来我们一一分析
// 定义初始化相关方法
initMixin(Vue)
// 定义状态相关方法
stateMixin(Vue)
// 事件相关
// $on,$off,$once方法
eventsMixin(Vue)
// 生命周期相关
lifecycleMixin(Vue)
// 实现$nextTick 与 _render方法
renderMixin(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)
// 事件相关
// $on,$off,$once方法
eventsMixin(Vue)
// 生命周期相关
lifecycleMixin(Vue)
// 实现$nextTick 与 _render方法
renderMixin(Vue)
export default Vue
initMixin(Vue)
主要定义了Vue.prototype._init方法,new Vue()实例化的时候调用的方法就是_init()方法
_init()方法将在 new Vue(options) 一节说明
stateMixin(Vue)
此方法主要定义了Vue.$set , Vue.$delete , Vue.$watch方法, 这三个方法的主要作用都是为了实现响应式原理,这些方法将在 响应式原理 章节讲解
eventsMixin(Vue)
此方法主要定义了 Vue.prototype.$on, Vue.prototype.$once, Vue.prototype.$off, Vue.prototype.$emit四个方法
- Vue.prototype.$on方法作用是给vue实例vm._events 赋值,组件绑定的事件都存在_events(数组)属性上
- Vue.prototype.$once方法的作用就是执行过一次的方法,就会被移除
- Vue.prototype.$off方法的作用就是将事件从_events(数组)属性中移除
- Vue.prototype.$emit方法的作用就是可以执行保存在_events属性中的事件
export function eventsMixin (Vue: Class<Component>) {
const hookRE = /^hook:/
// 监听当前实例上的自定义事件。事件可以由vm.$emit触发。回调函数会接收所有传入事件触发函数的额外参数。
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
// 如果是数组,递归调用
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
// vm的_events属性保存事件,同名的事件都保存在一个数组里
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
// 如果匹配到,标识有钩子事件
vm._hasHookEvent = true
}
}
return vm
}
// 监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on () {
// 执行on()的话,on会先被移除,再执行一次,也就是执行一次之后会被移除
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
// 无参数,移除所有事件
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
// 数组遍历递归移除
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event]
// 不存在 直接返回
if (!cbs) {
return vm
}
// 如果fn参数没传,或者为false,移除所有事件
if (!fn) {
vm._events[event] = null
return vm
}
// specific handler
// 如果fn参数传了,只移除于fn相等的函数
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}
lifecycleMixin(Vue)
主要定义了三个方法
- Vue.prototype._update 更新渲染真实dom
- Vue.prototype.$forceUpdate 迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
- Vue.prototype.$destroy 销毁组件方法
详细介绍将在后续章节讲解
renderMixin(Vue)
主要定义了两个方法
Vue.prototype.$nextTick方法, 此方法将在专门章节讲解
Vue.prototype._render方法, 此方法将在渲染章节讲解
接下来执行的是initGlobalAPI(Vue)全局api定义