On the principle of vue

Posted by deckrdx on Mon, 28 Feb 2022 10:48:05 +0100

directory structure

├── benchmarks                  Performance and benchmarking
├── dist                        Build packaged output directory
├── examples                    Case catalogue
├── flow                        flow Syntax type declaration
├── packages                    Some additional packages, such as those responsible for server rendering vue-server-renderer,coordination vue-loader Used vue-template-compiler,also weex dependent
│   ├── vue-server-renderer
│   ├── vue-template-compiler
│   ├── weex-template-compiler
│   └── weex-vue-framework
├── scripts                     Storage location of all configuration files, such as rollup Configuration file for
├── src                         vue Source directory
│   ├── compiler                compiler
│   ├── core                    Core package of runtime
│   │   ├── components          Global components, such as keep-alive
│   │   ├── config.js           Some default configuration items
│   │   ├── global-api          overall situation API,For example, familiar: Vue.use(),Vue.component() etc.
│   │   ├── instance            Vue Instance related, such as Vue The constructor is in this directory
│   │   ├── observer            Responsive principle
│   │   ├── util                Tool method
│   │   └── vdom                fictitious DOM Relevant, such as familiar patch The algorithm is right here
│   ├── platforms               Platform related compiler code
│   │   ├── web
│   │   └── weex
│   ├── server                  Server rendering related
├── test                        Test catalog
├── types                       TS Type declaration

What does Vue's initialization process (new Vue(options)) do?

1. Processing component configuration items

When initializing the root component, the options are merged to merge the global configuration into the local configuration of the root component

When initializing each sub component, some performance optimizations are made, and some deep-seated attributes on the component configuration object are put into VM$ Options option to improve the efficiency of code execution

2. Initialize the relationship attributes of component instances, such as $parent, $children, $root, $refs, etc

3. Handle custom events

4. Call beforeCreate hook function

5. Initialize the inject configuration item of the component to obtain the configuration object in the form of ret[key] = val, then perform shallow response processing on the configuration object (only the first layer data of the object is processed), and proxy each key to the vm instance

6. Data response, processing props, methods, data, computed, watch and other options

7. Resolve the provide object on the component configuration item and mount it to VM_ On the provided property

8. Call the created hook function

9. If the el option is found on the configuration item, the $mount method will be called automatically, that is, with the el option, there is no need to call the $mount method manually. On the contrary, if the el option is not provided, the $mount method must be called

10. Then enter the mount phase

Summary: Vue initialization mainly involves several things: merging configuration, initializing life cycle, initializing event center, initializing rendering, initializing data, props, computed, watcher, etc.

Implementation of Vue instance mount

In Vue, we use the $mount instance method to mount VMS

The $mount method actually calls the mountComponent method

The core of mountComponent is to instantiate a rendering Watcher first, calling the updateComponent method in its callback function, and calling vm._ in this method. The render method becomes a virtual Node and finally calls VM_ Update update DOM

Watcher plays two roles: one is to execute the callback function during initialization, and the other is to execute the callback function when the monitored data in the vm instance changes

Finally, set VM when it is judged as the root node_ Ismounted is true, which means that the instance has been mounted and the mounted hook function is executed at the same time

render

Vue's_ The render method is a private method of the instance, which is used to render the instance into a virtual Node
The most frequently written is the template template. In the implementation of the mounted method, the template will be compiled into the render method

Virtual DOM

The real DOM element is very huge, because the browser standard makes the DOM design very complex. When we update DOM frequently, there will be some performance problems

Virtual DOM uses a native JS object to describe a DOM node, so it costs much less than creating a DOM
In Vue JS, Virtual DOM is described by a Class like VNode

VNode is an abstract description of the real DOM. Its core definition is nothing more than several key attributes, such as tag name, data, child node, key value, etc. other attributes are used to expand the flexibility of VNode and realize some special feature s.

Because VNode is only used for rendering mapped to real DOM, it does not need to include methods to operate DOM, so it is very lightweight and simple

