Navigation
[[ thorough 01] Execution context ](https://juejin.im/post/684490...)
[[ thorough 02] Prototype chain ](https://juejin.im/post/684490...)
[[ thorough 03] Inherit ](https://juejin.im/post/684490...)
[[ thorough 04] The event loop ](https://juejin.im/post/684490...)
[[ thorough 05] currying Partial function Function memory ](https://juejin.im/post/684490...)
[[ thorough 06] Implicit conversion and Operator ](https://juejin.im/post/684490...)
[[ thorough 07] Browser caching mechanism (http Caching mechanisms )](https://juejin.im/post/684490...)
[[ thorough 08] Front end security ](https://juejin.im/post/684490...)
[[ thorough 09] Depth copy ](https://juejin.im/post/684490...)
[[ thorough 10] Debounce Throttle](https://juejin.im/post/684490...)
[[ thorough 11] Front-end routing ](https://juejin.im/post/684490...)
[[ thorough 12] Front-end modularization ](https://juejin.im/post/684490...)
[[ thorough 13] Observer mode Publish subscribe mode Two way data binding ](https://juejin.im/post/684490...)
[[ thorough 14] canvas](https://juejin.im/post/684490...)
[[ thorough 15] webSocket](https://juejin.im/post/684490...)
[[ thorough 16] webpack](https://juejin.im/post/684490...)
[[ thorough 17] http and https](https://juejin.im/post/684490...)
[[ thorough 18] CSS-interview](https://juejin.im/post/684490...)
[[ thorough 19] Handwriting Promise](https://juejin.im/post/684490...)
[[ thorough 20] Handwritten functions ](https://juejin.im/post/684490...)
[[react] Hooks](https://juejin.im/post/684490...)
[[ Deploy 01] Nginx](https://juejin.im/post/684490...)
[[ Deploy 02] Docker Deploy vue project ](https://juejin.im/post/684490...)
[[ Deploy 03] gitlab-CI](https://juejin.im/post/684490...)
[[ Source code -webpack01- Pre knowledge ] AST Abstract syntax tree ](https://juejin.im/post/684490...)
[[ Source code -webpack02- Pre knowledge ] Tapable](https://juejin.im/post/684490...)
[[ Source code -webpack03] Handwriting webpack - compiler Simple compilation process ](https://juejin.im/post/684490...)
[[ Source code ] Redux React-Redux01](https://juejin.im/post/684490...)
[[ Source code ] axios ](https://juejin.im/post/684490...)
[[ Source code ] vuex ](https://juejin.im/post/684490...)
[[ Source code -vue01] data Response type and Initialize rendering ](https://juejin.im/post/684490...)
[[ Source code -vue02] computed Response type - initialization , visit , The update process ](https://juejin.im/post/684490...)
[[ Source code -vue03] watch Listening properties - Initialization and update ](https://juejin.im/post/684490...)
[[ Source code -vue04] Vue.set and vm.$set ](https://juejin.im/post/684490...)
[[ Source code -vue05] Vue.extend ](https://juejin.im/post/684490...)
[[ Source code -vue06] Vue.nextTick and vm.$nextTick ](https://juejin.im/post/684790...)
Pre knowledge
Watcher class
Watcher Divided into three
- computed watcher -
Is responsible for updating computed
- user-watcher -
watch A function
- render-watcher -
To render
- computed watcher -
Three watcher There is a fixed execution sequence
computed watcher -> user watcher -> render watcher
There are three kinds of watcher The reason for the order of ?
- computed watcher stay render watcher Go ahead , You can ensure that you get the latest when rendering the view computed
(1) data Response type
<font color=blue size=5>data Responsive specific process </font>
- stay
new Vue(options)
Invocation in constructorthis._init(options)
Method , andthis._init(options)
The method is ininitMixin(Vue)
As defined in Vue.prototype._init
=> The main concern here isinitState(vm)
- Merge options object
- call initProxy
- call initState(vm)
- Initialization of other data
vm.$mount(vm.$options.el)
Initialize rendering
initState(vm)
=> The main concern here isinitData
- initProps
- initMethods
- initData
- initComputed
- initWatch
initData
- Incoming options Object's data, If it is a function call, it returns the object , If it's an object, use it directly
- If props,method,data There is the same in key Value throws a warning
proxy(vm,
_data, key)
- The main function is to give data The properties in are used as a layer of proxy
- By visiting
this.name = vm.name = vm._data.name = vm.$options.data.name = vm.data.name
observe(data, true)
observe(data, true)
- Judge data Whether they have
__ob__
attribute , The attribute represents data Have you observed , That is, it has a response - No,
__ob__
attribute , Just observe , performnew Observer(value)
- Judge data Whether they have
new Observer(value)
- to data add to
__ob__
attribute , Value is currentobserver
example data It's an array
With prototype execution protoAugment(value, arrayMethods)
- Rewrite the... On the array prototype 7 Methods
- push pop unshift shift splice sort reverse this 7 Both change the array
push unshift splice Wrap the added attributes into an array
-
- Carry on
ob.observeArray(inserted)
Loop through each attribute added , Execution section 5 In stepobserve(items[i])
- Carry on
-
- And call
ob.dep.notify()
perform watcher Species upate To update the view , Instead, these overridden array methods are responsive
- And call
-
- No prototype execution copyAugment(value, arrayMethods, arrayKeys)
this.observeArray(value)
- Loop through each member of the array , Execution section 5 In step
observe(items[i])
- Loop through each member of the array , Execution section 5 In step
data It's the object
this.walk(value)
- to data add to
this.walk(value)
defineReactive(obj, keys[i])
defineReactive(obj, keys[i])
Object.defineProperty
get
- dep.depend()
set
- dep.notify()
- stay
- Source code
initState - src/core/instance/state.js
initState - src/core/instance/state.js --- export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options // opts obtain vm Medium options Parameters if (opts.props) initProps(vm, opts.props) // props There is , Just initialize props if (opts.methods) initMethods(vm, opts.methods)// methods There is , Just initialize methods if (opts.data) { // data There is , initialization data initData(vm) } else { // data non-existent observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) // computed There is , initialization computed if (opts.watch && opts.watch !== nativeWatch) { // watch There is , And not on native objects watch attribute , Just initialize watch // Divided into three 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 // Get incoming options On the object data attribute // Be careful : there vm.$options In the case of non components , It's a merged options data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {} // data Is a function call to get the return value , If it is an object, it is directly assigned // vm._data = vm.$options.data if (!isPlainObject(data)) { // Not an object , Not a pure object assignment, empty object 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 The... Exists in key, therefore data There can no longer be the same 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 The... Exists in key, therefore data There can no longer be the same key ) } else if (!isReserved(key)) { // props and methods There is no such thing as key, Just execute the agent proxy function proxy(vm, `_data`, key) } } // observe data // data In response 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. rewrite get // 2. return this._data[key] // 3. this Point to :sharedPropertyDefinition.get The way is through vm.key To invoke the ,this Point to vm } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val // 1. rewrite set // 2. this._data[key] = val } Object.defineProperty(target, key, sharedPropertyDefinition) // vm[key] = vm._data[key] // because : vm._data = vm.$options.data // therefore :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) { // Not an object perhaps yes VNode return } let ob: Observer | void if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { // If value have __ob__ attribute also __ob__ yes Observer Example , Just assign it directly // That is, it has been observed , Direct assignment ob = value.__ob__ } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value) // Generate a ob example } if (asRootData && ob) { // If it's a root data namely new Vue() It was passed in during initialization data // also ob There is // vmCount++ // That is, count the number of times generated 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 // Assign the passed in object this.dep = new Dep() // dep example this.vmCount = 0 def(value, '__ob__', this) // def // to ( value ) add to ( __ob__ ) attribute , The value is ( observer ) example // 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 {} // It's an array , And it has prototype properties __proto__ protoAugment(value, arrayMethods) // protoAugment Rewriting prototype // value.__proto__ = arrayMethods // arrayMethods // const arrayMethods = Object.create(arrayProto) // const arrayProto = Array.prototype // def(arrayMethods, method, function mutator (...args){}) // rewrite 7 A method on the array prototype } else { // It's an array , And it doesn't have a prototype , Copy every attribute on the prototype 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) // Observation array // 1. Loop through each item of the array , Carry out... For each item observe(items[i]) } else { this.walk(value) // Observation object } } /** * 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) { // non-existent perhaps Property description object cannot be modified , Go straight back return } // Object.getOwnPropertyDescriptor(obj, key) Said to get obj.key Property describes the object // 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) // Continue to observe each item of the object value value , If it's still an object, continue to observe Add response 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() // Circular collection dependency if (Array.isArray(value)) { // If each item key Object's value Is an array dependArray(value) // Collection dependency } } } 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() // ---------------------------- notice watcher Perform the update } }) }
Dep class - src/core/observer/dep.js
Dep class - src/core/observer/dep.js --- export default class Dep { static target: ?Watcher; // target One watcher Static properties of type id: number; // Every time new All make id+1 // That is, each execution is self incremented 1 subs: Array<Watcher>; // subs Array For storage wacher constructor () { this.id = uid++ // id++ uid++ Add... Every time +1 this.subs = [] // Initialize to an empty array , Storage dependency is watcher } addSub (sub: Watcher) { this.subs.push(sub) // add to watcher } removeSub (sub: Watcher) { remove(this.subs, sub) // Delete watcher } depend () { if (Dep.target) { //--------------------------------------------- Focus on Dep.target // Dep.target Is currently executing and calculating watcher, Exist in closures , Equivalent to a global variable // There is initialization below Dep.target.addDep(this) // ------------------------------------ Dep and Watcher interrelation // Dep.target.addDep(this) // towards watcher Add dep example // this The parameter is dep example // Dep.target It's a calculation watcher // Watcher Medium addDep // addDep (dep: Dep) { // const id = dep.id // dep Example of id attribute // if (!this.newDepIds.has(id)) { // ------------ newDepIds There is no such thing as id // this.newDepIds.add(id) // towards ( Watcher class ) Of newDepIds Add id // this.newDeps.push(dep) // towards ( Watcher class ) Of newDeps Add dep // if (!this.depIds.has(id)) { // ------------- depIds There is no such thing as id // dep.addSub(this) // towards ( Dep class ) Of subs Add The watcher // } // } // } // The operation here is a little around // Dep.target.addDep(this) // 1. call Dep.target.addDep(this) = new Watcher().addDep(this) // 2. hold dep Instance added to watcher Of newDeps Array // 3. hold dep.id Add to watcher Of newDepIds Array // 4. perform dep.addSubs hold watcher Add to Dep Of subs Array } } notify () { const subs = this.subs.slice() // Shallow copy subs Array , Cache a copy 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() // perform subs Every member of the array watcher Upper update Method } } } Dep.target = null // At present watcher const targetStack = [] // Deposit watcher Stack structure of , Last in, first out export function pushTarget (target: ?Watcher) { targetStack.push(target) // watcher Push Dep.target = target // Assign the latest watcher } export function popTarget () { targetStack.pop() // watcher Out of the stack Dep.target = targetStack[targetStack.length - 1] // Previous watcher }
Watcher class - src/core/observer/dep.js
export default class Watcher { constructor ( vm: Component, // vm example expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean // Whether it is renderWatcher ) { this.vm = vm if (isRenderWatcher) { // yes renderWatcher, Then put the watcher Instance assigned to vm._watcher vm._watcher = this } vm._watchers.push(this) // push // parse expression for getter if (typeof expOrFn === 'function') { this.getter = expOrFn // Is a function assignment getter } else { // It's not a function 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 Example of id attribute if (!this.newDepIds.has(id)) { // ------------ newDepIds There is no such thing as id this.newDepIds.add(id) // towards ( Watcher class ) Of newDepIds Add id this.newDeps.push(dep) // towards ( Watcher class ) Of newDeps Add dep if (!this.depIds.has(id)) { // ------------- depIds There is no such thing as id dep.addSub(this) // towards ( Dep class ) Of subs Add The watcher } } } update () { /* istanbul ignore else */ if (this.lazy) { // be used for computed watcher this.dirty = true } else if (this.sync) { // Sync watcher this.run() } else { queueWatcher(this) // nextTick(flushSchedulerQueue) take watcher Put in queue , In the next round tick To update } } }
(2) Initialize rendering
(2-1) vue Different build versions of
Build... Independently ( Include template Compilation process ) -
runtime+compiler Full version
- Rendering process :
template -> render function -> vnode -> Actual dom
- Rendering process :
Build at run time ( barring template Compilation process ) -
runtime edition
- Rendering process :
render function -> vnode -> Actual dom
- Rendering process :
After packing dist The files in the folder will be different
- A full version built independently
vue.js
Runtime version
vue.runtime.js
- The runtime version is about... Smaller than the full version 30%
- A full version built independently
(2-2) dom Initial rendering of - vm.$mount(vm.$options.el)
perform
new Vue(options)
=>this._init(options)
=>Vue.prototype._init
=>initState(vm)
=>vm.$mount(vm.$options.el)
- That is to say
this._init
Initialize firstinitState()
hold data When it becomes responsive , Will execute dom mountvm.$mount(vm.$options.el)
- That is to say
<font color=red>vm.$mount</font>
- There are two versions ,runtime Version and runtime+compiler edition , But eventually it will call public mount method Of $mount
<font color=blue size=5> Initialize the render pass </font>
perform
vm.$mount(vm.$options.el)
Method- If it is runtime Version is a direct call to mountComponent(this, el, hydrating) Method
- If it is runtime+compiler edition ( The incoming new Vue() Does not exist in the parameter object of render Method ) Will deal with... First template, take template adopt compileToFunctions(template, options) The function is compiled to render Method , And then call mountComponent(this, el, hydrating) Method
perform mountComponent(this, el, hydrating) function
- Instantiation render watcher That is, and put updateComponent=()=>{vm._update(vm._render(), hydrating)} Function as new Watcher(vm, updateComponent, noop,{}) The second parameter of is passed in
new Watcher(vm, updateComponent, noop,{})
- perform get() Method
- stay get The current... In the method watcher Assign a value to Dep.target
- stay get Method execution updateComponent Method , To carry out vm._update(vm._render(), hydrating) Method
vm._update(vm._render(), hydrating)
- vm._render() Will be able to template Turn into vnode
- vm._update() It will put vnode Mount to the real dom On , Render the page
- Source code
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 Method initialization initState(vm) if (vm.$options.el) { vm.$mount(vm.$options.el) // mount Method - Be responsible for page mounting // Pass in el,el yes new Vue({el: '#app'}) there el // vm.$mount(vm.$options.el) // The end result is a call mountComponent(this, el, hydrating) Method } }
src/platforms/web/entry-runtime-with-compiler.js
summary : vm.$mount() Is that : call mountComponent(this, el, hydrating) Methodsrc/platforms/web/entry-runtime-with-compiler.js summary : - vm.$mount(vm.$options.el) Is that : call mountComponent(this, el, hydrating) Method --- const mount = Vue.prototype.$mount // mount // mout Main role of : cache runtime Version of $mount, The code is as follows : // 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 ( // Here is runtime+compiler Version of $mount Method el?: string | Element, hydrating?: boolean ): Component { el = el && query(el) // query(el) // The main role : Will be el Turn into Element node // The concrete is : // 1. Is a string and exists , Just look for it id Corresponding dom // 2. It's a string, but dom There is no corresponding element , The development environment will throw a warning , Then create an empty div return // 3. It's not a string , It is dom Element directly returns // 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 It can't be body or html label , Because it will be covered } const options = this.$options // resolve template/el and convert to render function if (!options.render) { let template = options.template // new Vue(options) when ,options There is no render Method , Will see if there is template attribute if (template) { if (typeof template === 'string') { // -------------------------------------------- template stay options There is... In the object parameter , And it's a string 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 stay options There is... In the object parameter , yes dom structure template = template.innerHTML } else { if (process.env.NODE_ENV !== 'production') { warn('invalid template option:' + template, this) } return this } } else if (el) { // ---------------------------------------------- template stay options Object parameter does not exist , Just look el,el There is template = getOuterHTML(el) // obtain el Corresponding dom, And assigned to template } // In fact, there is a large section above if (template) It's all dealing with template if (template) { // At this time template After the above treatment template /* istanbul ignore if */ // mark Direct filtration // 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 // The main thing is to put the template template Translate it into render function options.render = render options.staticRenderFns = staticRenderFns // take render Function mounted to $options On /* 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) // Call the cache above mount Method // mount Will call 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) // } // Execute here mount When the method is used , It will be executed again query(el) Method , One more time , It's just runtime Version considered }
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 Lifecycle hook let updateComponent /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { ... } else { updateComponent = () => { vm._update(vm._render(), hydrating) } // updateComponent Assignment // assignment : It's assigned here // perform : It triggered a responsive set Method , call watcher.update() Method } new Watcher(vm, updateComponent, noop, { before () { if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } }, true /* isRenderWatcher there watcher It's rendering watcher*/) // watcher Divided into three // 1. computed watcher // 2. normal watcher User defined watcher - namely watch Methods in objects // 3. render watcher // wathcer The order of execution is fixed // computed watcher -> normal watcher -> render watcher // This ensures that you can get the latest... When rendering computed and Yes watche The function defined in 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 Lifecycle hook } 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) { // If it's rendering watcher - renderWatcher vm._watcher = this // Just add... On the component instance _watcher attribute , Value is the right renderWatcher example } vm._watchers.push(this) // towards _watchers Add this to the array renderWatcher if (typeof expOrFn === 'function') { this.getter = expOrFn // expOrFn If it's a function, assign it to getter // 1. If it is renderWatcher Words this.getter = updateComponent Method // updateComponent Method returns vm._update(vm._render(), hydrating) } else { ... } this.value = this.lazy ? undefined : this.get() // lazy Properties are only computed Watcher Only then // 1. render watcher Will call get() Method and assign it to this.value } /** * Evaluate the getter, and re-collect dependencies. */ get () { pushTarget(this) // pushTarget Two functions // 1. towards targetStack Add this to the array watcher, Here is render watcher // 2. Dep.target = this Will be render watcher Assign a value to Dep.target, Indicates that the rendering is in progress watcher let value const vm = this.vm try { value = this.getter.call(vm, vm) // call getter function // Because it's rendering watcher, So the execution is updateComponent // updateComponent = vm._update(vm._render(), hydrating) // Notice here's the point // perform vm._update(vm._render(), hydrating) // 1. Because it's executing vm._update In the process of method , Will get the response data Properties in , Trigger get Do dependency collection // 2. vm._render() take template Turn into vnode // 2. vm._update() After comparison, it will vnode Turn into real 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 } }
Reference resources
detailed - The best written one you can find so far https://juejin.im/post/684490...
Three watcher https://juejin.im/post/684490...
Big Didi https://juejin.im/post/684490...