VueJS学习之旅 07
下面我就来看看Vue的核心构造器以及其实例的属性和方法。
Vue构造器
从 'src/core/index.js' 文件中可以找到Vue构造器的定义是在 'src/core/instance/index.js' 中给出的。
打开该文件,我们来看看代码。
// src/core/instance/index.js
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)
- 非常简短,该构造器接收一个对象类型的 options 参数。
- 初始化Vue实例对象时,默认调用原型对象上的 _init (Vue.prototype._init) 方法。
该文件中还通过调用一些模块,给Vue增加了一些实例方法。
- 与数据相关的方法:$set, $delete, $watch。
- 与事件相关的方法:$on, $once, $off, $emit。
- 与生命周期相关的方法:$forceUpdate, $destroy。
- 还有一个与生命周期相关的方法 $mount 是在entiry文件(如: 'src/entries/web-runtime-with-compiler.js')中定义的。
以上的这些方法,就是对应官方API中实例方法。
Vue实例对象方法
结合官方API文档和源码,具体来看看这些实例方法。
实例方法 / 数据相关
-
vm.$watch
对应方法源码 src/core/instance/state.js => Vue.prototype.$watch
- 该方法内部新建一个Watcher实例,并将其添加到当前Vue实例对象的 _watchers 列表中。
- 方法返回值是一个撤销观察函数 unwatch。如需撤销观察,只需执行
unwatch();
。
-
vm.$set
对应方法源码 src/core/instance/state.js => Vue.prototype.$set
- 从代码可见,这是Global API中
Vue.set
的别名。
- 从代码可见,这是Global API中
-
vm.$delete
对应方法源码 src/core/instance/state.js => Vue.prototype.$delete
- 从代码可见,这是Global API中
Vue.delete
的别名。
- 从代码可见,这是Global API中
实例方法 / 事件相关
-
vm.$on
对应方法源码 src/core/instance/events.js => Vue.prototype.$on
- Vue实例对象 vm 中有 _events 属性,用来记录事件的注册信息
- 如果 vm 中已有 event 事件注册过,则向该事件注册列表中添加callback处理器;如果没有相关事件注册过,则先行初始化该事件注册列表,再将callback注册到列表中。
- 如果 event 是 hook 事件,则将 vm 的 _hasHookEvent 属性设置为 true
- 执行结果返回 vm。
-
vm.$off
对应方法源码 src/core/instance/events.js => Vue.prototype.$off
- 如果没有传递参数,默认将 vm 的 _events 清空,然后返回当前实例对象。相当于注销所有事件注册。
- 如果 vm 还没有注册过 event 事件,直接返回 vm。
- 如果只提供了 event,则移除该事件所有的监听器
- 如果同时提供了 event 和 callback,则获取该 event 的注册列表,遍历列表将给定的 callback 监听器注销。
-
vm.$once
对应方法源码 src/core/instance/events.js => Vue.prototype.$once
- 分别调用 $on 和 $off 来完成事件的注册和注销。
- 只执行一次,执行后立即移除监听器。
-
vm.$emit
对应方法源码 src/core/instance/events.js => Vue.prototype.$emit
- 获取 vm 中 event 的注册列表
- 遍历注册列表中的所有处理器,依次调用
实例方法 / 生命周期相关
-
vm.$forceUpdate
对应方法源码 src/core/instance/lifecycle.js => Vue.prototype.$forceUpdate
- 如果当前实例对象存在 _watcher 属性,执行 _watcher 的 update 方法。
-
vm.$destroy
对应方法源码 src/core/instance/lifecycle.js => Vue.prototype.$destroy
- Vue实例对象 vm 如果被标记为正在销毁(_isBeingDestroyed == true),则直接返回,避免重复调用。
- 调用 'beforeDestroyed' 生命周期事件处理函数。
- 将 _isBeingDestroyed 属性标记为 true。
- 清除父对象中的信息
- 清除watchers
- 清除当前对象的数据监听(observer)
- 调用 'destroyed' 生命周期事件处理函数。
- 清除所有注册的事件监听
- 触发视图更新
-
vm.$nextTick
对应方法源码 src/core/instance/render.js => Vue.prototype.$nextTick
- 代码执行与
Vue.nextTick
一样,只不过方法执行的句柄(this
)自动邦定到当前的Vue实例对象。
- 代码执行与
-
vm.$mount
对应方法源码 src/entries/web-runtime-with-compiler.js => Vue.prototype.$mount
- 该方法实际上调用的是
Vue.prototype._mount
方法,定义在 'src/core/instance/lifecycle.js' 文件中。 - 如果实例对象 vm 中没有给定
render
,则将 'createEmptyVNode' 赋值给 'render'。 - 调用 'beforeMount' 生命周期事件处理函数。
- 给 vm 增加watcher。
- 调用 'mounted' 生命周期事件处理函数。
- 返回 vm 实例对象。
- 该方法实际上调用的是
Vue对象实例的属性
有关于Vue实例属性的初始化,基本上是调用 Vue.prototype._init 方法中完成的。上面提到过这个方法会在new Vue(options)
时自动调用。
这个方法定义在 'src/core/instance/init.js' 文件中,简要地看看它都做了什么。
- 初始化 vm.$options,主要是调用 mergeOptions 方法,将构造器的默认属性与给定的options合并后赋值为 vm.$options。
- 初始化生命周期相关的属性,给 vm.$parent, vm.$root, vm.$children, vm.$refs 等属性赋值。
- 初始化 vm 事件监听,将父组件事件更新到当前对象
- 调用 'beforeCreate' 生命周期事件处理函数
- 初始化与data相关的属性,这里面有一步重要的操作,就是observe数据(vm._data,vm.$data就是对该对象的代理)。这会创建Observer对象并调用 defineReactive 函数,这是Vue实现双向数据邦定的基础。具体请参照 defineReactive 函数以及JS属性描述符相关资料。
- 调用 'created' 生命周期事件处理函数
- 初始化 render 相关的属性:$slots, $scopedSlots等。
结合官方API文档和源码,具体来看看这些实例属性的含义以及他们是如何赋初值的。
-
vm.$data
Vue 实例观察的数据对象。Vue 实例代理了对其 data 对象属性的访问。
- 该属性不是直接定义在实例对象 vm 上,而是定义在原型对象上 =>
Vue.prototype.$data
。 - 该属性是只读属性,不能直接给该属性重新赋值。但可以set其内部具体的内嵌属性。
- 个人理解,就是应为要定义为只读属性,所以在定义在prototype对象上。
- 该属性不是直接定义在实例对象 vm 上,而是定义在原型对象上 =>
-
vm.$el
Vue 实例使用的根 DOM 元素。
- 该属性不是在初始化时设置的,而是在调用
Vue.prototype._mount
或ue.prototype._update
时才赋值的。
- 该属性不是在初始化时设置的,而是在调用
-
vm.$options
用于当前 Vue 实例的初始化选项。需要在选项中包含自定义属性时会有用处
- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。构造器的默认属性与给定的options合并后赋值给它。
- 当初始化实例对象时,通过执行
-
vm.$parent
父实例,如果当前实例有的话。
- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。
- 当初始化实例对象时,通过执行
-
vm.$root
当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自已。
- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。
- 当初始化实例对象时,通过执行
-
vm.$children
当前实例的直接子组件。需要注意 $children 并不保证顺序,也不是响应式的。如果你发现自己正在尝试使用 $children 来进行数据绑定,考虑使用一个数组配合 v-for 来生成子组件,并且使用 Array 作为真正的来源。
- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。
- 当初始化实例对象时,通过执行
-
vm.$slots
用来访问被 slot 分发的内容。每个具名 slot 有其相应的属性(例如:slot="foo" 中的内容将会在 vm.$slots.foo 中被找到)。default 属性包括了所有没有被包含在具名 slot 中的节点。
在使用 render 函数书写一个组件时,访问 vm.$slots 最有帮助。- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。
- 当初始化实例对象时,通过执行
-
vm.$scopedSlots
用来访问被
scoped slots
。包括default
在内的每个 slot,对象内都包含一个返回VNodes
的函数。
在使用 render 函数书写一个组件时,访问 vm.$slots 最有帮助。- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。
- 当初始化实例对象时,通过执行
-
vm.$refs
一个对象,其中包含了所有拥有 ref 注册的子组件。
- 当初始化实例对象时,通过执行
Vue.prototype._init
方法时赋值的。
- 当初始化实例对象时,通过执行
-
vm.$isServer
当前 Vue 实例是否运行于服务器。
- 该属性不是直接定义在实例对象 vm 上,而是定义在原型对象上 =>
Vue.prototype.$isServer
。 - 该属性根据当前运行的环境以及
process.ven.VUE_ENV
自动设置。
- 该属性不是直接定义在实例对象 vm 上,而是定义在原型对象上 =>