In addition to the definition of its data structure, mapping Virtual DOM to real DOM actually goes through the process of VNode create, diff, patch and so on

Vue.js uses the createElement method to create VNode

createElement is the process of creating VNode. Each VNode has children, and each element of children is also a VNode. In this way, a VNode Tree is formed, which well describes our DOM Tree.

update

Vue's_ Update is a private method of an instance. It is called at two times: the first rendering and the data update

The whole process from initializing Vue to final rendering (with pictures)

life cycle

beforeCreate & created

Both beforeCreate and created functions are in the stage of instantiating Vue_ Executed in init method
During the execution of these two hook functions, DOM is not rendered, so we cannot access dom. Generally speaking, if the component needs to interact with the back-end during loading, it can be executed by these two hook functions. If you need to access props, data and other data, you need to use the created hook function

beforeMount & mounted
The beforeMount hook function occurs in the mount function, that is, before the DOM is mounted, and its call time is in the mountComponent function
Executing VM_ The render() function executes the beforeMount hook function before rendering VNode. After vm_ After update() patches VNode to the real DOM, execute the mounted hook

beforeUpdate & updated
The hook functions of beforeUpdate and updated should be executed when the data is updated
The execution time of beforeUpdate is in the before function of rendering Watcher
Note that the hook function beforeUpdate will not be called until the component has been mounted

beforeDestroy & destroyed
The execution time of beforeDestroy and destroyed hook functions is in the stage of component destruction, and eventually the $destroy method will be called
During the execution of $destroy, it will execute VM__ patch__ (VM. _vnode, null) triggers the destruction hook function of its sub components, which is called recursively layer by layer. Therefore, the execution order of the destroy hook function is the child first and then the parent, which is the same as that of the mounted process

activated & deactivated
The activated and deactivated hook functions are specially customized hooks for keep alive components
Keep alive reference: https://www.jianshu.com/p/952...

Responsive

In the initialization process, the original data is finally mapped into the DOM, and the change of data will trigger the change of DOM
1. Data rendering to page
2. Handle user interaction

Native js approach
Monitor click events, modify data, and manually operate DOM to re render

vue practice
Using object The principle of defineproperty is detailed in: https://developer.mozilla.org...

Object.defineProperty(obj, prop, descriptor)

For get and set in descriptor, get is a getter method provided for the attribute. When we access the attribute, the getter method will be triggered; Set is a setter method provided for a property. When we modify the property, the setter method will be triggered
Once an object has getter s and setter s, we can simply call it a responsive object

proxy

The role of proxy is to proxy the properties on props and data to the vm instance. Props is defined and can be accessed through vm instances

let comP = {
  props: {
    msg: 'hello'
  },
  methods: {
    say() {
      console.log(this.msg)
    }
  }
}

In the say function, pass this msg accesses the msg defined in props. This process takes place in the proxy stage. The implementation principle is as follows:

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

observe

The function of the observe method is to add an Observer to the object type data of non VNode. If it has been added, it will be returned directly. Otherwise, an Observer object instance will be instantiated when certain conditions are met

Observer

Observer is a class. Its function is to add getter s and setter s to the properties of objects for dependency collection and distribution of updates

/**
 * Observer class that is attached to each observed
 * object. Once attached, the observer converts the target
 * object's property keys into getter/setters that
 * collect dependencies and dispatch updates.
 */
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that has this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    // Judge the value, call the observeArray method for the array, otherwise call the walk method for the pure object
    if (Array.isArray(value)) {
      const augment = hasProto
        ? protoAugment
        : copyAugment
      augment(value, arrayMethods, arrayKeys)
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through each property 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++) {
        // The function of defineReactive is to define a responsive object and dynamically add getter s and setter s to the object
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
        // The function of observe is to monitor the changes of data. See the source code for the detailed code
      observe(items[i])
    }
  }

  /**
 * Define a property.
 */
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}

Summary of responsive principle: the core is to use object Defineproperty adds getters and setters to data in order to automatically execute some logic when we access and write data: what getters do is rely on collection, and what setters do is distribute updates

Topics: Vue.js