在 Nuxt 中编写全局函数
在 Nuxt 中,我们可以通过以下三种方式创建 "插件" 或全局函数:
-
注入
Vue实例(客户端)// plugins/<function-name>.js import Vue from 'vue' Vue.prototype.$<function-name> = () => { // 函数实现... } -
注入
Nuxt上下文(服务端)// plugins/<function-name>.js export default (context, inject) => { context.app.$<function-name> = () => { // 函数实现... } } -
同时注入
Vue实例和Nuxt上下文// plugins/<function-name>.js export default (context, inject) => { inject('<function-name>', () => { // 函数实现... }) }
通过这些标准化格式,您可以轻松为应用创建全局函数。在接下来的章节中,我们将通过具体示例带您逐步实践。现在让我们开始吧!
将函数注入到 Vue 实例中
在这个示例中,我们将创建一个实现两数相加的函数(例如 1+2=3),并将其注入 Vue 实例。具体步骤如下:
-
在
/plugins/目录下创建一个.js文件,导入Vue,并将函数附加到Vue.prototype上:// plugins/vue-injections/sum.js import Vue from 'vue' Vue.prototype.$sum = (x, y) => x + y -
配置
Nuxt插件 在配置文件中注册插件路径:// nuxt.config.js export default { plugins: ['~/plugins/vue-injections/sum'] } -
全局调用函数 可在任意页面中使用该函数,例如:
// pages/vue-injections.vue <template> <p>{{ this.$sum(1, 2) }}</p> <p>{{ sum }}</p> </template> <script> export default { data() { return { sum: this.$sum(2, 3) } } } </script> -
在浏览器中运行该页面,你应该在屏幕上看到以下输出(即使你刷新页面):
3 5
将函数注入到 Nuxt 上下文中
在这个示例中,我们将创建一个计算数值平方的函数(例如 5*5=25),并通过以下步骤将其注入 Nuxt 上下文:
-
创建插件文件 在
/plugins/目录下创建.js文件,将函数挂载至 context.app:// plugins/ctx-injections/square.js export default ({ app }, inject) => { app.$square = (x) => x * x } -
配置
Nuxt插件 在配置文件中注册插件路径:// nuxt.config.js export default { plugins: ['~/plugins/ctx-injections/square'] } -
在页面中使用函数 可在任意能访问上下文的场景调用,例如
asyncData方法:// pages/ctx-injections.vue <template> <p>{{ square }}</p> </template> <script> export default { asyncData(context) { return { square: context.app.$square(5) } } } </script> -
运行效果 页面刷新后将始终显示:
25
重要说明:
-
asyncData方法在页面组件初始化前执行,因此:-
无法通过
this访问Vue实例 -
不能调用注入
Vue实例的函数(如前例的$sum函数)
-
-
上下文注入的函数(如本示例的
$square)同样不能在Vue生命周期钩子/方法(如mounted、updated等)中使用
如果需要创建同时支持 this 和上下文调用的函数,我们将在下一节演示如何将其同时注入 Vue 实例和 Nuxt 上下文。
将函数同时注入到 Vue 实例和 Nuxt 上下文中
在本示例中,我们将创建一个计算两数乘积的函数(如 2*3=6),并通过以下步骤同时注入 Vue 实例和 Nuxt 上下文:
-
创建插件文件 使用
inject方法封装函数(自动添加$前缀):// plugins/combined-injections/multiply.js export default ({ app }, inject) => { inject('multiply', (x, y) => x * y) }请注意,
$会自动添加到你的函数名前面,所以你无需担心自己添加。 -
配置
Nuxt插件// nuxt.config.js export default { plugins: ['~/plugins/combined-injections/multiply'] } -
在你拥有
context和this(Vue实例)访问权限的任何页面上使用该函数,例如:// pages/combined-injections.vue <template> <p>{{ this.$multiply(4, 3) }}</p> <p>{{ multiply }}</p> <button @click="updateStore">更新Store</button> </template> <script> export default { asyncData(context) { return { multiply: context.app.$multiply(2, 3) } }, methods: { updateStore() { this.$store.dispatch('setNumbers') } } } </script> -
运行效果 页面加载显示:
12 6该函数可在
Vue生命周期的任意钩子中使用,例如:mounted() { console.log(this.$multiply(5, 3)) // 浏览器控制台将输出15 }此外,该函数还可通过
this在Vuex Store的以下场景调用:-
actions对象中的方法 -
mutations属性中的方法
我们将在第 10 章《集成
Vuex状态管理》中详细探讨Vuex的相关用法。 -
-
创建一个
.js文件并将以下函数封装在actions和mutations对象中:// store/index.js export const state = () => ({ xNumber: 1, yNumber: 3 }) export const mutations = { changeNumbers(state, newValue) { state.xNumber = this.multiply(3, 8) state.yNumber = newValue } } export const actions = { setNumbers({ commit }) { const newValue = this.multiply(9, 6) commit('changeNumbers', newValue) } } -
在任何你喜欢的页面上使用上述
store的action方法(即setNumbers),例如以下示例:<template> <div> <p>{{ $store.state }}</p> <button class="button" @click="updateStore"> Update Store </button> </div> </template> <script> export default { methods: { updateStore() { this.$store.dispatch('setNumbers') } } } </script> -
在浏览器中运行该页面,屏幕上应该显示以下输出(即使刷新页面也会保持):
{ "xNumber": 1, "yNumber": 3 } -
点击 "更新 Store" 按钮后,带有上述数字的
store默认状态将更改为:{ "xNumber": 24, "yNumber": 54 }
这很棒。通过这种方式,我们可以编写一个在客户端和服务器端都能工作的插件。但有时我们需要专门在服务器端或客户端单独使用的函数。要做到这一点,我们必须指示 Nuxt 如何专门运行我们的函数。所以在下一节中,让我们了解如何实现这一点。
注入仅客户端或仅服务器的插件
在这个示例中,我们将创建一个用于两数相除的函数(例如 8 / 2 = 4),以及另一个用于两数相减的函数(例如 8 - 2 = 6)。我们将第一个函数注入 Vue 实例并限定其仅在客户端使用,而第二个函数则注入 Nuxt 上下文并限定其仅在服务端使用:
-
创建两个函数文件,分别添加
.client.js和.server.js后缀:// plugins/name-conventions/divide.client.js import Vue from 'vue' Vue.prototype.$divide = (x, y) => x / y // plugins/name-conventions/subtract.server.js export default ({ app }, inject) => { inject('subtract', (x, y) => x - y) }以
.client.js结尾的函数文件将仅在客户端运行,而以.server.js结尾的函数文件则仅在服务端运行。 -
在
Nuxt配置文件的plugins属性中添加这两个文件路径:// nuxt.config.js: export default { plugins: [ '~/plugins/name-conventions/divide.client.js', '~/plugins/name-conventions/subtract.server.js' ] } -
在任意页面中使用这些插件,例如:
// pages/name-conventions.vue <p>{{ divide }}</p> <p>{{ subtract }}</p> export default { data () { let divide = '' if (process.client) { divide = this.$divide(8, 2) } return { divide } }, asyncData (context) { let subtract = '' if (process.server) { subtract = context.app.$subtract(10, 4) } return { subtract } } } -
在浏览器中运行该页面,屏幕上将显示以下输出:
4 6
请注意:当首次访问或刷新页面时会得到上述结果。但如果通过 <nuxt-link> 导航到该页面,则只会显示:
4
特别提醒:我们必须用 process.client 条件语句包裹 $divide 方法,因为这是仅限客户端的函数。如果移除该条件判断,浏览器将报服务端错误:
this.$divide is not a function
同理,$subtract 方法必须用 process.server 条件语句包裹。如果移除该条件判断,浏览器将报客户端错误:
this.$subtract is not a function
虽然每次使用时都包裹条件判断可能不够理想,但在那些仅客户端调用的 Vue 生命周期钩子/方法(如 mounted 钩子)中,可以安全地省略 process.client 条件判断:
mounted () {
console.log(this.$divide(8, 2))
}
浏览器控制台将输出 4。以下表格展示了八个 Vue 生命周期钩子/方法,值得注意的是在 Nuxt 应用中只有其中两个会在两端都被调用:
| 客户端和服务端 | 仅客户端 |
|---|---|
|
|
需要注意的是,我们在 Vue 和 Nuxt 应用中使用的 data 方法与 asyncData 方法一样,会在客户端和服务端同时被调用。因此,对于专为客户端设计的 $divide 方法,您可以在 "仅客户端"(ClientOnly)的钩子列表中不加条件判断地使用它。而专为服务端设计的 $subtract 方法,则可以在 nuxtServerInit 操作中安全地不加条件判断使用,如下例所示:
export const actions = {
nuxtServerInit ({ commit }, context) {
console.log(context.app.$subtract(10, 4))
}
}
当应用在服务端运行时(包括刷新任意页面时),您将获得输出结果 6。需要特别说明的是,Nuxt 上下文只能通过以下两种方法访问:nuxtServerInit 和 asyncData。其中 nuxtServerInit 操作将上下文作为第二个参数访问,而 asyncData 方法将其作为第一个参数访问。我们将在第 10 章《添加 Vuex Store》中详细讲解 nuxtServerInit 操作,不过现在,在下一节中,我们将重点介绍在 nuxtServerInit 操作之后、但在 Vue 实例和插件初始化之前,以及在你刚刚学习过的 $root 和 Nuxt 上下文注入函数之前被注入的 JavaScript 函数。这类函数被称为 Nuxt 模块,通过本章的学习,您将掌握编写这些模块的方法。让我们开始吧。