在 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
模块,通过本章的学习,您将掌握编写这些模块的方法。让我们开始吧。