技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

Vue.use() 的作用及原理

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/13-Vue use()的作用及原理
本文目录7 个章节
  1. Vue.prototype 做了什么?
  2. Vue.use() 是什么?
  3. Vue 2 中 Vue.use() 的源码逻辑
  4. Vue 3 的当前写法
  5. 自己编写一个插件
  6. 总结
  7. 官方参考

Vue.use() 的作用及原理

本文原文讨论的是 Vue 2 的全局 API。Vue 2.7 仍可使用 Vue.use(),但 Vue 2 已于 2023 年 12 月 31 日结束维护。Vue 3 应使用 createApp() 创建应用,再通过 app.use() 安装插件。下面保留 Vue 2 的历史原理,并补充当前写法。

在 Vue 中引入第三方库通常使用 import。有些库引入后还要执行 Vue.use(),有些库则会写成 Vue.prototype.$something = something。它们都和“把能力接入组件实例或应用”有关,但不是同一件事。

Vue.prototype 做了什么?

以 axios 为例,直接导入后,普通 JavaScript 模块可以这样使用:

import axios from 'axios'

axios.get('/api/users')

在 Vue 2 项目中,也可以把它挂到 Vue 构造函数的原型上:

import Vue from 'vue'
import axios from 'axios'

Vue.prototype.$axios = axios

这样安装后,组件实例可以通过 this.$axios 使用它,模板也可以访问 $axios。它不会让任意 JavaScript 文件凭空获得 $axios;普通模块仍应直接 import axios,或者通过其他依赖注入方式取得实例。

Vue 2 通过 Vue.prototype 注入 axios 的历史示意

上图是原文的历史截图,仅用于说明 Vue 2 原型注入;Vue 3 不再使用 Vue.prototype

Vue.use() 是什么?

Vue 2 官方将 Vue.use() 定义为安装插件的全局方法:

  • 插件对象应提供 install 方法。
  • 如果插件本身是函数,就把这个函数当作安装函数。
  • Vue 2 调用安装函数时,第一个参数是 Vue 构造函数,后面是 Vue.use(plugin, options) 传入的额外参数。
  • 同一个插件对象在同一个 Vue 构造函数上重复安装时只会执行一次。
  • 通常应在 new Vue() 创建根实例之前安装插件。

例如:

import Vue from 'vue'
import Element from 'element-ui'

Vue.use(Element, { size: 'small' })

new Vue({
  render: h => h(App)
}).$mount('#app')

“需要使用 Vue.use()”并不是说所有第三方库都必须使用它,而是说该库需要通过安装函数完成全局注册、注入或配置。axios 本身是独立的请求库,不依赖 Vue;直接 import 即可使用,也可以由项目自行封装成插件。

Vue 2 中 Vue.use() 的源码逻辑

下面是 Vue 2.7 src/core/global-api/use.ts 中的核心逻辑整理。这里的 Function | Object 是 API 的类型约定,不应理解为运行时会对所有非法值做完整的类型校验:

export function initUse(Vue) {
  Vue.use = function (plugin) {
    const installedPlugins =
      this._installedPlugins || (this._installedPlugins = [])

    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    // Vue.use(plugin, option1, option2) 中除 plugin 外的参数
    const args = toArray(arguments, 1)
    args.unshift(this) // 第一个参数变为 Vue 构造函数

    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }

    installedPlugins.push(plugin)
    return this
  }
}

这段逻辑可以概括为:

  1. 取得当前 Vue 构造函数已经安装过的插件列表。
  2. 如果同一个插件对象已经安装,直接返回,不重复执行。
  3. 把 Vue 构造函数和额外选项传给插件的 install 函数。
  4. 记录插件并返回 Vue 构造函数,使 Vue.use(...).use(...) 这类链式调用成为可能。

插件的安装函数可以注册全局组件、指令、混入、实例属性或其他全局能力。例如:

const plugin = {
  install(Vue, options = {}) {
    Vue.prototype.$message = message => {
      console.log(options.prefix, message)
    }
  }
}

Vue.use(plugin, { prefix: '[app]' })

Vue 3 的当前写法

Vue 3 将全局 API 从 Vue 构造函数迁移到应用实例。插件的安装函数接收 app,而不是 Vue 构造函数:

// plugins/api.js
export default {
  install(app, options = {}) {
    const api = createApi(options.baseURL)

    // 让组件实例可以通过 this.$api 访问
    app.config.globalProperties.$api = api

    // 也可以让 Composition API 通过 inject() 访问
    app.provide('api', api)
  }
}
import { createApp } from 'vue'
import App from './App.vue'
import apiPlugin from './plugins/api'

const app = createApp(App)
app.use(apiPlugin, { baseURL: '/api' })
app.mount('#app')

Vue 3 中常见的插件能力包括:

  • app.component() 注册全局组件。
  • app.directive() 注册全局指令。
  • app.provide() 提供可注入的资源。
  • app.config.globalProperties 添加少量全局实例属性或方法。

globalProperties 应节制使用;如果只是组件树内部共享状态,优先考虑 provide/inject、composable 或 Pinia。插件安装状态也按 app 实例隔离:同一个插件可以分别安装到两个 app 中,而不会像 Vue 2 那样修改同一个 Vue 构造函数的全局状态。

自己编写一个插件

Vue 2 历史写法:

// api-plugin.js
const apiList = {
  login,
  home,
  detail
}

export default {
  install(Vue) {
    Object.defineProperty(Vue.prototype, '$api', {
      get() {
        return apiList
      }
    })
  }
}
import Vue from 'vue'
import apiPlugin from './api-plugin'

Vue.use(apiPlugin)
new Vue({ render: h => h(App) }).$mount('#app')

Vue 3 对应的是应用级安装:

// api-plugin.js
export default {
  install(app) {
    app.config.globalProperties.$api = apiList
    app.provide('api', apiList)
  }
}

<script setup> 中通常使用注入的资源:

<script setup>
import { inject } from 'vue'

const api = inject('api')
</script>

总结

  • import 只是加载模块;插件安装还可能需要注册组件、指令、注入依赖或配置应用。
  • Vue 2 的 Vue.use() 调用 plugin.install(Vue, ...options),并对同一 Vue 构造函数去重。
  • Vue 2 的 Vue.prototype.$api 只会成为组件实例属性,不是通用模块变量。
  • Vue 3 使用 app.use(plugin, ...options)app.config.globalPropertiesapp.provide()
  • axios 不需要安装并不代表它不能被封装成插件,只是它本身不依赖 Vue 的全局注册机制。

官方参考

作者:国服第一摸鱼选手

原文链接:https://juejin.cn/post/7138216381283205128

来源:稀土掘金。著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS