准备工作
这个文章很简单,主要是看下面的代码运行的过程
<script src="../../dist/vue.js"></script>
<body>
<div id="app">
{{ msg }}
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 'Hello!',
}
});
</script>
</body>
开始调试
所在目录 | 调试函数 | 要做的事 | src/core/instance/index.js | Vue | 发现他调用了this._init()方法(此时this上已经挂载了很多方法,关于这些挂载的方法,以后再出文) | src/core/instance/init.js | _init() | 调用initState去初始化实例状态, 进入initState | src/core/instance/state.js | initState() | _data、_props、_methods, 调用 initData(vm), 进入initData | src/core/instance/state.js | initData() | 把data中的成员注入到vue实例,并把她转换成响应式 | .. | initData() | 获取data的所有属性名,判断是否和props,methods重名,如果不重名把data中的成员注入到vue实例 _data中 | .. | initData() | 执行observe(data,true /* asRootData */) 开始响应式的处理,进入observer函数 | src/core/observer/index.js | observer() | 判断value是否为对象或者是VNode,不是对象直接返回 | .. | observe() | 如果value有__ob__(observer对象)属性并且value.ob 是Observer实例,返回实例 | .. | observe() | 否则 new Observer(value) ,并且返回, 下面我们进入Observer看下里面发生了什么 | src/core/observer/index.js | Observer类 | 在构造函数内部,看到了this.dep = new Dep() 进行依赖收集,进入Dep | src/core/observer.dep.js | Dep类 | 发现里面定义了一些方法,和subs数组,准备进行依赖收集,注意:此时还没收集呢,只是定义,我们回到 Observer类 | src/core/observer/index.js | Observer类 | 执行def函数,给value对象设置__ob__属性,值为当前实例,然后判断value是否为数组,如果是数组,执行对应的响应式处理,如果是对象,执行this.walk(value) ,进入walk函数 | .. | walk | 遍历每个对象属性,调用defineReactive(obj,key[i]) 设置为响应式数据,下面我们进入defineReactive | .. | defineReactice | 创建依赖对象实例 、获取obj的属性描述符对象, 通过描述符对象提取用户设置的get、set,判断是否递归观察子对象(根据shallow去判断是否需要深度监听),并将子对象属性都转换成getter/setter, 返回子观察对象 | .. | defineReactice | 执行Object.defineProperty方法为对象设置get和set, 这个部分是响应式的关键,下面附上了这部分的代码及相应注释 |
---|
附上核心实现源码
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// 如果用户设置了getter,则value等于getter调用的返回值, 否则直接赋予属性值
const value = getter ? getter.call(obj) : val
// 下面的部分来收集依赖
// 当访问值得时候会进行依赖收集,依赖收集其实就是把依赖该属性的watcher对象添加到dep对象的subs数组中,将来数据变化的时候通知所有的watcher
// 如果存在当前依赖目标,即 watcher对象,则建立依赖
if (Dep.target) {
// 进行依赖收集
dep.depend()
// 如果子观察目标存在,建立子对象的依赖关系
if (childOb) {
childOb.dep.depend()
// 如果属性是数组,则特殊处理收集数组对象依赖
if (Array.isArray(value)) {
dependArray(value)
}
}
}
// 返回属性值
return value
},
set: function reactiveSetter (newVal) {
// 如果用户设置了getter,则value等于getter调用的返回值, 否则直接赋予属性值
const value = getter ? getter.call(obj) : val
// 如果新值等于旧值 或者 新值旧值都为NaN则不执行
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// 如果没有 setter 直接返回
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
// 如果用户设置了setter则调用setter
setter.call(obj, newVal)
} else {
// setter/getter 都不存在,更新新值
val = newVal
}
// 如果新值是对象,观察子对象并返回 子的 observer对象
childOb = !shallow && observe(newVal)
// 派发更新(发布更改通知)
dep.notify()
}
})
当响应式数据处理完,在src/core/instance/init.js
中继续向下执行,执行$mount
if (vm.$options.el) {
vm.$mount(vm.$options.el)
// 此函数里面主要是解析模板,如果用户不传递render函数,解析模板
}
在src/platforms/web/entry-runtime-with-compiler.js
下面重写了$mount
并进行模板的渲染
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!