navigation

[Deep 01] Execution context [Deep 02] Prototype chain [Deep 03] Inheritance [Deep 04] Event loop [Deep 05] Curri Bias function [Deep 06] Function memory [Deep 07] Implicit conversions and operators [Deep 07] Browser caching mechanism (HTTP caching mechanism) [Deep 08] Front-end security [Deep 09] Deep copy [Deep 10] Debounce Throttle [Deep 10] Front-end routing [Deep 12] Front-end modularization [Deep 13] Observer mode Publish subscribe mode Bidirectional data binding [Deep 14] Canvas [Deep 15] webSocket Webpack HTTP and HTTPS CSS- Interview Handwriting Promise

[react] Hooks

[Deployment 01] Nginx [Deployment 02] Docker deployVue project [Deployment 03] gitlab-CI

[source code – Webpack01 – precompiler] AST abstract syntax tree [source code – Webpack02 – Precompiler] Tapable [source code – Webpack03] hand written webpack-compiler simple compilation process [source code] Redux React-redux01 [source] Axios [source] vuex [source -vue01] Data reactive and initialize render [source -vue02] Computed responsive – Initialize, access, Update Procedure [source -vue04] Watch Listening properties – Initialize and update [source -vue04] vue. set and vm.$set [source -vue05] vue.extend

Vue. NextTick and VM.$nextTick

Front knowledge

Watcher class

  • Watcher falls into three categories
    • computed watcher – Responsible for updating the computed
    • user-watcher – Watch a function
    • render-watcher – To render
  • The three types of watcher have a fixed order of execution
    • computed watcher -> user watcher -> render watcher
    • The reason for this order of three watcher?
      • Computed Watcher executes in front of Render Watcher, ensuring that the view is rendered with the latest computed

(1) Data response

  • Data response specific process

    1. innew Vue(options)Constructorthis._init(options)Method, andthis._init(options)Method is ininitMixin(Vue)Defined in the
    2. Vue.prototype._init=> Here is the main concerninitState(vm)
      • Merge options objects
      • Call initProxy
      • Call initState (vm)
      • Initialization of other data
      • vm.$mount(vm.$options.el)Initialize rendering
    3. initState(vm)=> Here is the main concerninitData
      • initProps
      • initMethods
      • initData
      • initComputed
      • initWatch
    4. initData
      • If the data of the options object is passed in, it is called to return the object. If it is an object, it is used directly
      • — Props, method, and data (props, method, and data) with the same key
      • proxy(vm,_data, key)
        • The main function is to provide a layer of proxy for attributes in data
        • By visitingthis.name = vm.name = vm._data.name = vm.$options.data.name = vm.data.name
      • observe(data, true)
    5. observe(data, true)
      • Determine whether data has__ob__Property that indicates whether the data has been observed and therefore has a response
      • There is no__ob__Properties, just observe, executenew Observer(value)
    6. new Observer(value)
      • Add the data__ob__Property whose value is currentobserverThe instance
      • The data is an array
        • Implement protoAugment(Value, arrayMethods) with prototype
          • Override the seven methods on the array prototype
          • Push pop unshift Shift splice sort reverse
          • Push unshift splice adds properties wrapped in arrays
              1. Continue to performob.observeArray(inserted)Loop through each of the added properties, performing the step in step 5observe(items[i])
              1. And call theob.dep.notify()Perform a watcher upate to update the view so that the overridden array methods are responsive
        • Perform copyAugment(Value, arrayMethods, arrayKeys) without prototype
        • this.observeArray(value)
          • Loop through each member of the array, performing the step in step 5observe(items[i])
      • The data is an object
        • this.walk(value)
    7. this.walk(value)
      • defineReactive(obj, keys[i])
    8. defineReactive(obj, keys[i])
      • Object.defineProperty
        • get
          • dep.depend()
        • set
          • dep.notify()
  • The source code

  • initState – src/core/instance/state.js

