Vue.js 源码剖析(一)- 响应式原理(2)Vue初始化及首次渲染过程

一、Vue构造函数

本篇第一章我们来找一下Vue源码中的构造函数的定义。

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

platforms目录下的代码都是和平台相关的代码,上一篇我们说到,在runtime-with-compiler.ts文件中,定义了$mount方法,将html转换为render函数,
在这里插入图片描述
这个文件其中的关键代码:

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

这个文件中通过import导入了Vue,文件绝对路径:src/platforms/web/runtime/index.ts

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

我们进入这个文件中查看,下面是几行关键代码:

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

这个文件主要用于给平台注册特有的指令和组件,并且给Vueprototype定义了$mount方法,它的Vue也是从外界导入的,我们再去到core/index中查看

(三)src/core/index.ts

以下是几行关键代码:

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

这个方法用于初始化Vue实例的静态成员,并且给Vue.prototype添加了几个运行环境相关的属性。但是它的Vue也是通过外部引入的,我们在到./instance/index中查看Vue

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

在这里终于看到了Vue的构造函数

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)

这个文件的作用是定义Vue构造函数,以及给Vue混入常见的实例成员

二、Vue静态成员

Vue静态成员

这一章我们来介绍一下Vue中静态成员的定义。Vue的静态成员主要是在src/core/global-api/index.ts文件的initGlobalAPI()方法中定义,initGlobalAPI()中的功能如下图所示:
在这里插入图片描述
源码:
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
      }
    }
  })
}

三、Vue实例成员

src/core/instance/init.ts文件中定义了Vue的实例成员,是给Vue.prototype上添加成员,Vue实例对象可以通过原型链继承,文件中通过一下几个函数分块对实例成员进行初始化:

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

实现的关键的功能如下图所示:
在这里插入图片描述
其中initMixin(Vue)Vue实例增加了_init()方法,是Vue实例初始化的方法,在Vue的构造函数中会执行这个方法。我们再来看_init()方法的具体功能:
在这里插入图片描述

四、调试Vue初始化过程

Vue初始化过程主要在第一章的四个文件中进行,完成了实例成员的初始化和Vue静态方法的初始化,通过断掉调试,大致的执行过程如下:
在这里插入图片描述

五、调试首次渲染过程

上述第三章已经画过了Vue.proptotype._init()的思维导图,这个函数的最后是vm.$mount()方法,首次渲染的过程从这个函数开始调试

在这里插入图片描述

六、扩展

1、调试技巧:在chrome浏览器的watch栏点击小+输入要查看的数据,在断点调试的过程中就可以一直查看该数据的变化。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45855469/article/details/131781542