likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 17

🐶 问题背景

在最近的项目开发中,有一个水印的需求。这个水印功能,在一个已知的 vue2 项目中已经实现,在vue2中,将这个水印封装成了一个 watermark plugin,方法是直接在 vue 实例原型上添加 watermark对象,这样在项目中,可直接使用‘this.watermark 对象,这样在项目中,可直接使用 ` this.watermark对象,这样在项目中,可直接使用this.watermark ` 来调用 watermark 插件中定义的方法。

// 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 中新的插件修改为如下形式:

const watermark = {
 set: () => {
   console.log('set watermark')
},
 show: () => {
   console.log('show watermark')
},
 hide: () => {
   console.log('hide watermark')
},
 clear: () => {
   console.log('clear watermark')
},
 installfunction (app, options) {
   const watermark = this
   // Here is the plugin register operation
   app.config.globalProperties.$watermark = watermark
}
}

export default watermark

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

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 中编写代码,测试一下该方法:

<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>

结果为:

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

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

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

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

🐹 解决方法

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

1. getCurrentInstance

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

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

<!--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>

以下是结果:

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

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

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

2. Provide/Inject (依赖注入)

🐮Vue 依赖/注入

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

import { useRoute, useRouter } from 'vue-router'

const route = useRoute()
const router = useRouter()

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

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

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

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

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

// 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
}
<!--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 <script setup> 中如何调用自定义全局方法

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

🐻附录(示例源码)

源码