导航
[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[react] Hooks
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend
[源码-vue06] Vue.nextTick 和 vm.$nextTick
前置知识
Watcher类
- Watcher分为三种
- computed watcher -
负责更新computed
- user-watcher -
watch 一个函数
- render-watcher -
重新渲染
- computed watcher -
- 三种watcher有固定的执行顺序
computed watcher -> user watcher -> render watcher
- 这样安排三种 watcher 的顺序的原因?
- computed watcher 在 render watcher 前面执行,就能保证在视图渲染的时候拿到的是最新的computed
(1) data响应式
[图片上传失败...(image-581ea4-1632444988304)]
-
data响应式具体过程
- 在
new Vue(options)
构造函数中调用this._init(options)
方法,而this._init(options)
方法是在initMixin(Vue)
中定义的 -
Vue.prototype._init
=> 这里主要关注initState(vm)
- 合并 options 对象
- 调用 initProxy
- 调用 initState(vm)
- 其他数据的初始化
-
vm.$mount(vm.$options.el)
初始化渲染
-
initState(vm)
=> 这里主要关注initData
- initProps
- initMethods
- initData
- initComputed
- initWatch
-
initData
- 传入的options对象的data,是函数就调用返回对象,是对象就直接使用
- 如果props,method,data中有相同的key值就抛出警告
-
proxy(vm,
_data, key)
- 主要的作用就是给data中的属性做一层代理
- 通过访问
this.name = vm.name = vm._data.name = vm.$options.data.name = vm.data.name
observe(data, true)
-
observe(data, true)
- 判断data是否具有
__ob__
属性,该属性表示data是否观察过了,即具有响应式了 - 没有
__ob__
属性,就就行观测,执行new Observer(value)
- 判断data是否具有
-
new Observer(value)
- 给data添加
__ob__
属性,值是当前的observer
实例 - data是数组
- 具有原型执行 protoAugment(value, arrayMethods)
- 重写数组原型上的 7 种方法
- push pop unshift shift splice sort reverse 这7种都改变数组
- push unshift splice 添加的属性包装成数组
-
- 继续执行
ob.observeArray(inserted)
循环遍历添加的每一项属性,执行第5步中的observe(items[i])
- 继续执行
-
- 并调用
ob.dep.notify()
执行watcher种的upate去更新视图,从而是这些重写的数组方法具有响应式
- 并调用
-
- 不具有有原型执行 copyAugment(value, arrayMethods, arrayKeys)
-
this.observeArray(value)
- 循环遍历数组种的每一个成员,执行第5步中的
observe(items[i])
- 循环遍历数组种的每一个成员,执行第5步中的
- 具有原型执行 protoAugment(value, arrayMethods)
- data是对象
this.walk(value)
- 给data添加
-
this.walk(value)
defineReactive(obj, keys[i])
-
defineReactive(obj, keys[i])
- Object.defineProperty
- get
- dep.depend()
- set
- dep.notify()
- get
- Object.defineProperty
- 在
源码
initState - src/core/instance/state.js
initState - src/core/instance/state.js
---
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options // opts 获取vm中的 options 参数
if (opts.props) initProps(vm, opts.props) // props存在,就初始化 props
if (opts.methods) initMethods(vm, opts.methods)// methods存在,就初始化 methods
if (opts.data) {
// data存在,初始化 data
initData(vm)
} else {
// data不存在
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed) // computed存在,初始化 computed
if (opts.watch && opts.watch !== nativeWatch) {
// watch存在,并且不是原生的对象上的watch属性,就初始化 watch
// 分为三种 watcher
// render watcher
// compute watcher
// user watcher - watch
initWatch(vm, opts.watch)
}
}
- initData - src/core/instance/state.js
initData - src/core/instance/state.js
---
function initData (vm: Component) {
let data = vm.$options.data
// 获取传入的options对象上的data属性
// 注意:这里的vm.$options在不是组件的情况下,是合并后的options
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
// data是一个函数就调用取返回值,是对象就直接赋值
// vm._data = vm.$options.data
if (!isPlainObject(data)) {
// 不是一个对象,不是一个纯对象赋值空对象
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
// method中存在了该key,所以data中不能再有相同的key
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
// props中存在了该key,所以data中不能再有相同的key
)
} else if (!isReserved(key)) {
// props和methods中都不存在该key,就执行代理proxy函数
proxy(vm, `_data`, key)
}
}
// observe data
// data的响应式
observe(data, true /* asRootData */)
}
- proxy - src/core/instance/state.js
proxy - src/core/instance/state.js
---
export function proxy (target: Object, sourceKey: string, key: string) {
// proxy(vm, `_data`, key)
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
// 1. 重写 get
// 2. 返回 this._data[key]
// 3. this指向:sharedPropertyDefinition.get方法是通过 vm.key 来调用的,this指向vm
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
// 1. 重写 set
// 2. this._data[key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
// vm[key] = vm._data[key]
// 因为: vm._data = vm.$options.data
// 所以:vm[key] = vm._data[key] = vm.$options.data[key]
}
- observe - src/core/observer/index.js
observe - src/core/observer/index.js
---
export function observe (value: any, asRootData: ?boolean): Observer | void {
// 1. observe(vm._data = {}, true /* asRootData */)
// 2. observe(data, true /* asRootData */)
if (!isObject(value) || value instanceof VNode) {
// 不是一个对象 或者 是VNode
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
// 如果value具有__ob__属性 并且 __ob__ 是Observer的实例,就直接赋值
// 即已经观测过了, 直接赋值
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
// 生成一个ob实例
}
if (asRootData && ob) {
// 如果是根data即new Vue()初始化的时候传入的data
// 并且 ob 存在
// vmCount++
// 即统计被生成的次数
ob.vmCount++
}
return ob
}
- Observer - src/core/observer/index.js
Observer - src/core/observer/index.js
---
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value // 赋值传入的对象
this.dep = new Dep() // dep实例
this.vmCount = 0
def(value, '__ob__', this)
// def
// 给 ( value ) 添加 ( __ob__ ) 属性,值是 ( observer ) 实例
// function def (obj: Object, key: string, val: any, enumerable?: boolean) {
// Object.defineProperty(obj, key, {
// value: val,
// enumerable: !!enumerable,
// writable: true,
// configurable: true
// })
// }
if (Array.isArray(value)) {
if (hasProto) {
// const hasProto = '__proto__' in {}
// 是数组,并且本身具有原型属性__proto__
protoAugment(value, arrayMethods)
// protoAugment 重写原型
// value.__proto__ = arrayMethods
// arrayMethods
// const arrayMethods = Object.create(arrayProto)
// const arrayProto = Array.prototype
// def(arrayMethods, method, function mutator (...args){})
// 重写 7 种数组原型上的方法
} else {
// 是数组,并且本身不具有原型,复制原型上的每一个属性
copyAugment(value, arrayMethods, arrayKeys)
// function copyAugment (target: Object, src: Object, keys: Array) {
// for (let i = 0, l = keys.length; i < l; i++) {
// const key = keys[i]
// def(target, key, src[key])
// }
// }
// const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
}
this.observeArray(value)
// 观测数组
// 1. 循环数组的每一项, 对每一项执行 observe(items[i])
} else {
this.walk(value)
// 观测对象
}
}
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/**
* Observe a list of Array items.
*/
observeArray (items: Array) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
- walk - src/core/observer/index.js
walk - src/core/observer/index.js
---
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
- defineReactive - src/core/observer/index.js
defineReactive - src/core/observer/index.js
---
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
// 不存在 或者 属性描述对象不可以被修改,就直接返回
return
}
// Object.getOwnPropertyDescriptor(obj, key) 表示获取obj.key属性属性描述对象
// 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]
}
let childOb = !shallow && observe(val)
// 继续观测对象的每一项的value值,如果还是对象就继续观察 添加响应Object.defineProperty
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) {
childOb.dep.depend() // 循环收集依赖
if (Array.isArray(value)) {
// 如果每一项的key对象的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() // ---------------------------- 通知watcher执行更新
}
})
}
- Dep类 - src/core/observer/dep.js
Dep类 - src/core/observer/dep.js
---
export default class Dep {
static target: ?Watcher;
// target一个 watcher 类型的静态属性
id: number;
// 每次new都使id+1
// 即每次执行都自增1
subs: Array;
// subs数组 用来存放 wacher
constructor () {
this.id = uid++ // id++ uid++ 每次执行都加 +1
this.subs = [] // 初始化为空数组,存放依赖即watcher
}
addSub (sub: Watcher) {
this.subs.push(sub)
// 添加 watcher
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
// 删除 watcher
}
depend () {
if (Dep.target) { //--------------------------------------------- 重点注意 Dep.target
// Dep.target 是当前正在执行的正在计算的 watcher,存在闭包中,相当于全局变量
// 在下面有初始化
Dep.target.addDep(this) // ------------------------------------ Dep 和 Watcher 相互关系
// Dep.target.addDep(this)
// 向 watcher 中添加 dep 实例
// this参数就是dep实例
// Dep.target 就是一个正在计算的watcher
// Watcher中的 addDep
// addDep (dep: Dep) {
// const id = dep.id // dep实例的id属性
// if (!this.newDepIds.has(id)) { // ------------ newDepIds中不存在该id
// this.newDepIds.add(id) // 向 ( Watcher类 ) 的 newDepIds中添加 id
// this.newDeps.push(dep) // 向 ( Watcher类 ) 的 newDeps 中添加 dep
// if (!this.depIds.has(id)) { // ------------- depIds 中不存在该id
// dep.addSub(this) // 向 ( Dep类 ) 的 subs 中添加 该watcher
// }
// }
// }
// 这里操作有点绕
// Dep.target.addDep(this)
// 1. 调用 Dep.target.addDep(this) = new Watcher().addDep(this)
// 2. 把 dep 实例添加到 watcher的 newDeps 数组中
// 3. 把 dep.id 添加到 watcher的 newDepIds 数组中
// 4. 执行 dep.addSubs 把 watcher 添加到 Dep的 subs 数组中
}
}
notify () {
const subs = this.subs.slice()
// 浅拷贝subs数组,缓存一份
if (process.env.NODE_ENV !== 'production' && !config.async) {
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
// 执行subs数组的每一个成员watcher上的update方法
}
}
}
Dep.target = null
// 当前watcher
const targetStack = []
// 存放watcher的栈结构,后进先出
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
// watcher 入栈
Dep.target = target
// 赋值最新的watcher
}
export function popTarget () {
targetStack.pop()
// watcher 出栈
Dep.target = targetStack[targetStack.length - 1]
// 前一个watcher
}
- Watcher类 - src/core/observer/dep.js
export default class Watcher {
constructor (
vm: Component, // vm实例
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean // 是否是renderWatcher
) {
this.vm = vm
if (isRenderWatcher) {
// 是renderWatcher, 则把该watcher实例赋值给 vm._watcher
vm._watcher = this
}
vm._watchers.push(this) // push
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn
// 是函数赋值 getter
} else {
// 不是函数
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = noop
process.env.NODE_ENV !== 'production' && warn(
`Failed watching path: "${expOrFn}" ` +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
)
}
}
this.value = this.lazy
? undefined
: this.get()
}
get () {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
addDep (dep: Dep) {
const id = dep.id // dep实例的id属性
if (!this.newDepIds.has(id)) { // ------------ newDepIds中不存在该id
this.newDepIds.add(id) // 向 ( Watcher类 ) 的 newDepIds中添加 id
this.newDeps.push(dep) // 向 ( Watcher类 ) 的 newDeps 中添加 dep
if (!this.depIds.has(id)) { // ------------- depIds 中不存在该id
dep.addSub(this) // 向 ( Dep类 ) 的 subs 中添加 该watcher
}
}
}
update () {
/* istanbul ignore else */
if (this.lazy) { // 用于computed watcher
this.dirty = true
} else if (this.sync) { // 同步watcher
this.run()
} else {
queueWatcher(this) // nextTick(flushSchedulerQueue)将watcher放入队列,在下一轮tick去更新
}
}
}
(2) 初始化渲染
[图片上传失败...(image-251f59-1632444988304)]
(2-1) vue的不同构建版本
- 独立构建 (包括template编译的过程) -
runtime+compiler完整版
- 渲染过程:
template -> render函数 -> vnode -> 真实的dom
- 渲染过程:
- 运行时候构建(不包括template编译的过程) -
runtime版
- 渲染过程:
render函数 -> vnode -> 真实的dom
- 渲染过程:
- 打包后dist文件夹中的文件会有所不同
- 独立构建的完整版
vue.js
- 运行时版本
vue.runtime.js
- 运行时版本相比完整版体积要小大约 30%
- 独立构建的完整版
(2-2) dom的初始化渲染 - vm.$mount(vm.$options.el)
-
执行
new Vue(options)
=>this._init(options)
=>Vue.prototype._init
=>initState(vm)
=>vm.$mount(vm.$options.el)
- 即在
this._init
中先初始化initState()
把data变成响应式后,就会执行dom挂载vm.$mount(vm.$options.el)
- 即在
-
vm.$mount
- 有两种版本,runtime版和runtime+compiler版本,但最终都会调用 public mount method 的$mount
-
初始化渲染过程
- 执行
vm.$mount(vm.$options.el)
方法
- 如果是runtime版本就是直接调用 mountComponent(this, el, hydrating) 方法
- 如果是runtime+compiler版本(即传入new Vue()的参数对象中不存在render方法)就会先处理template,将template通过 compileToFunctions(template, options) 函数编译成render方法,然后调用 mountComponent(this, el, hydrating) 方法
- 执行 mountComponent(this, el, hydrating) 函数
- 实例化render watcher即并把updateComponent=()=>{vm._update(vm._render(), hydrating)}函数作为new Watcher(vm, updateComponent, noop,{})的第二个参数传入
- new Watcher(vm, updateComponent, noop,{})
- 执行get()方法
- 在get方法中将当前watcher赋值给Dep.target
- 在get方法中执行updateComponent方法,从而执行vm._update(vm._render(), hydrating)方法
- vm._update(vm._render(), hydrating)
- vm._render()会把template转成 vnode
- vm._update()则会把vnode挂载到真正的dom上,渲染出页面
- 执行
源码
src/core/instance/index.js
src/core/instance/index.js
---
function Vue (options) {
this._init(options)
}
- src/core/instance/init.js
src/core/instance/init.js
---
Vue.prototype._init = function (options?: Object) { // _init方法初始化
initState(vm)
if (vm.$options.el) {
vm.$mount(vm.$options.el)
// mount方法 - 负责页面的挂载
// 传入el,el是 new Vue({el: '#app'}) 这里的el
// vm.$mount(vm.$options.el)
// 最终就是调用 mountComponent(this, el, hydrating) 方法
}
}
- src/platforms/web/entry-runtime-with-compiler.js
总结: vm.$mount() 的作用就是:调用 mountComponent(this, el, hydrating) 方法
src/platforms/web/entry-runtime-with-compiler.js
总结:
- vm.$mount(vm.$options.el) 的作用就是:调用 mountComponent(this, el, hydrating) 方法
---
const mount = Vue.prototype.$mount
// mount
// mout的主要作用:缓存 runtime 版本的 $mount,代码如下:
// mount = Vue.prototype.$mount = function (
// el?: string | Element,
// hydrating?: boolean
// ): Component {
// el = el && inBrowser ? query(el) : undefined
// return mountComponent(this, el, hydrating)
// }
Vue.prototype.$mount = function (
// 这里是runtime+compiler版本的$mount方法
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
// query(el)
// 主要作用:就是将el转成Element节点
// 具体是:
// 1. 是字符串并且存在,就查找id对应的dom
// 2. 是字符串但是dom没有对应的元素,开发环境会抛出警告,然后就创建一个空的div返回
// 3. 不是字符串,而是dom元素直接返回
// function query (el: string | Element): Element {
// if (typeof el === 'string') {
// const selected = document.querySelector(el)
// if (!selected) {
// process.env.NODE_ENV !== 'production' && warn(
// 'Cannot find element: ' + el
// )
// return document.createElement('div')
// }
// return selected
// } else {
// return el
// }
// }
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to or - mount to normal elements instead.`
)
return this
// el不能是body或html标签,因为会被覆盖
}
const options = this.$options
// resolve template/el and convert to render function
if (!options.render) {
let template = options.template
// new Vue(options)时,options中没有render方法,就会去看是不是有template属性
if (template) {
if (typeof template === 'string') {
// -------------------------------------------- template在options对象参数中存在,并且是字符串
if (template.charAt(0) === '#') {
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} else if (template.nodeType) {
// -------------------------------------------- template在options对象参数中存在,是dom结构
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
// ---------------------------------------------- template在options对象参数中不存在,就寻找el,el存在
template = getOuterHTML(el)
// 获取el对应的dom,并且赋值给template
}
// 其实上面一大段 if (template) 都是在处理 template
if (template) {
// 此时的template是经过上面处理过后的template
/* istanbul ignore if */
// mark直接滤过
// if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// mark('compile')
// }
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this)
// compileToFunctions
// 主要就是将模板 template 编译成 render 函数
options.render = render
options.staticRenderFns = staticRenderFns
// 将render函数挂载到$options上
/* istanbul ignore if */
// if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
// mark('compile end')
// measure(`vue ${this._name} compile`, 'compile', 'compile end')
// }
}
}
return mount.call(this, el, hydrating)
// 调用上面缓存的 mount 方法
// mount 中会调用 mountComponent(this, el, hydrating)
// mount = Vue.prototype.$mount = function (
// el?: string | Element,
// hydrating?: boolean
// ): Component {
// el = el && inBrowser ? query(el) : undefined
// return mountComponent(this, el, hydrating)
// }
// 这里执行 mount 方法时,还会再次执行 query(el) 方法,多了一次,只是runtime版本考虑的
}
- src/core/instance/lifecycle.js
src/core/instance/lifecycle.js
---
export function mountComponent (
vm: Component,
el: ?Element,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode
...
}
callHook(vm, 'beforeMount') // ------------------------ beforeMount 生命周期钩子
let updateComponent
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
...
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
// updateComponent 的赋值
// 赋值:是在这里赋值的
// 执行:是触发了响应式的 set 方法,调用watcher.update()方法时执行的
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher 这里的watcher就是渲染watcher*/)
// watcher分为三种
// 1. computed watcher
// 2. normal watcher 用户自定义的 watcher - 即 watch 对象中的方法
// 3. render watcher
// wathcer的执行顺序是固定的
// computed watcher -> normal watcher -> render watcher
// 这样就能保证在渲染时能拿到最新的 computed 和 执行了 watche 中定义的函数
hydrating = false
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted') // ------------------------ mounted 生命周期钩子
}
return vm
}
- src/core/observer/watcher.js
src/core/observer/watcher.js
---
export default class Watcher {
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {
// 如果是渲染watcher - renderWatcher
vm._watcher = this
// 就在组件实例上添加 _watcher 属性,值就是该renderWatcher实例
}
vm._watchers.push(this)
// 向 _watchers 数组中添加该renderWatcher
if (typeof expOrFn === 'function') {
this.getter = expOrFn
// expOrFn 是函数就赋值给 getter
// 1. 如果是renderWatcher的话 this.getter = updateComponent方法
// updateComponent 方法返回 vm._update(vm._render(), hydrating)
} else {
...
}
this.value = this.lazy
? undefined
: this.get()
// lazy属性只有 computed Watcher 才有
// 1. render watcher 就会调用 get() 方法并赋值给 this.value
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get () {
pushTarget(this)
// pushTarget 两个作用
// 1. 向 targetStack 数组中添加该 watcher,这里是render watcher
// 2. Dep.target = this 将该render watcher 赋值给 Dep.target,表示正在执行的是渲染watcher
let value
const vm = this.vm
try {
value = this.getter.call(vm, vm)
// 调用 getter 函数
// 这里因为是渲染watcher,所以执行的是 updateComponent
// updateComponent = vm._update(vm._render(), hydrating)
// 注意这里是重点
// 执行 vm._update(vm._render(), hydrating)
// 1. 因为在执行 vm._update 方法的过程中,会获取响应式data中的属性,触发get进行依赖收集
// 2. vm._render() 将template转成 vnode
// 2. vm._update() 对比后将 vnode 转成真正的 DOM
} catch (e) {
if (this.user) {
handleError(e, vm, `getter for watcher "${this.expression}"`)
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
}
参考
详细 - 目前能找到的写得最好的 https://juejin.im/post/6844903648623853581
三种 watcher https://juejin.im/post/6844904128435470350
大滴滴 https://juejin.im/post/6844903450455588871