Vue源码探究-全局API
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类注册的全局函数包括了 directive
、component
、filter
,三个方法合在一个模块里,其余都分了各自的模块来定义。
全局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
包含有三,分别是 directive
、component
、filter
的注册并获取方法。方法的作用视参数而定,只传入资源标识名称ID未传定义函数或对象,则视为获取资源方法,如果都传则是资源注册方法,可谓是非常js化的。比较重要的是这里对于 definition
参数的重赋值,根据资源的种类不同,会进行不同的处理:组件主要是扩展Vue类,指令是格式化成定义对象,方便之后对指令的统一处理。
全局API的细节大概就是以上这些,对于经常使用的方式,了解其具体实现可以帮助我们在应用时避免出现不必要的错误,对于不经常使用的方法,在探索其实现时可以学习它们的实现原理和良好的方式。重要是在实践中分清楚每一个方法的使用场景,选取最恰当的方式实现功能。
Vue源码探究-全局API相关推荐
- Vue源码探究-事件系统
Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...
- vue大括号里接受一个函数_vue源码探究(第四弹)
vue源码探究(第四弹) 结束了上一part的数据代理,这一部分主要讲讲vue的模板解析,感觉这个有点难理解,而且内容有点多,hhh. 模板解析 废话不多说,先从简单的入手. 按照之前的套路,先举一个 ...
- vue foreach用法_vue 源码探究(第二弹)
vue 源码探究(第二弹) 接着上一篇,继续来讲一个非常有意思的东西documentFragment 解析 六.DocumentFragment: 文档碎片(高效批量更新多个节点) 这里先甩出 2 个 ...
- 【2021软件创新实验室暑假集训】SpringMVC框架(设计原理、简单使用、源码探究)
系列文章目录 20级 Java篇 [2021软件创新实验室暑假集训]计算机的起源与大致原理 [2021软件创新实验室暑假集训]Java基础(一) [2021软件创新实验室暑假集训]Java基础(二) ...
- 深入Vue - 源码目录及构建过程分析
摘要: Vue源码阅读第一步. 原文:深入vue - 源码目录及构建过程分析 公众号:前端小苑 Fundebug经授权转载,版权归原作者所有. 本文主要梳理一下vue代码的目录,以及vue代码构建流程 ...
- vue源码之响应式数据
分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...
- vue 初始化方法_前端发展方向指南—Vue源码初始化
Vue 的源码结构比较绕,同时使用了大量的面向对象的高级技巧.重写方法,扩展方法,多态等应用.从 Vue 实例的加载过程就可以看出来,这一节重点看看 Vue 的源码加载流程是什么. 前言 vue已是目 ...
- vue源码学习--vue源码学习入门
本文为开始学习vue源码的思路整理.在拿到vue项目源码的之后看到那些项目中的文件夹,会很困惑,不知道每个文件夹内的世界,怎么变换,怎样的魔力,最后产生了vue框架.学习源码也无从学起.我解决了这些困 ...
- 手写简易版Vue源码之数据响应化的实现
当前,Vue和React已成为两大炙手可热的前端框架,这两个框架都算是业内一些最佳实践的集合体.其中,Vue最大的亮点和特色就是数据响应化,而React的特点则是单向数据流与jsx. 笔者近期正在研究 ...
最新文章
- 表情的机器自动识别(有图有真相)
- 3行代码,Python数据预处理提速6倍
- 使用Delphi自带的TDockTabSet组件实现停靠功能(Jeremy North)
- Python基础(10) - 异常
- 数据结构与算法--实现Singleton模式
- 字符串转换为整数的源码atoi()
- 人口增量超过北上广!二线城市是怎样逆袭的?
- SQL Server 2005中设置Reporting Services发布web报表的匿名访问
- linux动态调试工具od,OllyDBG(OD动态调试工具)
- iFrame只要竖滚动条,不要横滚动条
- 毁掉孩子自信的10个杀手
- Android NDK 入门与实践
- spring4.x的一些新特性
- 秦时明月2服务器端代码修改,《秦时明月2》为什么修改不了?
- 求网页中嵌入mp3 格式音乐的html代码,子夜星·关于网页中加入音乐代码的讲解...
- Unity资源加载简析(二)AssetsBundle
- Java Exception的日志输出
- VMware Workstation Pro安装教程图文详解
- vue项目落地(qiankun.js)微前端服务
- 国资委79号文解读:国央企OA办公系统信创替代落地实践与标杆案例
热门文章
- 用户信息填写web代码_基于web的自定义表单引擎
- docker 主进程 日志_[docker]从一个实例,一窥docker进程管理
- java 之 语言基础
- c语言答案填空选择,C语言试题配答案
- 测试linux写文件系统,linux下各主要文件系统的读写性能测试
- java删除本地文件_读取Properties文件六种方法
- java openldap_java操作OpenLdap示例
- chrome正受到自动软件的控制_谷歌优化Chrome Omnibox自动完成功能 使其更加智能
- android屏幕分享软件,ScreenStream(屏幕分享)
- mobaxterm设置中文界面显示_Adobe系列2020如何更改界面显示语言