导航
[[深刻01] 执行上下文](https://juejin.im/post/684490...)
[[深刻02] 原型链](https://juejin.im/post/684490...)
[[深刻03] 继承](https://juejin.im/post/684490...)
[[深刻04] 事件循环](https://juejin.im/post/684490...)
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...)
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490...)
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...)
[[深刻08] 前端平安](https://juejin.im/post/684490...)
[[深刻09] 深浅拷贝](https://juejin.im/post/684490...)
[[深刻10] Debounce Throttle](https://juejin.im/post/684490...)
[[深刻11] 前端路由](https://juejin.im/post/684490...)
[[深刻12] 前端模块化](https://juejin.im/post/684490...)
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490...)
[[深刻14] canvas](https://juejin.im/post/684490...)
[[深刻15] webSocket](https://juejin.im/post/684490...)
[[深刻16] webpack](https://juejin.im/post/684490...)
[[深刻17] http 和 https](https://juejin.im/post/684490...)
[[深刻18] CSS-interview](https://juejin.im/post/684490...)
[[深刻19] 手写Promise](https://juejin.im/post/684490...)
[[深刻20] 手写函数](https://juejin.im/post/684490...)
[[react] Hooks](https://juejin.im/post/684490...)
[[部署01] Nginx](https://juejin.im/post/684490...)
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...)
[[部署03] gitlab-CI](https://juejin.im/post/684490...)
[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...)
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...)
[[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...)
[[源码] Redux React-Redux01](https://juejin.im/post/684490...)
[[源码] axios ](https://juejin.im/post/684490...)
[[源码] vuex ](https://juejin.im/post/684490...)
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...)
[[源码-vue02] computed 响应式 - 初始化,拜访,更新过程 ](https://juejin.im/post/684490...)
[[源码-vue03] watch 侦听属性 - 初始化和更新 ](https://juejin.im/post/684490...)
[[源码-vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490...)
[[源码-vue05] Vue.extend ](https://juejin.im/post/684490...)
[[源码-vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790...)
前置常识
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响应式
<font color=blue size=5>data响应式具体过程</font>
- 在
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步中的
data是对象
this.walk(value)
- 给data增加
this.walk(value)
defineReactive(obj, keys[i])
defineReactive(obj, keys[i])
Object.defineProperty
get
- dep.depend()
set
- dep.notify()
- 在
- 源码
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存在,就初始化 propsif (opts.methods) initMethods(vm, opts.methods)// methods存在,就初始化 methodsif (opts.data) { // data存在,初始化 data initData(vm)} else { // data不存在 observe(vm._data = {}, true /* asRootData */)}if (opts.computed) initComputed(vm, opts.computed) // computed存在,初始化 computedif (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在不是组件的状况下,是合并后的optionsdata = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}// data是一个函数就调用取返回值,是对象就间接赋值 // vm._data = vm.$options.dataif (!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 instanceconst keys = Object.keys(data)const props = vm.$options.propsconst methods = vm.$options.methodslet i = keys.lengthwhile (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 | voidif (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 $dataconstructor (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<string>) { // 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<any>) { 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/settersconst getter = property && property.getconst setter = property && property.setif ((!getter || setter) && arguments.length === 2) { val = obj[key]}let childOb = !shallow && observe(val)// 持续观测对象的每一项的value值,如果还是对象就持续察看 增加响应Object.definePropertyObject.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// 即每次执行都自增1subs: Array<Watcher>;// 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// 以后watcherconst 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) 初始化渲染
(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)
- 即在
<font color=red>vm.$mount</font>
- 有两种版本,runtime版和runtime+compiler版本,但最终都会调用 public mount method 的$mount
<font color=blue size=5>初始化渲染过程</font>
执行
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 <html> or <body> - mount to normal elements instead.` ) return this // el不能是body或html标签,因为会被笼罩}const options = this.$options// resolve template/el and convert to render functionif (!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 = elif (!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 hookif (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/684490...
三种 watcher https://juejin.im/post/684490...
大滴滴 https://juejin.im/post/684490...