Vue源码探究-全局API

本篇代码位于vue/src/core/global-api/

Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言。这里主要来看一下全局API模块的实现。全局API的文件夹里有一个入口文件,各个功能分开定义,在这个入口文件中统一注入。

入口文件index.js

/* @flow */
// 从各个模块导入功能函数
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'// 导入内部辅助函数
import {warn,extend,nextTick,mergeOptions,defineReactive
} from '../util/index'// 定义并导出initGlobalAPI函数
export function initGlobalAPI (Vue: GlobalAPI) {// 定义全局配置对象// configconst configDef = {}// 定义配置对象的取值器函数configDef.get = () => config// 不允许在外部修改配置对象,非生产环境会给出警告if (process.env.NODE_ENV !== 'production') {configDef.set = () => {warn('Do not replace the Vue.config object, set individual fields instead.')}}// 定义Vue类的静态属性configObject.defineProperty(Vue, 'config', configDef)// 暴露工具方法// exposed util methods.// 注意:这不是全局公共API的一部分,// 除非了解到它们会带来的风险否则请避免使用。// NOTE: these are not considered part of the public API - avoid relying on// them unless you are aware of the risk.Vue.util = {warn,extend,mergeOptions,defineReactive}// 定义Vue的静态方法set、delete、nextTickVue.set = setVue.delete = delVue.nextTick = nextTick// 初始化Vue.options属性为空对象Vue.options = Object.create(null)// 初始化options属性的各个子属性为空对象ASSET_TYPES.forEach(type => {Vue.options[type + 's'] = Object.create(null)})// 这用于标识“基础”构造函数// 以在Weex的多实例场景中扩展所有普通对象组件// this is used to identify the "base" constructor to extend all plain-object// components with in Weex's multi-instance scenarios.Vue.options._base = Vue// 扩展options.components属性,加入内建组件extend(Vue.options.components, builtInComponents)// 向Vue类挂载静态方法initUse(Vue)initMixin(Vue)initExtend(Vue)initAssetRegisters(Vue)
}

入口文件从总体来讲可以分为两个部分:

  • 定义静态属性

    • config:在最开始的部分定义了Vue的静态属性 config,这是全局配置对象。
    • options:稍后定义的 options 对象是非常重要的属性,存放初始化的数据,我们平时在创建Vue实例时传入的配置对象最终要与这份配置属性合并,在实例初始化函数中的合并配置对象一部分可以初窥端倪。
  • 定义静态方法

    • util:虽然暴露了一些辅助方法,但官方并不将它们列入公共API中,不鼓励外部使用。
    • set:设置响应式对象的响应式属性,强制触发视图更新,在数组更新中非常实用,不适用于根数据属性。
    • delete:删除响应式属性强制触发视图更新, 使用情境较少。
    • nextTick:结束此轮循环后执行回调,常用于需要等待DOM更新或加载完成后执行的功能。
    • use:安装插件,自带规避重复安装。
    • mixin:常用于混入插件功能,不推荐在应用代码中使用。
    • extend:创建基于Vue的子类并扩展初始内容。
    • directive:注册全局指令。
    • component:注册全局组件。
    • filter:注册全局过滤器。

除了后6个方法之外,其他的辅助函数和方法都已经在其他模块里见识过了,继续来详细探索一下剩下的6个功能。initAssetRegisters 方法为Vue类注册的全局函数包括了 directivecomponentfilter,三个方法合在一个模块里,其余都分了各自的模块来定义。

全局API use

// 导入toArray辅助函数
import { toArray } from '../util/index'// 定义并导出initUse函数
export function initUse (Vue: GlobalAPI) {// 定义Vue类静态方法use,接受插件函数或对象Vue.use = function (plugin: Function | Object) {// 定义内部属性installedPlugins,存放已安装插件// 首次应用时定义为空数组const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))// 检测是否安装过传入的插件,已存在则返回if (installedPlugins.indexOf(plugin) > -1) {return this}// 处理附加参数,加入参数Vue// additional parameters// 将传入的参数转化为数组const args = toArray(arguments, 1)// 插入Vue类本身为第一个元素args.unshift(this)// 如果插件有install方法,则在plugin对象上调用并传入新参数if (typeof plugin.install === 'function') {plugin.install.apply(plugin, args)} else if (typeof plugin === 'function') {// 如果plugin本身是函数,则直接调用并传入新参数plugin.apply(null, args)}// 向缓存插件数组中添加此插件并返回installedPlugins.push(plugin)return this}
}

use 方法的实现很简单,在内部定义了数组来缓存已经注册过的插件,并在下一次注册前检验是否已注册过,可以避免重复注册插件。接受的参数值得注意,如果插件本身就是一个函数,则直接调用;如果插件是对象,则必须有install方法,否则没有任何行为,这是Vue为了统一插件定义规范所设置的入口方法名称。

全局API mixin

// 导入mergeOptions辅助函数
import { mergeOptions } from '../util/index'// 定义并导出initMixin函数
export function initMixin (Vue: GlobalAPI) {// 定义Vue的静态方法mixinVue.mixin = function (mixin: Object) {// 合并配置对象,重置Vue类的静态属性optionsthis.options = mergeOptions(this.options, mixin)// 返回return this}
}

mixin 方法的实现更加简洁,在重用Vue类的所有状态下,只是重新合并了options属性。由于使用场景大都是用来混入插件功能的,所以创建项目时几乎没有运用,了解即可。

全局API extend

// 导入资源类型,模块方法和辅助方法
import { ASSET_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'// 定义并导出initExtend
export function initExtend (Vue: GlobalAPI) {// 每个实例构造函数,包括Vue都有唯一的cid。// 这使我们能够为原型继承创建包装的“子构造函数”并缓存它们。/*** Each instance constructor, including Vue, has a unique* cid. This enables us to create wrapped "child* constructors" for prototypal inheritance and cache them.*/// 设置Vue的cid为0Vue.cid = 0// 定义cid变量let cid = 1// 定义类继承方法/*** Class inheritance*/// 定义Vue类静态方法extend,接受扩展选项对象Vue.extend = function (extendOptions: Object): Function {// extendOptions若未定义则设置为空对象extendOptions = extendOptions || {}// 存储父类和父类的cidconst Super = thisconst SuperId = Super.cid// 定义缓存构造器对象,如果扩展选项的_Ctor属性未定义则赋值空对象const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})// 如果缓存构造器已存有该构造器,则直接返回if (cachedCtors[SuperId]) {return cachedCtors[SuperId]}// 获取扩展配置对象名称或父级配置对象名称属性,赋值给nameconst name = extendOptions.name || Super.options.name// 在非生产环境下验证name是否合法并给出警告if (process.env.NODE_ENV !== 'production' && name) {validateComponentName(name)}// 定义子类构造函数const Sub = function VueComponent (options) {this._init(options)}// 实现子类原型继承,原型指向父类原型,构造器指向SubSub.prototype = Object.create(Super.prototype)Sub.prototype.constructor = Sub// 定义子类cid,并递增cidSub.cid = cid++// 定义子类options属性,合并配置对象Sub.options = mergeOptions(Super.options,extendOptions)// 定义子类super属性,指向父类Sub['super'] = Super// 对于props和computed属性,扩展时在Vue实例上定义了代理getter。// 这避免了对每个创建的实例执行Object.defineProperty调用。// For props and computed properties, we define the proxy getters on// the Vue instances at extension time, on the extended prototype. This// avoids Object.defineProperty calls for each instance created.// 初始化子类的propsif (Sub.options.props) {initProps(Sub)}// 初始化子类的计算属性if (Sub.options.computed) {initComputed(Sub)}// 定义子类的全局API,扩展、混入和使用插件// allow further extension/mixin/plugin usageSub.extend = Super.extendSub.mixin = Super.mixinSub.use = Super.use// 创建子类的资源注册方法,允许子类有私有资源// create asset registers, so extended classes// can have their private assets too.ASSET_TYPES.forEach(function (type) {Sub[type] = Super[type]})// 启用递归自查找// enable recursive self-lookupif (name) {Sub.options.components[name] = Sub}// 在扩展时保持对父类配置对象的引用,// 以后实例化时可以检查父级配置对象是否更新// keep a reference to the super options at extension time.// later at instantiation we can check if Super's options have// been updated.Sub.superOptions = Super.optionsSub.extendOptions = extendOptionsSub.sealedOptions = extend({}, Sub.options)// 缓存子类构造函数// cache constructorcachedCtors[SuperId] = Sub// 返回return Sub}
}// 定义初始化propss函数
function initProps (Comp) {// 获取配置对象的props属性const props = Comp.options.props// 设置代理for (const key in props) {proxy(Comp.prototype, `_props`, key)}
}// 定义初始化计算属性函数
function initComputed (Comp) {// 获取配置对象的computed属性const computed = Comp.options.computed// 设置代理for (const key in computed) {defineComputed(Comp.prototype, key, computed[key])}
}

extend 方法是最为复杂的全局API了,它在扩展类实现继承时进行了很多处理:除去判断是否有已存储的子类构造函数之外,首先是实现类继承,原理是原型式继承;然后为子类初始化props和computed属性的代理:最后是扩展全局API。另外对继承的父类的属性也进行了引用存储。

全局API 资源获取和注册

// 导入资源类型和辅助函数
import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'// 定义并注册initAssetRegisters函数
export function initAssetRegisters (Vue: GlobalAPI) {// 创建资源注册方法/*** Create asset registration methods.*/// 遍历ASSET_TYPES数组,为Vue定义相应方法// ASSET_TYPES包括了directive、 component、filterASSET_TYPES.forEach(type => {// 定义资源注册方法,参数是标识名称id,和定义函数或对象Vue[type] = function (id: string,definition: Function | Object): Function | Object | void {// 如果未传入definition,则视为获取该资源并返回if (!definition) {return this.options[type + 's'][id]} else {// 否则视为注册资源// 非生产环境下给出检验组件名称的错误警告/* istanbul ignore if */if (process.env.NODE_ENV !== 'production' && type === 'component') {validateComponentName(id)}// 如果是注册component,并且definition是对象类型if (type === 'component' && isPlainObject(definition)) {// 设置definition.name属性definition.name = definition.name || id// 调用Vue.extend扩展定义,并重新赋值definition = this.options._base.extend(definition)}// 如果是注册directive且definition为函数if (type === 'directive' && typeof definition === 'function') {// 重新定义definition为格式化的对象definition = { bind: definition, update: definition }}// 存储资源并赋值this.options[type + 's'][id] = definition// 返回definitionreturn definition}}})
}

initAssetRegisters 包含有三,分别是 directivecomponentfilter 的注册并获取方法。方法的作用视参数而定,只传入资源标识名称ID未传定义函数或对象,则视为获取资源方法,如果都传则是资源注册方法,可谓是非常js化的。比较重要的是这里对于 definition 参数的重赋值,根据资源的种类不同,会进行不同的处理:组件主要是扩展Vue类,指令是格式化成定义对象,方便之后对指令的统一处理。


全局API的细节大概就是以上这些,对于经常使用的方式,了解其具体实现可以帮助我们在应用时避免出现不必要的错误,对于不经常使用的方法,在探索其实现时可以学习它们的实现原理和良好的方式。重要是在实践中分清楚每一个方法的使用场景,选取最恰当的方式实现功能。

Vue源码探究-全局API相关推荐

  1. Vue源码探究-事件系统

    Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...

  2. vue大括号里接受一个函数_vue源码探究(第四弹)

    vue源码探究(第四弹) 结束了上一part的数据代理,这一部分主要讲讲vue的模板解析,感觉这个有点难理解,而且内容有点多,hhh. 模板解析 废话不多说,先从简单的入手. 按照之前的套路,先举一个 ...

  3. vue foreach用法_vue 源码探究(第二弹)

    vue 源码探究(第二弹) 接着上一篇,继续来讲一个非常有意思的东西documentFragment 解析 六.DocumentFragment: 文档碎片(高效批量更新多个节点) 这里先甩出 2 个 ...

  4. 【2021软件创新实验室暑假集训】SpringMVC框架(设计原理、简单使用、源码探究)

    系列文章目录 20级 Java篇 [2021软件创新实验室暑假集训]计算机的起源与大致原理 [2021软件创新实验室暑假集训]Java基础(一) [2021软件创新实验室暑假集训]Java基础(二) ...

  5. 深入Vue - 源码目录及构建过程分析

    摘要: Vue源码阅读第一步. 原文:深入vue - 源码目录及构建过程分析 公众号:前端小苑 Fundebug经授权转载,版权归原作者所有. 本文主要梳理一下vue代码的目录,以及vue代码构建流程 ...

  6. vue源码之响应式数据

    分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...

  7. vue 初始化方法_前端发展方向指南—Vue源码初始化

    Vue 的源码结构比较绕,同时使用了大量的面向对象的高级技巧.重写方法,扩展方法,多态等应用.从 Vue 实例的加载过程就可以看出来,这一节重点看看 Vue 的源码加载流程是什么. 前言 vue已是目 ...

  8. vue源码学习--vue源码学习入门

    本文为开始学习vue源码的思路整理.在拿到vue项目源码的之后看到那些项目中的文件夹,会很困惑,不知道每个文件夹内的世界,怎么变换,怎样的魔力,最后产生了vue框架.学习源码也无从学起.我解决了这些困 ...

  9. 手写简易版Vue源码之数据响应化的实现

    当前,Vue和React已成为两大炙手可热的前端框架,这两个框架都算是业内一些最佳实践的集合体.其中,Vue最大的亮点和特色就是数据响应化,而React的特点则是单向数据流与jsx. 笔者近期正在研究 ...

最新文章

  1. 表情的机器自动识别(有图有真相)
  2. 3行代码,Python数据预处理提速6倍
  3. 使用Delphi自带的TDockTabSet组件实现停靠功能(Jeremy North)
  4. Python基础(10) - 异常
  5. 数据结构与算法--实现Singleton模式
  6. 字符串转换为整数的源码atoi()
  7. 人口增量超过北上广!二线城市是怎样逆袭的?
  8. SQL Server 2005中设置Reporting Services发布web报表的匿名访问
  9. linux动态调试工具od,OllyDBG(OD动态调试工具)
  10. iFrame只要竖滚动条,不要横滚动条
  11. 毁掉孩子自信的10个杀手
  12. Android NDK 入门与实践
  13. spring4.x的一些新特性
  14. 秦时明月2服务器端代码修改,《秦时明月2》为什么修改不了?
  15. 求网页中嵌入mp3 格式音乐的html代码,子夜星·关于网页中加入音乐代码的讲解...
  16. Unity资源加载简析(二)AssetsBundle
  17. Java Exception的日志输出
  18. VMware Workstation Pro安装教程图文详解
  19. vue项目落地(qiankun.js)微前端服务
  20. 国资委79号文解读:国央企OA办公系统信创替代落地实践与标杆案例

热门文章

  1. 用户信息填写web代码_基于web的自定义表单引擎
  2. docker 主进程 日志_[docker]从一个实例,一窥docker进程管理
  3. java 之 语言基础
  4. c语言答案填空选择,C语言试题配答案
  5. 测试linux写文件系统,linux下各主要文件系统的读写性能测试
  6. java删除本地文件_读取Properties文件六种方法
  7. java openldap_java操作OpenLdap示例
  8. chrome正受到自动软件的控制_谷歌优化Chrome Omnibox自动完成功能 使其更加智能
  9. android屏幕分享软件,ScreenStream(屏幕分享)
  10. mobaxterm设置中文界面显示_Adobe系列2020如何更改界面显示语言