Vue核心
Vue.js执行了初始化定义(initMixin)、状态管理(stateMixin)、事件(eventsMixin)、生命周期(lifecycleMixin)、渲染(renderMixin)五个方法
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'
import type { GlobalAPI } from 'types/global-api'
function Vue(options) {
if (__DEV__ && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
//@ts-expect-error Vue has function type
initMixin(Vue)
//@ts-expect-error Vue has function type
stateMixin(Vue)
//@ts-expect-error Vue has function type
eventsMixin(Vue)
//@ts-expect-error Vue has function type
lifecycleMixin(Vue)
//@ts-expect-error Vue has function type
renderMixin(Vue)
export default Vue as unknown as GlobalAPI
initMixin
这个方法只做了一件事,就是定义了Vue
的初始化流程。
export function initMixin(Vue: typeof Component) {
Vue.prototype._init = function (options?: Record<string, any>) {
//初始化流程
}
}
注意了只是定义,这是一个匿名函数,不会执行函数里的代码的,我们打开src/core/instance/index.ts
文件
function Vue(options) {
if (__DEV__ && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
会发现这个匿名函数实际是在Vue
的构造函数中调用的,也就是new Vue({xxx})
时触发。
stateMixin
这个方法定义实现了对Vue.js的data
和props
两大数据持有对象的数据劫持,以及定义了观察者模式实现
const dataDef: any = {}
dataDef.get = function () {
return this._data
}
const propsDef: any = {}
propsDef.get = function () {
return this._props
}
Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
这里需要认真理解this的含义,this代表的是被劫持的对象,也就是vm
,这里通过数据劫持使vm
上的$data
/$props
成为了_data
/_props
的代理对象。
只要在Vue
初始化时将传入的data
/props
实例放到vm
的_data
/_props
属性中,就可以愉快的用this.$data
获取到data
数据了
Vue.prototype.$set = set
Vue.prototype.$delete = del
这两行对$set
和$delete
的赋值看着有点莫名其妙,但是他在Runtime中不会执行,所以先不必理会其具体实现。
这两个方法是用来动态增删观察者模式中需要观察的属性的,也就是说代码中data里没有定义的属性,如果也想获得数据绑定能力,可以通过vm.$set()
方法设置监听,尔某个属性如果想要解除数据绑定则可以调用vm.$delete()
方法解除监听。
Vue.prototype.$watch = function (
expOrFn: string | (() => any),
cb: any,
options?: Record<string, any>
): Function {
//观察者实现
}
这里为vm
的$watch
属性定义了观察者模式实现,同样Runtime中不会执行,先不必理会。
eventsMixin
这个方法定义了Vue.js设计的几个事件的实现。
const hookRE = /^hook:/
Vue.prototype.$on = function (
event: string | Array<string>,
fn: Function
): Component {
const vm: Component = this
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
;(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
}
$on
方法是用来添加监听事件的,比如说v-on:click="handle()"
,就会调用vm.$on()
来新增一个click
监听,发生click
事件的时候,则会触发handle()
函数,click
是浏览器的事件,我们也可以添加自定义的事件监听。
这里的核心是设计了一个事件维护对象_events
,组件中所有的事件都会被存放到这个对象中
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on() {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
$once
方法可以直接看其实现,当事件产生的时候,会立即注销该监听函数的监听,然后执行监听函数,达到只执行一次的效果。
Vue.prototype.$off = function (
event?: string | Array<string>,
fn?: Function
): Component {
//注销监听函数实现
}
$off
方法在$once
方法中用到了,是用来注销监听函数的。
具体实现是找到事件名对应的所有监听函数,将想要注销的监听函数从对象中移除。
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
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
}
$emit
方法会将传入的事件名对应的所有监听函数都执行一遍。
这里有一个arguments
变量,是一个表示所有入参的数组,别看函数入参只有一个event
,但实际的入参数据是放在event
参数后传入的,事件对应的监听函数入参会接收到$emit
方法的第二到最后一个参数。
这四个事件相关的函数构成了Vue.js的事件系统
lifecycleMixin
这个方法定义了生命周期中页面的更新、强制更新、销毁实现
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
//更新页面实现
}
vm._update
可以触发
Vue.prototype.$forceUpdate = function () {
//强制观察者更新
}
Vue.prototype.$destroy = function () {
//销毁页面
}
renderMixin
这个方法定义了页面渲染的一些功能
installRenderHelpers(Vue.prototype)
这里会在vm
上设置一些对VNode
的操作类,方便_render
中调用
Vue.prototype.$nextTick = function (fn: (...args: any[]) => any) {
//
}
$nextTick
方法可以注册一个DOM
更新完成时的回调函数。
因为DOM
不是时刻都在更新的,而是会异步将该一段时间内的变化合并后再更新到DOM
,所以我们修改了data
中的数据后是无法在对应位置的DOM
上立马获取到变更后数据的,我们需要使用$nextTick
达成在一个事件循环后再获取DOM
数据的目的。
Vue.prototype._render = function (): VNode {
//页面解析实现
}
}
Vue.js的runtime
就设计了这么些API,虽然我们还没有对每个API的实现进行研究,但凭借Vue.js的开发经验,能够对整体的功能设计有一个大致的了解,只有真正理解了上述内容,才能在阅读Vue
初始化流程的时候不迷茫、不迷惑、不迷路。
网友评论