initState - src/core/instance/state.js --- export function initState (vm: $options // opts to get props (vm, props, props, props, props, props, props, props, props, props, props, props, props Opts.props)// props if (opts.methods) initMethods(vm, opts.methods)// methods exist, Initialize methods if (opts.data) {// data exists, Initialize data initData(VM)} else {// data does not exist Observe (vm._data = {}, /* asRootData */)} if (opts.computed) initComputed(VM, opts.computed) // Computed, Initialize computed IF (opts.watch && opts.watch! == nativeWatch) {// Watch exists and is not a native object's watch property, Render watcher // compute watcher // user watcher - watch initWatch(vm, opts.watch)}}Copy the code
  • initData – src/core/instance/state.js
initData - src/core/instance/state.js --- function initData (vm: Component) {let data = vm.$options.data // Get the data property on the options object passed in // Note: $options = vm._data = typeof data === 'function'? GetData (data, vm) : data | | {} / / data take the return value is a function is called, is the object is direct assignment / / vm _data while forming = vm. $options. The data if (! IsPlainObject (data)) {// Not an object, not a pure object assignment null 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) }} 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 ', vm // props can't have the same key as data)} else if (! IsReserved (key)) {// props and methods don't exist, Proxy (VM, '_data', key)}} // observe data // data response observe(data, true /* asRootData */)}Copy the code
  • 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. // 2. Return this._data[key] // 3. SharedPropertyDefinition. The get method is through the vm. The key to call, This points to the vm} sharedPropertyDefinition. Set = function proxySetter (val) {this [sourceKey] [key] = val / / 1. // 2.this._data [key] = val} object.defineProperty (target, key, SharedPropertyDefinition) // vm[key] = vm._data[key] // Because: vm._data = vm.$options.data vm[key] = vm._data[key] = vm.$options.data[key] }Copy the code
  • 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) {/ / is not an object or a VNode return} let ob: Observer | void if (hasOwn(value, __ob__ instanceof Observer) {// if the value has the __ob__ attribute and __ob__ is an instanceof the Observer, Ob = value.__ob__} else if (shouldObserve &&! isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && ! _isVue) {ob = new Observer(value) // generate an OB instance} if (asRootData && ob) {// if the root data is the data passed when new Vue() is initialized // and Ob.vmcount ++} return ob}Copy the code
  • 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: Def (value, '__ob__') {this.dep = new dep () def(value, '__ob__'); This) // def // add (__ob__) attribute to (value), value is (observer) instance // function def (obj: Object, key: string, val: any, enumerable?: boolean) { // Object.defineProperty(obj, key, { // value: val, // enumerable: !! enumerable, // writable: true, // configurable: //} if (array.isarray (value)) {if (hasProto) {// const hasProto = '__proto__' in {} // And has the prototype attribute __proto__ protoAugment(value, // value.__proto__ = arrayMethods // arrayMethods // const arrayMethods = Object.create(arrayProto) // const arrayProto = Array.prototype // def(arrayMethods, method, function mutator (... Args){}) // override 7 methods on the array prototype} else {// is an array, // function Augment(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 / / / / observation array (value) 1. Loop through each item of the array, Observe (items[I])} else {this.walk(value) // Observe the 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]) } } }Copy the code
  • 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])
    }
  }
