Análise do código-fonte Vue.js (1) - princípio de capacidade de resposta (2) Inicialização Vue e primeiro processo de renderização

1. Construtor Vue

No primeiro capítulo deste artigo, veremos Vuea definição do construtor no código-fonte.

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

Os códigos no platformsdiretório são todos códigos relacionados à plataforma. Como mencionamos no artigo anterior, no arquivo os métodos runtime-with-compiler.tssão definidos e convertidos em funções. Os códigos-chave neste arquivo são:$mounthtmlrender
Insira a descrição da imagem aqui

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

Este arquivo é importimportado Vue, o caminho absoluto do arquivo src/platforms/web/runtime/index.ts:,

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

Vamos verificar este arquivo. Aqui estão algumas linhas principais de código:

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

Este arquivo é usado principalmente para registrar instruções e componentes exclusivos para a plataforma e definir Vuemétodos . Seu Vue também é importado do mundo externo. Vamos conferir emprototype$mountcore/index

(três)src/core/index.ts

Aqui estão algumas linhas principais de código:

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

Este método é usado para inicializar Vueos membros estáticos da instância e adicionar Vue.prototypediversas propriedades relacionadas ao ambiente em execução. Mas Vuetambém é introduzido de fora. Olhamos ./instance/indexpara Vue em

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

Aqui finalmente vemos Vueo construtor

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)

O objetivo deste arquivo é definir Vueo construtor e Vuecombinar membros de instância comuns.

2. Membros estáticos do Vue

Membros estáticos do Vue

Neste capítulo, apresentaremos Vuea definição de membros estáticos. Os membros estáticos do Vue são definidos principalmente em métodos src/core/global-api/index.tsde arquivo e suas funções são mostradas na figura abaixo: Código-fonte:initGlobalAPI()initGlobalAPI()
Insira a descrição da imagem aqui

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. Membros da instância Vue

src/core/instance/init.tsVueOs membros da instância definidos no arquivo são Vue.prototypemembros adicionados. VueO objeto da instância pode ser herdado por meio da cadeia de protótipo. Os membros da instância são inicializados no arquivo por meio dos seguintes blocos de função:

// 给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)

As principais funções implementadas são mostradas na figura abaixo: um método é adicionado à
Insira a descrição da imagem aqui
instância , que é o método de inicialização da instância, este método será executado no construtor. Vejamos as funções específicas do método:initMixin(Vue)Vue_init()VueVue_init()
Insira a descrição da imagem aqui

4. Depurando o processo de inicialização do Vue

VueVueO processo de inicialização é realizado principalmente nos quatro arquivos do Capítulo 1. A inicialização dos membros da instância e a inicialização dos métodos estáticos são concluídas . Ao desconectar a depuração, o processo geral de execução é o seguinte:
Insira a descrição da imagem aqui

5. Depurando o primeiro processo de renderização

Vue.proptotype._init()No mapa mental desenhado no Capítulo 3 acima , a última parte desta função é vm.$mount()o método. O primeiro processo de renderização começa a partir desta função.

Insira a descrição da imagem aqui

6. Extensão

1. Habilidades de depuração: Clique no pequeno + na barra de observação do navegador Chrome e insira os dados que deseja visualizar. Você sempre pode visualizar as alterações nos dados durante a depuração do ponto de interrupção.
Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/weixin_45855469/article/details/131781542
Recomendado
Clasificación