环境准备及打包调试
依赖安装
git clone https://github.com/vuejs/vue
安装依赖 npm i
安装rollup npm i -g rollup
修改dev脚本添加sourceMap
"dev": "rollup -w -c scripts/config.js --sourcemap"
dist/vue.runtime.common/esm.js
runtime: 仅包含运行时,包含vue运行核心代码,没有模板编译器
umd:用于浏览器script标签,默认包含运行时和编译器
commonJs:cjs规范适用于旧版打包
esm:ES Model规范用语现代打包器
initMixin(Vue)
initLifecycle(vm)
组件实例里面常用属性初始化,root/refs的初始化
initEvents(vm)
更新组件传递的需要处理的事件
initRender(VM)
createElement函数声明,listeners响应化处理
callHook(vm, 'beforeCreate')
initInjection(vm)
inject数据响应化处理
initState(vm)
执行数据状体初始化
stateMixin() 状态混入
定义props两个实例属性和delete、$watch三个实例方法
eventsMixin()
实现事件相关实例api:emit,once
lifecycleMixin()
实现组件生命周期相关的三个核心实例api:_update,destory _update是组件更新周期中的关键方法,组件出发更新该函数会调用,它执行vnode的diff和patch等操作
renderMixin()
实现$nextTick,_render()
数据响应式
vue一大特点是数据响应式,数据的变化会作用于UI而不是DOM,原理上讲是利用了js语言特性Object.defineProperty()通过定义对象属性set方法拦截对象属性变更,从而将数值的变化转换为UI的变化
Observe
返回一个Observer实例,根据数据类型执行对应的数据响应化操作
Dep
管理一组watcher,包括watcher实例的增删及通知更新
watcher
watcher解析一个表达式并收集依赖,当数值变化时触发回调函数,每个组件会有对应的watcher,数值变化会触发其update函数导致重新渲染
watcher:组件生成/创建watch/renderwatcher,mountComponent跟组件会明确的创建一次watcher
Vue中数据响应化
- defineReactive中的getter和setter对应订阅和发布行为
- Dep的角色相当于主题Subject,维护订阅者,通知观察者更新
- watcher的角色相当于Observe,执行更新
- 但Vue里面的Observe不是上面所说的观察,它和data中对象一一对应,有内嵌的对象就会有childObserver与之对应
数组响应化
数组数据变化的侦测跟对象不同,我们操作数组通常使用push、pop、splice等方法,此时没有办法得知数据变化,所以vue中采取的策略是拦截这些方法通知dep、
拦截器 为数组原型中的7个可以改变内容的方法定义拦截器
Vue异步更新队列
Vue在更新DOM时是异步执行的,只要侦听到数据变化,vue讲开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,如果同一个watcher呗触发多次,只会被推入到队列中一次,这种缓冲时去重对于避免不必要的计算和DOM操作是非常主要的。在下一个事件循环tick中,vue刷新队列并执行实际工作。vue在内部对异步队列尝试使用原生的promise、mutationObserver和setinmmediate,如果执行环节不支持,则会采用settimeout(fn,0)代替
queueWatcher 执行watcher入队操作,若存在重复id则跳过
// 刷新队列
if(!waiting){
waiting = true
nextTick(flushSchedulerQueue)
}
nextTick(flushSchedulerQueue) nextTick按照特定异步策略执行队列刷新操作
Observe、Dep、Watcher
调用mountComponent时会实例化一个renderWatcher。initData对数据做响应化时,利用defineProperty 的get拦截器Dep进行收集,一个watcher对应着N个Observer实例,因此数据更新时相当于n个watcher都会更新。如果同一个watcher呗触发多次,只会被推入到队列中一次