声明:以下内容为个人学习vue源码中记录的笔记,仅适合我本人,其他人请谨慎阅读,读了你可能也看不懂(狗头
目标
-
熟悉vue源码架构
-
了解vue初始化过程
-
了解vue响应式原理
准备
-
git clone https://github.com/vuejs/vue.git
-
npm i
-
npm i rollup -g
-
package.json => scripts => dev 加上
sourcemap
- "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev", + "dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
-
npm run dev
-
创建测试html,引入
dist/vue.js
断点调试
new Vue()
然后F11逐步深入调试
src\core\instance\index.js
- this._init(options)
- initMixin(Vue)
- stateMixin(Vue)
- eventsMixin(Vue)
- lifecycleMixin(Vue)
- renderMixin(Vue)
初始化流程
vm.mount()
src/core/instance/lifecycle.js
mountComponent //挂载组件
updateComponent/new Watcher
_update // patch解析
/src/core/instance/render.js
_render
总结一下
new Vue() => this._init() => vm.mount() => mountComponent() => updateComponent/new Watch => _render() => -update()
响应式流程
其中stateMixin里面是数据响应式相关的东西,我们重点学习的内容,
src\core\instance\state.js
在initData中就是初始化data中方法,观察其实很简单,总结一下就做了这几件事:
- 判断data是函数还是对象
- 判断有没有和props或method中的名字冲突
- 最后把data传给obsever
所以重点学习的内容在obsever中
~小插曲:
在state.js中发现了一个函数,解决了我的一个疑惑
- 为什么this.xxx可以直接访问this.data.xxx?
const sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
}
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
// proxy(vm, `_data`, key)
原来是通过Object.defineProperty加了一层代理
src\core\observer\index.js
核心函数就是defineRective
/**
* Define a reactive property on an Object.
*/
// 定义属性拦截
// Vue.util.defineReactive = defineReactive
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 小管家:每个key一个
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 如果val是对象还会递归它
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
// 依赖收集
if (Dep.target) {
dep.depend()
if (childOb) {
// 如果存在对象嵌套,则存在子ob实例,需要建立大管家和当前watcher之间的关系
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* 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()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
写个简版便于理解
function observe(){
/* 一些其他操作 */
return new Observe()
}
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get(){
/* ...依赖搜集... */
return val
},
set(newVal) {
if(newVal === val) return
val = newVal
/* ...更新通知... */
}
})
}
class Observe {
constructor(value) {
/* ... 一些针对数组的判断及处理... */
this.walk(value)
}
walk(obj) {
const keys = Object.keys(obj)
for(let i = 0;i<keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
视图中出现每个key
都需要一个Watcher
来维护,一个key
可能在出现多次,每个key
创建一个dep,
所以一个dep可以管理多个watcher
总结
先上一个刚画的简图
1,初始化的时候会遍历使用observer遍历data,拦截他们的get,set
2,每个key对应一个Dep,get里面使用dep.addDep收集依赖,set里dep.notify通知更新
3,如果将来data里面的数据变化,会通过对应的dep,通知所有的watcher执行更新函数