2

Directory Structure

├── benchmarks                  性能、基准测试
├── dist                        构建打包的输出目录
├── examples                    案例目录
├── flow                        flow 语法的类型声明
├── packages                    一些额外的包,比如:负责服务端渲染的包 vue-server-renderer、配合 vue-loader 使用的 vue-template-compiler,还有 weex 相关的
│   ├── vue-server-renderer
│   ├── vue-template-compiler
│   ├── weex-template-compiler
│   └── weex-vue-framework
├── scripts                     所有的配置文件的存放位置,比如 rollup 的配置文件
├── src                         vue 源码目录
│   ├── compiler                编译器
│   ├── core                    运行时的核心包
│   │   ├── components          全局组件,比如 keep-alive
│   │   ├── config.js           一些默认配置项
│   │   ├── global-api          全局 API,比如熟悉的:Vue.use()、Vue.component() 等
│   │   ├── instance            Vue 实例相关的,比如 Vue 构造函数就在这个目录下
│   │   ├── observer            响应式原理
│   │   ├── util                工具方法
│   │   └── vdom                虚拟 DOM 相关,比如熟悉的 patch 算法就在这儿
│   ├── platforms               平台相关的编译器代码
│   │   ├── web
│   │   └── weex
│   ├── server                  服务端渲染相关
├── test                        测试目录
├── types                       TS 类型声明

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

1. Process component configuration items

When initializing the root component, the option merging operation is performed, and the global configuration is merged into the local configuration of the root component

Some performance optimizations were made when initializing each sub-component, and some deep-level properties on the component configuration object were placed in the vm.$options option to improve the execution efficiency of the code

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

3. Handling custom events

4. Call the beforeCreate hook function

5. Initialize the inject configuration item of the component, get the configuration object in the form of ret[key] = val, and then perform shallow reactive 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 responsiveness, handling options such as props, methods, data, computed, watch, etc.

7. Parse the provide object on the component configuration item and mount it on the vm._provided attribute

8. Call the created hook function

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

10. Next, enter the mount stage

Summary: Vue initialization mainly does 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 mount vm through the $mount instance method

The $mount method actually calls the mountComponent method

The core of mountComponent is to first instantiate a rendering Watcher, and call the updateComponent method in its callback function. In this method, call the vm._render method to generate a virtual Node first, and finally call vm._update to update the DOM

Watcher has two functions, 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.

When it is finally judged to be the root node, set vm._isMounted to true, indicating that the instance has been mounted, and execute the mounted hook function at the same time

render

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

Virtual DOM

The real DOM element is very large, because browser standards make the DOM very complex. When we frequently do DOM updates, there will be certain performance problems

And Virtual DOM uses a native JS object to describe a DOM node, so it is much less expensive 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 a few key attributes, tag name, data, child nodes, key values, etc. Other attributes are used to extend the flexibility of VNode and implement some special features of.

Since the VNode is only used to map to the rendering of the real DOM and does not need to contain methods for manipulating the DOM, it is very lightweight and simple

Virtual DOM In addition to the definition of its data structure, mapping to the real DOM actually has to go through the processes of VNode create, diff, patch, etc.

Vue.js uses createElement method to create VNode

createElement The process of creating VNode, each VNode has children, and each element of children is also a VNode, thus forming a VNode Tree, which describes our DOM Tree very well.

update

Vue's _update is a private method of the instance. It is called at two times, one is the first rendering and the other is when the data is updated

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

The life cycle

beforeCreate & created

Both beforeCreate and created functions are executed in the _init method in the stage of instantiating Vue
When these two hook functions are executed, the DOM is not rendered, so we cannot access the DOM. Generally speaking, if the component needs to interact with the backend when it is loaded, it can be executed in these two hook functions. , if you need to access data such as props, data, etc., you need to use the created hook function

beforeMount & mounted
The beforeMount hook function occurs before mount, that is, before the DOM is mounted, and its call timing is in the mountComponent function
Before executing the vm._render() function to render the VNode, execute the beforeMount hook function. After executing vm._update() to patch the VNode to the real DOM, execute the mounted hook

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

beforeDestroy & destroyed
The execution timing of the beforeDestroy and destroyed hook functions will eventually call the $destroy method during the component destruction stage.
During the execution of $destroy, it will execute vm.__patch__(vm._vnode, null) to trigger the destruction hook function of its subcomponents, which is called recursively layer by layer, so the execution order of destroy hook function is first child and then parent , the same as the mounted process

activated & deactivated
The activated and deactivated hook functions are custom hooks for keep-alive components
keep-alive reference: https://www.jianshu.com/p/9523bb439950

Responsive

In the initialization process, the original data is finally mapped to the DOM, and the change of the data will trigger the change of the DOM
1. Data rendering to the page
2. Handling User Interactions

Native js practice
Listen for click events, modify data, and manually manipulate DOM to re-render

Vue practice
Using the principle of Object.defineProperty, please refer to: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty(obj, prop, descriptor)

For get and set in descriptor, get is a getter method provided to a property, and the getter method will be triggered when we access the property; set is a setter method provided to a property, when we modify the property will trigger the setter method
Once an object has getters and setters, we can simply call this object reactive

proxy

The role of the proxy is to proxy the properties on props and data to the vm instance. defines props, which can be accessed through the vm instance

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

In the say function, the msg defined in the props is accessed through this.msg. This process occurs 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 non-VNode object type data. If it has been added, it will return directly. Otherwise, an instance of the Observer object will be instantiated when certain conditions are met.

Observer

Observer is a class whose function is to add getters and setters to the properties of objects for dependency collection and dispatch 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)
    // 对 value 做判断,对于数组会调用 observeArray 方法,否则对纯对象调用 walk 方法
    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++) {
        // defineReactive 的功能就是定义一个响应式对象,给对象动态添加 getter 和 setter
      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 的功能就是用来监测数据的变化, 详细代码看源码
      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 to add getter and setter to data, the purpose is to automatically execute some logic when we access data and write data: what getter does is dependent collection, what setter does is Distribute updates


Grace糖衣
15 声望2 粉丝

用于记录一些平时遇到的问题