开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情
前言
前面两篇,经过调试,画图,查阅资料,大致把整个初始化阶段,更新阶段,销毁阶段的代码梳理了一遍。其实调试过程中,还是有一些不清楚的地方,其实整个源码看起来并不复杂,主要是基础方法、各种方法嵌套、闭包使用等基础部分不熟练。等完成这一系列,要把红宝书拿出来翻翻记录起来。
前端啊,没完没了!!!!
回顾
涉及的前端小知识【遇到就记录一下】
在上一篇中提到的optimize(ast,options)方法中有一个markStatic标识静态节点的方法,在里面有这样的一些代码:
这个地方涉及到一个nodeType的小知识点,记录一下:
Node.nodeType:仅读属性,Node.nodeType表示的是该节点的类型
1:元素节点
2:属性节点
3:文本节点
4:CDATA区段
5:实体应用元素
6:实体
7:表示处理指令
8:注释节点
9:最外层的Root element,包括所有其他节点
11:文档碎片节点
12:DTD中声明的符号节点
10:<!DOCTYPE ...>
全图
跟着生命周期,源码,运行过程描绘完整个全图,大致就是下图这样(真的好丑,但是我并不想重新画一下)
vue中最最最核心的方法
Watcher源码解析:方法参数的解释:
文件调用路径:(src/core/observer/watcher.js)
- deps[]存放dep的数组
- lazy 标记是否是计算属性watcher
- user标记是否是用户watcher
- getter 渲染函数或表达式
- value计算属性的缓存
- addDep()去重重复dep并记录后调用dep.addSub(this)
- depend()调用所有deps[]中的dep.depend()
- get()主要执行getter()
- update()去重watcher防抖,异步执行run()
- evaluate()缓存计算属性,执行getter
- run()执行get()及侦听器中用户的回调 vue文件中使用方式
watch: {
value: function (value) {
// update value
$(this.$el).val(value).trigger('change')
}
}
computed: {
valuse(val){
return val + 'sd'
}
}
render Watcher
生命周期中调用(src/core/instance/lifecycle.js[197行]):render Watcher
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)// 最后一个参数为true,说明此处调用的是一个render watcher
class Watcher
export default class Watcher {
...
constructor (
vm: Component, // vue实例
expOrFn: string | Function, // 表达式或者函数
cb: Function, // 回调函数
options?: ?Object, // 控制选项
isRenderWatcher?: boolean // 是否是render watcher
){
...
if (options) {
this.deep = !!options.deep
this.user = !!options.user 标识用户代码,为true时,出现异常时会有错误提示
this.lazy = !!options.lazy 用于computed watcher,值为true不会执行run()
this.sync = !!options.sync 用于watch watcher,值为true时,同步执行run(),执行更新
this.before = options.before // 用于触发beforeUpdate钩子
} else {
this.deep = this.user = this.lazy = this.sync = false
}
...
this.deps = [] // 执行cleanupDeps后,将newDeps赋值给deps
this.newDeps = [] 用于手机更新后当前组件实例依赖的deps
this.depIds = new Set() 执行cleanupDeps后,将newDepIds赋值给depIds
this.newDepIds = new Set() 防止重复收集依赖
...
> 这一步的操作就是将`expOrEn`赋值给getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn // expOrFn这个参数指的就是:updateComponent函数
} else {
this.getter = parsePath(expOrFn)
...
}
...
}
watcher中的get方法
get () {
pushTarget(this) // 把当前的watcher实例赋值给Dep.target(手机依赖的时候,Dep.target必须有值)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm) // 重要,代码发生变化,都是在这一个行执行时发生改变
} catch (e) {
...
} finally {
...
popTarget() // 将Dep.target的值还原为之前的watcher实例
this.cleanupDeps() // 这个时候表明watcher的依赖收集完毕
}
return value
}
this.getter打印结果:实际上执行的就是updateComponent
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
_render:执行vue实例的渲染,返回vue实例的vnode(虚拟DOM)
update:执行挂载,内部有个__patch_
patch:src/core/vdom/patch.js----->著名的diff算法(下一篇文章专门记录)
watcher中的update方法
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true // 并不会立即执行,也不会加入到异步更新队列
} else if (this.sync) {
this.run() // 立即执行
} else {
queueWatcher(this) // 将watcher实例加入到异步更新队列中,执行nextTick 等方法,后续单独加一片文章记录一下异步更新机制。
}
}
watcher中的run方法
run () {
if (this.active) { // 标识watcher是否在激活状态下
const value = this.get() // 执行updateComponent,返回永远是undefined,下面代码不再执行
if (
value !== this.value ||
isObject(value) ||
this.deep
) {
// set new value
const oldValue = this.value
this.value = value
if (this.user) {
const info = `callback for watcher "${this.expression}"`
invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info)
} else {
this.cb.call(this.vm, value, oldValue)
}
}
}
}
watcher 可以分为三种调用方式
- computed watcher: 用于计算属性的监听
- watch watcher: watch监听数据是否变化
- render watcher :vue渲染
1.computed watcher
initComputed()方法的执行顺序:beforeCreate--->initComputed()--->created
- ...
- callHook(vm, 'beforeCreate')
- ...
- initState(vm)[src/core/instance/init.js(57)]
- if (opts.computed) initComputed(vm, opts.computed)
- initComputed[src/core/instance/state.js(170)]
- if (opts.computed) initComputed(vm, opts.computed)
- ....
- callHook(vm, 'created')
const computedWatcherOptions = { lazy: true }
function initComputed(){
...
// 判断用户写法:
// value(){return 1}
// value:{get(){return 1}}
const getter = typeof userDef === 'function' ? userDef : userDef.get
...
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions // {lazy:true} 不会立即执行
)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef) // 定义计算属性
} else {...}
}
2.watch watcher
initWatch()方法的执行顺序:beforeCreate--->initWatch()[src/core/instance/state.js)(293)]--->created
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key] // handler 为每个key值对应的方法
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) { // 循环内部每一个key执行creatWatcher
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options) // 内部执行new Watcher 创建实例(src/core/instance/state.js[359])
}
3.render watcher
后记
本文仅作为自己一个阅读记录,具体还是要看大佬们的文章 下一篇:我的源码学习之路(四)---vue-2.6.14