导航
[[深刻 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 存在,就初始化 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<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/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<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 // 以后 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) 初始化渲染
(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 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/684490…
三种 watcher https://juejin.im/post/684490…
大滴滴 https://juejin.im/post/684490…