在Vue3 <script setup> 中如何调用自定义全局方法

🐶 问题背景

在最近的项目开发中,有一个水印的需求。这个水印功能,在一个已知的 vue2 项目中已经实现,在vue2中,将这个水印封装成了一个 watermark plugin,方法是直接在 vue 实例原型上添加 <math xmlns="http://www.w3.org/1998/Math/MathML"> w a t e r m a r k 对象,这样在项目中,可直接使用 ' t h i s . watermark 对象,这样在项目中,可直接使用 ` this. </math>watermark对象,这样在项目中,可直接使用'this.watermark ` 来调用 watermark 插件中定义的方法。

javascript 复制代码
// Vue2中书写方法
export default {
 set: () => {
   console.log('set watermark')
},
 show: () => {
   console.log('show watermark')
},
 hide: () => {
   console.log('hide watermark')
},
 clear: () => {
   console.log('clear watermark')
},
 install (app, options) {
   const watermark = this
   app.prototype.$watermark = watermark
}
}

在vue3中已不支持以 Vue.prototype 的形式直接在 Vue 对象原型上添加自定义属性了,但是提供了另一种注册全局属性的方法:app.config.globalProperties

一个用于注册能够被应用内所有组件实例访问到的全局属性的对象。

这是对 Vue 2 中 Vue.prototype 使用方式的一种替代,此写法在 Vue 3 已经不存在了。与任何全局的东西一样,应该谨慎使用。

在 Vue3 中新的插件修改为如下形式:

javascript 复制代码
const watermark = {
 set: () => {
   console.log('set watermark')
},
 show: () => {
   console.log('show watermark')
},
 hide: () => {
   console.log('hide watermark')
},
 clear: () => {
   console.log('clear watermark')
},
 install: function (app, options) {
   const watermark = this
   // Here is the plugin register operation
   app.config.globalProperties.$watermark = watermark
}
}
​
export default watermark

然后在 vue项目的入口文件(main.js)中安装插件即可:

javascript 复制代码
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import watermark from './plugin/watermark.js'
​
const app = createApp(App)
app
.use(watermark)
.mount('#app')

接下来使用一下,在 App.vue 中编写代码,测试一下该方法:

xml 复制代码
<template>
 <div>
   <a href="https://vitejs.dev" target="_blank">
     <img src="/vite.svg" class="logo" alt="Vite logo" />
   </a>
   <a href="https://vuejs.org/" target="_blank">
     <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
   </a>
 </div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
export default {
 name: 'App',
 created () {
   this.$watermark.set() // we call the watermark.set() function
}
}
</script>

结果为:

可以看到,这里这个方法成功调用了。

我们使用的是选项式写法,可以直接通过访问this的形式调用。

但是,我的vue3新项目使用的是

🐹 解决方法

以下这个问题的2种解决方案:

1. getCurrentInstance

getCurrentInstance 方法是 vue3 提供的一个 api, 该api可以访问组件内部实例。

(不过现在查看官方文档时,已查询不到关于该api的信息,早在以前的官方说明中就已经指出,该api仅暴露给高阶使用场景,反对在业务代码中把此方案当做获取this的替代方案)。

xml 复制代码
<!--App.vue-->
<template>
 <div>
   <a href="https://vitejs.dev" target="_blank">
     <img src="/vite.svg" class="logo" alt="Vite logo" />
   </a>
   <a href="https://vuejs.org/" target="_blank">
     <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
   </a>
 </div>
 <HelloWorld msg="Vite + Vue" />
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import { getCurrentInstance } from 'vue'
​
const internalInstance = getCurrentInstance()
internalInstance.appContext.config.globalProperties.$watermark.set()
</script>

以下是结果:

可以看到,该方法能正常调用我们的自定义全局方法。

不过,既然该方案官方已明确不推荐在应用系统中调用,那我们也应该避免使用。

2. Provide/Inject (依赖注入)

🐮Vue 依赖/注入

同样是插件,在遇到这一问题时,我首先想到我们最常用的依赖如 VueRouter 是怎么做的呢?在 vue-router 4.x 中,vue-router 暴露了2个 composition api 来获取 route 和 router。

javascript 复制代码
import { useRoute, useRouter } from 'vue-router'
​
const route = useRoute()
const router = useRouter()

同样,我们的插件也可以如此实现。于是,查看了一下 vue-router 的源码文件,发现 vue-router 是基于 vue 中的依赖注入实现的。

javascript 复制代码
// vue-router.mjs
const createRouter = function () {
 ...
 const router = {
   ...
   install (app) {
   const router = this
   ...
   app.provide(routerKey, router)
}
}
 return router
}
​
const useRouter = function () {
 return inject(routerKey)
}

同样的,我们使用与 vue-router 相同的方法来实现:

javascript 复制代码
// src/plugin/watermark.js
import { inject } from 'vue'
const watermark = {
 set: () => {
   console.log('set watermark')
},
 show: () => {
   console.log('show watermark')
},
 hide: () => {
   console.log('hide watermark')
},
 clear: () => {
   console.log('clear watermark')
},
 install (app, options) {
   const watermark = this
   app.config.globalProperties.$watermark = watermark
   app.provide('watermark', watermark)
}
}
​
const useWaterMark = function () {
 return inject('watermark')
}
​
export {
 watermark,
 useWaterMark
}
xml 复制代码
<!--App.vue-->
<template>
 <div>
   <a href="https://vitejs.dev" target="_blank">
     <img src="/vite.svg" class="logo" alt="Vite logo" />
   </a>
   <a href="https://vuejs.org/" target="_blank">
     <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
   </a>
 </div>
 <HelloWorld msg="Vite + Vue" />
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import { getCurrentInstance } from 'vue'
import { useWaterMark } from './plugin/watermark.js'
​
const watermark = useWaterMark()
watermark.hide()
</script>

以下是实现结果:

在应用中使用时,在vue3中推荐使用依赖注入的方式来访问我们的全局属性和方法。

🐻附录(示例源码)

源码

相关推荐
一只小阿乐4 小时前
前端web端项目运行的时候没有ip访问地址
vue.js·vue·vue3·web端
计算机学姐4 小时前
基于python+django+vue的旅游网站系统
开发语言·vue.js·python·mysql·django·旅游·web3.py
.ccl4 小时前
web开发 之 HTML、CSS、JavaScript、以及JavaScript的高级框架Vue(学习版2)
前端·javascript·vue.js
小徐不会写代码4 小时前
vue 实现tab菜单切换
前端·javascript·vue.js
2301_765347545 小时前
Vue3 Day7-全局组件、指令以及pinia
前端·javascript·vue.js
辛-夷5 小时前
VUE面试题(单页应用及其首屏加载速度慢的问题)
前端·javascript·vue.js
刘志辉7 小时前
vue传参方法
android·vue.js·flutter
dream_ready7 小时前
linux安装nginx+前端部署vue项目(实际测试react项目也可以)
前端·javascript·vue.js·nginx·react·html5
编写美好前程7 小时前
ruoyi-vue若依前端是如何防止接口重复请求
前端·javascript·vue.js
喵喵酱仔__7 小时前
阻止冒泡事件
前端·javascript·vue.js