Copy the code
  • 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.6464x === false) {// The property description object does not exist or cannot be modified, Simply return return} / / Object. GetOwnPropertyDescriptor (obj, // 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) 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 () // Loop to collect dependencies if (array.isArray (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 () / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- notify watcher update}})}Copy the code
  • Dep class – the SRC/core/observer/Dep. Js
Dep class -src /core/observer/ de.js -- export default class Dep {static target:? Watcher; // target a static attribute of watcher type id: number; Subs: Array<Watcher>; subs: Array<Watcher>; // constructor wacher constructor () {this.id = UI ++ // id++ UI ++ ++ for each execution +1 this.subs = [] // initialize to an empty array, } addSub (sub: watcher) {this.subs.push(sub)} removeSub (sub: watcher) Watcher) { remove(this.subs, Sub) / / delete the watcher} depend () {if (Dep., target) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- focus on Dep. The target / / Dep.target is the currently executing watcher that is being computed and exists in the closure. Equivalent to a global variable / / in the following initialization Dep. Target. AddDep (this) / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Dep and Watcher / / relationship Dep.target (this) addDep(this) addDep(this) addDep(this) addDep(this) addDep(this) addDep(this) addDep(this AddDep (dep: dep) {// const id = dep.id // id attribute of dep instance // if (! This.newdepids.has (id)) {// ------------ newDepIds does not exist // this.newdepids.add (id) // Add the ID to newDepIds (Watcher class) // this.newdeps.push (dep) // add dep to newDeps // if (! This. DepIds. From the (id)) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- depIds does not exist in the id / / dep. AddSub (this) / / to the subs (dep) add the watcher / /} / /} // target. AddDep (this) // 1. Call dep.target.adddep (this) = new Watcher().adddep (this) // 2. Add dep instances to watcher's newDeps array // 3. Add dep.id to watcher's newDepIds array // 4. }} notify () {const subs = this.subs.slice() // Make a shallow copy of subs array, 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() // execute the update method on each member of the subs array watcher}}} dep. target = null // Current watcher const targetStack = [] Export function pushTarget (target:? Export function popTarget () {targetstack.push (target)} export function popTarget () { Dep. Target = targetStack[targetStack.length - 1] // previous watcher}Copy the code
  • Watcherç±» – src/core/observer/dep.js
Export the default class Watcher {constructor (vm: Component, / / vm instances expOrFn: string | Function, cb: the Function, the options? :? Object, isRenderWatcher? : {this.vm = vm if (isRenderWatcher) {// isRenderWatcher, _watcher Vm. _watcher = this} vm. _watcher. push(this) // push // Parse expression for getter if (typeof expOrFn === 'function') {this.getter = expOrFn // 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; This.newdepids.has (id)) {// ------------ newDepIds does not have the id this.newdepids.add (id) // Add the ID to newDepIds (Watcher class) This.newdeps.push (dep) // Add dep if (! This.depids.has (id)) {// ------------- depIds does not exist dep.addsub (this) // Add watcher to subs (dep class)}}} update () {/* Istanbul ignore else */ if (this.lazy) {// for computed watcher this.dirty = true} else if (this.sync) {// // nextTick(flushSchedulerQueue) : flushSchedulerQueue: flushSchedulerQueue: flushSchedulerQueue: flushSchedulerQueue: flushSchedulerQueue;Copy the code

(2) Initialize rendering

(2-1) Different builds of VUE

  • Independent build (including template compilation process) –The runtime + compiler full version
    • Rendering process:Template -> render function -> vnode -> real DOM
  • Run time build (not including the template compilation process) –The runtime version
    • Rendering process:Render function -> vnode -> real DOM
  • The files in the dist folder will be different after packaging
    • A full version built independentlyvue.js
    • Runtime versionvue.runtime.js
      • The runtime version is approximately 30% smaller than the full version

(2-2) Dom initialization rendering –vm.$mount(vm.$options.el)

  • New Vue(options) => this._init(options) => vue.prototype._init => initState(VM) => vm.$mount(vm.$options.el)

    • In thethis._initInit firstinitState()After the data is made responsive, the DOM mount is performedvm.$mount(vm.$options.el)
  • vm.$mount

    • There are two versions, the Runtime version and the Runtime + Compiler version, but they all end up calling $mount for the public mount method
  • Initialize the render process

    1. performvm.$mount(vm.$options.el)methods
    • The Runtime version calls the mountComponent(this, el, Hydrating) method directly
    • If the runtime+compiler version is passed to new Vue() and the render method does not exist, the template is processed first. Compile the template into the render method via compileToFunctions(template, options) and then call the mountComponent(this, el, hydrating) method
    1. Execute mountComponent(this, el, hydrating)
    • UpdateComponent =()=>{vm._update(vm._render(), hydrating)} function as new watcher (vm, updateComponent, Noop,{}) is passed as the second argument
    1. new Watcher(vm, updateComponent, noop,{})
    • Execute the get() method
      • Assign the current watcher to dep.target in the get method
      • Execute the vm._update(vm._render(), hydrating) method by executing the updateComponent method in the GET method
    1. vm._update(vm._render(), hydrating)
    • Vm._render () converts the template to a vNode
    • Vm._update () mounts the VNode to the real DOM and renders the page
  • The source code

  • src/core/instance/index.js

src/core/instance/index.js
---

function Vue (options) {
  this._init(options)
}
Copy the code
  • src/core/instance/init.js
src/core/instance/init.js --- Vue.prototype._init = function (options? : If (vm.$options.el) {vm.$options.el) {// mount(vm.$options.el) // mount(vm.$options.el) {el: '#app'} // vm.$mount(vm.$options. El) // Invoke mountComponent(this, el, hydrating)}}Copy the code
  • src/platforms/web/entry-runtime-with-compiler.js

$mount() calls the mountComponent(this, el, hydrating) method

SRC /platforms/web/entry-runtime-with-compiler.js -vm.$mount(vm.$options.el) $mountComponent(this, el, hydrating) = const mount = vue.prototype.$mount // mount // mout // mount = vue.prototype.$mount = function (// el? : string | Element, // hydrating? : boolean // ): Component { // el = el && inBrowser ? query(el) : undefined // return mountComponent(this, el, $mount = function (// here is runtime+ Compiler version $mount = el? : string | Element, hydrating? : Boolean): Component {el = el && query(el) // Query (el) // Component {el = el && query(el) // Query (el) // 2. If it is a string but dom has no corresponding element, the development environment will raise a warning, and then create an empty div and return // 3. Instead of string, dom Element returned directly / / 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 // } const options = this.$options // resolve template/el and convert to render function if (! Options. render) {let template = options.template // new Vue(options) Will go to see whether have the template attribute the if (template) {if (typeof template = = = 'string') {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - Template exists in the options object argument, 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) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the template in the options object parameters, Template = template.innerhtml} else {if (process.env.node_env! == 'production') { warn('invalid template option:' + template, This)} return this}} else if (el) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the template does not exist in the options object parameters, is looking for el, Template = getOuterHTML(el) And assigned to the template} / / in fact, a large section of the above the if (template) are in dealing with the template if (template) {/ / at this point after the template is treated with the above template / * Istanbul // 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 / / main template template is compiled to render function options. The render = render options. StaticRenderFns = /* 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, // Mount will call mountComponent(this, el,) hydrating) // mount = Vue.prototype.$mount = function ( // el? : string | Element, // hydrating? : boolean // ): Component { // el = el && inBrowser ? query(el) : }}}}}}}}}}}}}}}}Copy the code
  • 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 hooks let updateComponent / * Istanbul ignore if * / if (process.env.NODE_ENV ! == 'production' && config.performance && mark) { ... } else {updateComponent = () => {vm._update(vm._render(), hydrating)} else {vm._update(vm._render(), hydrating)} } new watcher (vm, updateComponent, noop, {before () {if (vm._ismounted &&! vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } }, True /* isRenderWatcher The watcher is a rendering watcher*/) // There are three types of watcher // 1. Computed Watcher // 2 Render watcher // computed watcher -> Normal watcher -> render // Manually mounted instance, // manually mounted instance, // 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}} and return the vmCopy the code
  • 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) {// add _watcher to the component instance if it isRenderWatcher - renderWatcher vm Properties, } vm._watcher.push (this) // Add renderWatcher to _Watchers array if (typeof expOrFn === 'function') { }}}}}}}}}}}}}}}} this.getter = expOrFn // expOrFn RenderWatcher this. Getter = updateComponent method // updateComponent method returns vm._update(vm._render(), hydrating) } else { ... } this.value = this.lazy ? undefined : Render Watcher calls the get() method and assigns a value to this.value} /** * Evaluate the Getter, and re-collect dependencies. */ get() {pushTarget(this) {pushTarget(this) {// 1. Add the watcher to the targetStack array, here is render watcher // 2.dep. target = this assign the render watcher to dep. target, Let value const vm = this.vm try {value = this.getter.call(vm, vm) // Call the getter.call(vm, vm) UpdateComponent = vm._update(vm._render(), // Execute vm._update(vm._render(), hydrating) // 1. Because during the execution of the vm._update method, the properties in the reactive data are retrieved, } catch (e) {if (this.user) {if (this.user) {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 } }Copy the code

reference

Detailed – the best written juejin.cn/post/684490 available today… Three kinds of watcher juejin. Cn/post / 684490… Big drops juejin. Cn/post / 684490…