Vue.js source code analysis (1) - responsiveness principle (2) Vue initialization and first rendering process

1. Vue constructor

In the first chapter of this article, let’s look at Vuethe definition of the constructor in the source code.

(one)src/platforms/runtime-with-compiler.ts

The codes in platformsthe directory are all platform-related codes. As we mentioned in the previous article, in the file, methods runtime-with-compiler.tsare defined and converted into functions. The key codes in this file are:$mounthtmlrender
Insert image description here

import Vue from './runtime/index'
const mount = Vue.prototype.$mount
// 重写$mount方法
Vue.prototype.$mount = xx;
// 给vue添加compile方法,将html字符串转换成render函数
Vue.compile = compileToFunctions
export default Vue as GlobalAPI

This file is importimported Vue, the absolute path of the file: src/platforms/web/runtime/index.ts,

(two)src/platforms/web/runtime/index.ts

Let's go into this file to check. Here are a few key lines of code:

import Vue from 'core/index'
// 注册平台相关的指令和组件
// extend:将参数二扩展到参数一上
extend(Vue.options.directives, platformDirectives)
// Vue.options.components存储全局组件
extend(Vue.options.components, platformComponents)
// 给Vue原型上添加$mount方法
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
    
    
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
export default Vue

This file is mainly used to register unique instructions and components for the platform, and defines Vuemethods . Its Vue is also imported from the outside world. Let’s check it out inprototype$mountcore/index

(three)src/core/index.ts

Here are a few key lines of code:

import Vue from './instance/index'
import {
    
     initGlobalAPI } from './global-api/index'
// 初始化Vue的静态成员
initGlobalAPI(Vue)
// 给Vue.prototype添加属性
Object.defineProperty(Vue.prototype, '$isServer', {
    
    
  get: isServerRendering
})
export default Vue

This method is used to initialize Vuethe static members of the instance and add Vue.prototypeseveral properties related to the running environment. But it Vueis also introduced from the outside. We ./instance/indexlook at Vue in

(Four)src/core/instance/index.ts

Here we finally see Vuethe constructor

