使用Vue也有一些时间了,发现对Vue的理解基本上还停留在使用层面,深究到细节处,都是似懂非懂。参考官网和网上几篇介绍Vue响应式原理的文章似乎都过于‘深入浅出’,对于我这种非计算专业毕业的同学来说,刚开始看还是比较吃力的,想继续学习研究又很难有突破,所以还是自己动手,这几天重新看了下Vue代码,对基础做些梳理,想把自己的理解写下来,也算是对自己学习的一个总结,能跟其他同学分享也是极高兴的,有理解不到位的地方,请加大多多指教。
个人觉得不管入手Vue还是其他库,最重要的是要理解好基础对象,毕竟面向对象编程嘛-_-,其中Vue构造函数,Vue实例对象,Watcher,Dep 一套下来是理解响应式的基础吧。
今天梳理的是Vue的实例对象,也就是new Vue(options)返回的对象,在实际开发中就是我们面对的应用或者组件。刚开始看代码的同学应该都有一种感觉,好像一些属性都眼熟,但是干什么用的,怎么来的不太清晰,下面列出的是Vue实例对象的属性:
const VueInstance = {
$attrs: {},
$children: [],
$createElement: ƒ (a, b, c, d),
$el: domNode,
$listeners: {},
$options: {},
$parent: undefined,
$refs: {},
$root: {},
$scopedSlots: {},
$slots: {},
$vnode: undefined,
$data: {},
$isServer: false,
$props: undefined,
$ssrContext: undefined,
_c: ƒ (a, b, c, d),
_computedWatchers: {xxx: Watcher},
_data: {__ob__: Observer},
_directInactive: false,
_events: {},
_hasHookEvent: false,
_inactive: null,
_isBeingDestroyed: false,
_isDestroyed: false,
_isMounted: true,
_isVue: true,
_renderProxy: {},
_self: {},
_staticTrees: null,
_uid: 0,
_vnode: {},
_watcher: {},
_watchers: []
}
对上面的属性做个介绍:
-
_watcher: 组件模版的watcher
- 初始化为null
- 组件在挂载时,为组件模版实例化了Watcher实例,并赋值给vm._watcher(仅在为模版创建Watcher实例时)
-
_watchers:
- initComputed过程中,实例化了Watcher实例,并push到vm._watchers数组中,
- initWatch过程中,实例化了Watcher实例,并push到vm._watchers数组中,(实质上是调用
vm.$watch,通过vm.$watch实例化了Watcher实例) - 通过
vm.$watch监听的属性,实例化了Watcher实例,并push到vm._watchers数组中,包括initWatch和手动this.$watch - 组件在挂载时,为组件模版实例化了Watcher实例,并push到vm._watchers数组中,此处还为vm._watcher赋值(仅在为模版创建Watcher实例时)
-
_computedWatchers:
- 实例化时,在initState中调用initComputed,为$options.computed对象中的键值对创建Watcher实例对象
- initComputed过程中,通过defineComputed为组件实例设置对应属性,
- initComputed过程中,实例化了Watcher实例,并push到vm._watchers数组中,
-
$options:
- 实例化时,通过mergeOptions,合并了constructor的$options和传入配置项options。
- $options._parentListeners属性作为组件的初始化事件
- listeners属性,并且不可修改
-
$vnode: 指向$options._parentVnode- 在父组件中表示当前组件的vnode
$vnode.context,表示该组件所在父组件中的上下文,作为scopedSlots的上下文使用$vnode.data,表示该组件所在父组件中解析到的data对象,取其中的attrs属性,设置该组件的$attrs,并不可修改。
-
_vnode:
- 组件内模版解析后的vnode根节点,初始化为null,在挂载生命周期调用_update方法时设置
- vnode.parent属性指向
$options._parentVnode,亦等于$vnode
-
$parent:
- 实例化时,通过options.parent向上递归查询,查找到的第一个非抽象的实例对象,如果没有那该组件就是root对象了。
-
$children:
- 实例化时,子组件通过查找$parent对象过程,主动push。
-
parent对象,取root对象,如果没有合适的root
-
$attrs:
- 实例化时,通过initRender,取$vnode.data属性
- 不可修改属性;
-
$listeners:
- 实例化时,通过initRender,取$options._parentListeners
- 不可修改属性;
-
$refs:
- 初始化为空对象:{}
- 在自建初始化时进行填充
-
$slots:
- 初始化组件时,通过initRender,从$options._renderChildren中解析出所有对应name的slot
- 这里的$options._renderChildren 对应组件模版中的插槽,原生标签不会有组件初始化过程
-
$scopedSlots:
- 初始化组件时,通过initRender,初始化为空对象
-
$data:
- 脚本库运行时,通过stateMixin,在原型上设置了$data,其get属性返回this._data
-
$props: todo
- 脚本库运行时,通过stateMixin,在原型上设置了$props,其get属性返回this._props
-
_data:
- 实例化时,通过initState => initData 取options.data执行返回的对象,并设置响应式
-
_provided:
- 如果$options.provide有值,创建_provided对象
- _provided对象供子元素向上遍历,查找inject时使用
-
_events:
- 初始化为空对象:{}
- 实例化时,如果解析后的$options._parentListeners数组不为空,则添加这些事件
- 通过this.$on添加事件,构建{eventName: fn[]} 对象
- 添加事件时,如果事件名以hook:开头,则设置_hasHookEvent为true
- 通过this.$off注销事件,
-
_hasHookEvent:
- 是否有钩子事件,初始化为false
- 添加事件时,如果事件名以hook:开头,则设置_hasHookEvent为true
-
_staticTrees:
- 使用v-once指令时保存的静态树,初始化为null
- 静态的根节点时保存的静态树,为了优化
-
$el: 根节点用于挂载的元素或元素查询字符串,非必须
-
$isServer: 是否是服务端渲染
-
$ssrContext: 服务端渲染时的上下文
-
_uid: 实例化时设置,一个根据实例化Vue组件次数递增的数字,标示组件的id。
-
_isVue: 实例化时设置,作为标示
-
_self: 实例化时设置为自身的一个引用
-
_c, $createElement: 创建元素的方法
-
_renderProxy: 实例化时,生产环境下设置为自身。
-
_isMounted: 初始化为false,组件挂载后设置为true;
-
_isBeingDestroyed: 初始化为false,组件正在销毁时设置为true;
-
_isDestroyed: 初始化为false,组件销毁后设置为true;
-
_directInactive: 初始化为false
-
_inactive: 初始化为null
方法部分比较好理解就不赘述了
以上!