function Vue(options) {
    
    
  if (__DEV__ && !(this instanceof Vue)) {
    
    
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  // 调用_init方法
  this._init(options)
}
// 给Vue初始化init方法
initMixin(Vue)
// 注册vm 的 $data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化事件相关方法
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
lifecycleMixin(Vue)
//@ts-expect-error Vue has function type
// 混入render方法
renderMixin(Vue)

The purpose of this file is to define Vuethe constructor and Vuemix in common instance members.

2. Vue static members

Vue static members

In this chapter, we will introduce Vuethe definition of static members. Vue's static members are mainly defined in src/core/global-api/index.tsfile methods, and their functions are as shown in the figure below: Source code:initGlobalAPI()initGlobalAPI()
Insert image description here

initGlobalAPI()

export function initGlobalAPI(Vue: GlobalAPI) {
    
    
  // config
  const configDef: Record<string, any> = {
    
    }
  configDef.get = () => config
  // 非生产环境下,不允许调用config的set方法修改Vue.config
  if (__DEV__) {
    
    
    configDef.set = () => {
    
    
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  // 初始化Vue.config静态属性
  Object.defineProperty(Vue, 'config', configDef)
  
  // Vue内部使用的方法,不建议外部使用
  Vue.util = {
    
    
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  // 静态方法
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  // 设置响应式数据
  Vue.observable = <T>(obj: T): T => {
    
    
    observe(obj)
    return obj
  }

  // 初始化Vue.options静态属性,并给其扩展
  // 初始化Vue.options.components、Vue.options.directives、Vue.options.filters
  // 存储全局组件、指令、过滤器
  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    
    
    Vue.options[type + 's'] = Object.create(null)
  })
  
  // 记录当前Vue构造函数
  Vue.options._base = Vue

  // 把builtInComponents对象属性拷贝到Vue.options.components中
  // 注册全局组件keep-alive
  extend(Vue.options.components, builtInComponents)

  // 初始化Vue.use静态方法,用来注册插件
  initUse(Vue)
  // 初始化Vue.mixin静态方法,实现混入
  initMixin(Vue)
  // 初始化Vue.extend静态方法,基于传入的options返回一个组件的构造函数
  initExtend(Vue)
  // 初始化Vue.component、Vue.directive、Vue.filter静态方法
  initAssetRegisters(Vue)
}

initUse(Vue)

export function initUse(Vue: GlobalAPI) {
    
    
  // Vue.use方法
  // plugin:要注册的插件
  Vue.use = function (plugin: Function | any) {
    
    
    // this指向Vue构造函数
    const installedPlugins =
      this._installedPlugins || (this._installedPlugins = [])
    if (installedPlugins.indexOf(plugin) > -1) {
    
    
      // 已经注册过了,直接返回
      return this
    }

    // 注册插件就是调用插件
    // 如果插件是一个对象,那么调用其install方法
    // 如果插件是一个函数,那么直接调用
    // arguments是调用Vue.use时传入的参数,第一个参数一定是插件,
    // 后面的参数是传给插件的参数
    const args = toArray(arguments, 1)
    // 把Vue构造函数添加到参数的第一个位置
    args.unshift(this)
    if (isFunction(plugin.install)) {
    
    
      // apply将数组展开成一个个参数
      plugin.install.apply(plugin, args)
    } else if (isFunction(plugin)) {
    
    
      plugin.apply(null, args)
    }
    // 把插件添加到已注册插件列表中
    installedPlugins.push(plugin)
    return this
  }
}

initMixin(Vue)

export function initMixin(Vue: GlobalAPI) {
    
    
  Vue.mixin = function (mixin: Object) {
    
    
    // 把传过来的选项传到Vue.options中
    // 注册全局选项
    this.options = mergeOptions(this.options, mixin)
    return this
  }
}

initExtend(Vue)


export function initExtend(Vue: GlobalAPI) {
    
    
  // 每个实例构造器,包括Vue,都有一个唯一的cid,便于缓存和创建子构造函数
  Vue.cid = 0
  let cid = 1

  // 给Vue添加extend方法
  Vue.extend = function (extendOptions: any): typeof Component {
    
    
    extendOptions = extendOptions || {
    
    }
    const Super = this
    const SuperId = Super.cid
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {
    
    })
    if (cachedCtors[SuperId]) {
    
    
      return cachedCtors[SuperId]
    }

    const name =
      getComponentName(extendOptions) || getComponentName(Super.options)
    if (__DEV__ && name) {
    
    
      validateComponentName(name)
    }

    // 创建组件构造函数
    const Sub = function VueComponent(this: any, options: any) {
    
    
      this._init(options)
    } as unknown as typeof Component
    // 继承Vue
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    // 合并选项
    Sub.options = mergeOptions(Super.options, extendOptions)
    Sub['super'] = Super

	// 对于props,定义到Sub.prototype._props上
    if (Sub.options.props) {
    
    
      initProps(Sub)
    }
    // computed定义到Sub.prototype上
    if (Sub.options.computed) {
    
    
      initComputed(Sub)
    }

    // 初始化extension/mixin/plugin
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // 初始化component()、directive()、filter()
    ASSET_TYPES.forEach(function (type) {
    
    
      Sub[type] = Super[type]
    })
    // 如果传进来的选项有name属性,将构造函数存储到options中
    if (name) {
    
    
      Sub.options.components[name] = Sub
    }

    //存储父类的options选项
    Sub.superOptions = Super.options
    // 传进来的选项
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({
    
    }, Sub.options)

    // 缓存 constructor
    cachedCtors[SuperId] = Sub
    // 返回组件构造函数
    return Sub
  }
}

initAssetRegisters(Vue)

export function initAssetRegisters(Vue: GlobalAPI) {
    
    
  // ASSET_TYPES:['component', 'directive', 'filter]
  // 这几个方法使用的时候都是Vue.component('xxx', definition)
  // definition: Function | Object
  // 如果没有传definition,那么就是获取全局组件、指令、过滤器
  ASSET_TYPES.forEach(type => {
    
    
    Vue[type] = function (
      id: string,
      definition?: Function | Object
    ): Function | Object | void {
    
    
      if (!definition) {
    
    
        // 如果没有第二个参数,获取对应id组件、指令、过滤器
        return this.options[type + 's'][id]
      } else {
    
    
        if (__DEV__ && type === 'component') {
    
    
          validateComponentName(id)
        }
        // isPlainObject:判断是否是纯对象,区分于函数、数组、null、Date、RegExp等
        if (type === 'component' && isPlainObject(definition)) {
    
    
          // 给组件定义name属性,如果没有name属性,就使用id
          definition.name = definition.name || id
          // this.options._base:Vue构造函数
          // Vue.extend:根据传入的选项,返回一个组件的构造函数
          // 所以这里的definition是一个组件的构造函数
          definition = this.options._base.extend(definition)
        }
        if (type === 'directive' && isFunction(definition)) {
    
    
          definition = {
    
     bind: definition, update: definition }
        }
        // 给组件、指令、过滤器添加到Vue.options中
        // 直接传递Vue.extend()构造函数会直接将构造函数存储到options中
        this.options[type + 's'][id] = definition
        return definition
      }
    }
  })
}

3. Vue instance members

src/core/instance/init.tsVueThe instance members defined in the file are Vue.prototypeadded members. VueThe instance object can be inherited through the prototype chain. The instance members are initialized in the file through the following function blocks:

// 给Vue的实例对象上增加init方法
initMixin(Vue)
// 注册vm 的 $data/$props/$set/$delete/$watch
stateMixin(Vue)
// 初始化vm中事件相关方法
eventsMixin(Vue)
// 初始化生命周期相关的混入方法
// _update/$forceUpdate/$destroy
// _update中调用了__patch__方法对比差异更新DOM
lifecycleMixin(Vue)
// 混入render方法
renderMixin(Vue)

The key functions implemented are shown in the figure below: a method is added to
Insert image description here
the instance, which is the instance initialization method. This method will be executed in the constructor. Let’s look at the specific functions of the method:initMixin(Vue)Vue_init()VueVue_init()
Insert image description here

4. Debugging the Vue initialization process

VueVueThe initialization process is mainly carried out in the four files in Chapter 1. The initialization of instance members and the initialization of static methods are completed . By disconnecting debugging, the general execution process is as follows:
Insert image description here

5. Debugging the first rendering process

Vue.proptotype._init()In the mind map that has been drawn in Chapter 3 above , the last part of this function is vm.$mount()the method. The first rendering process starts from this function.

Insert image description here

6. Extension

1. Debugging skills: Click the small + in the watch bar of the Chrome browser and enter the data you want to view. You can always view the changes in the data during breakpoint debugging.
Insert image description here

Guess you like

Origin blog.csdn.net/weixin_45855469/article/details/131781542