各位观众老爷,大家好!今天咱们来聊聊 Nuxt.js 的模块系统,这玩意儿就像乐高积木,能让你的 Nuxt 应用瞬间变得高大上。别怕,听起来高深,其实原理简单粗暴,用起来也相当顺手。
开场白:Nuxt 模块系统是啥?
想象一下,你盖了一座房子,但只有个毛坯房,啥家具、电器都没有。Nuxt 核心就像这毛坯房,提供了基础框架和功能。而 Nuxt 模块就像家具、电器,可以往房子里添砖加瓦,让你的应用功能更丰富。
模块系统的重要性:
- 功能复用: 把常用的功能封装成模块,在多个项目里重复使用,省时省力。
- 代码组织: 将复杂的功能拆分成独立的模块,让代码结构更清晰,易于维护。
- 社区生态: Nuxt 社区里有大量的模块,拿来即用,可以快速构建各种应用。
模块的本质:
Nuxt 模块本质上就是一个 JavaScript 函数,这个函数接收两个参数:
moduleOptions
:模块的配置选项,在nuxt.config.js
中配置。nuxt
:Nuxt 实例,可以访问 Nuxt 的各种 API。
这个函数的主要任务就是利用 Nuxt 提供的 API,对 Nuxt 应用进行配置和扩展。
Nuxt 模块能干啥?
- 注册插件: 自动注册 Vue 插件,例如 Vuex、Axios 等。
- 添加中间件: 自动添加中间件,例如权限验证、日志记录等。
- 配置构建: 修改 Webpack 配置,例如添加 Loader、Plugin 等。
- 创建路由: 动态创建路由,例如根据数据库内容生成页面。
- 注入全局: 向 Vue 实例中注入全局变量或方法,例如
$api
、$config
等。 - 静态文件处理: 复制、转换静态文件,例如图片优化、字体处理等。
- 扩展 CLI: 添加自定义的命令行工具,例如数据导入、代码生成等。
- 修改模板: 修改默认的 HTML 模板。
总之,你能想到的,模块几乎都能干!
Nuxt 模块的生命周期:
Nuxt 模块在 Nuxt 应用启动时被加载和执行,它的生命周期大致如下:
- 加载模块: Nuxt 会读取
nuxt.config.js
中的modules
配置,找到需要加载的模块。 - 执行模块函数: Nuxt 会执行每个模块的函数,并将
moduleOptions
和nuxt
实例作为参数传递给模块。 - 模块配置: 模块函数利用 Nuxt 提供的 API 对应用进行配置和扩展。
- 应用启动: Nuxt 完成所有模块的配置后,启动应用。
自定义模块:手把手教你撸一个
现在,咱们来做一个简单的自定义模块,这个模块会在页面中添加一个版权信息。
1. 创建模块文件:
在项目根目录下创建一个 modules
目录(如果不存在),然后在 modules
目录下创建一个 copyright.js
文件。
// modules/copyright.js
import { resolve } from 'path'
export default function (moduleOptions) {
const options = {
author: 'Your Name',
year: new Date().getFullYear(),
...moduleOptions // 合并用户配置
}
// 1. 添加插件
this.addPlugin({
src: resolve(__dirname, 'plugin.js'), // 插件文件路径
fileName: 'copyright.js', // 插件文件名
options: options // 传递给插件的配置
})
// 2. 监听 'vue-template:extend' 钩子,修改 HTML 模板
this.nuxt.hook('vue-template:extend', (templateOptions) => {
templateOptions.compilerOptions.whitespace = 'condense'
})
// 3. 监听 'build:before' 钩子,在构建之前做一些事情
this.nuxt.hook('build:before', () => {
console.log('Building with copyright module!')
})
}
2. 创建插件文件:
在 modules
目录下创建一个 plugin.js
文件。
// modules/plugin.js
export default (ctx, inject) => {
const copyright = `Copyright © ${ctx.app.$config.copyrightYear} ${ctx.app.$config.copyrightAuthor}. All rights reserved.`
inject('copyright', copyright) // 注入到 Vue 实例
}
3. 在 nuxt.config.js
中配置模块:
// nuxt.config.js
export default {
modules: [
['~/modules/copyright', { // 模块路径
author: '你的大名', // 自定义配置
year: 2023
}]
],
publicRuntimeConfig: {
copyrightAuthor: process.env.NUXT_PUBLIC_COPYRIGHT_AUTHOR || 'Default Author',
copyrightYear: process.env.NUXT_PUBLIC_COPYRIGHT_YEAR || new Date().getFullYear()
}
}
4. 在页面中使用:
<template>
<div>
<h1>Hello, Nuxt!</h1>
<p>{{ $copyright }}</p>
</div>
</template>
代码解释:
modules/copyright.js
:这是模块的主文件,它接收moduleOptions
和nuxt
实例作为参数。this.addPlugin()
:用于注册插件,将plugin.js
注册为 Vue 插件。resolve(__dirname, 'plugin.js')
:用于获取插件文件的绝对路径。fileName: 'copyright.js'
:指定插件的文件名。options
:传递给插件的配置选项。this.nuxt.hook('vue-template:extend', ...)
:使用钩子函数对vue模板进行修改this.nuxt.hook('build:before', ...)
:使用钩子函数在构建前打印log
modules/plugin.js
:这是插件文件,它接收 Vue 上下文ctx
和inject
函数作为参数。inject('copyright', copyright)
:将copyright
变量注入到 Vue 实例中,可以在页面中使用$copyright
访问。
nuxt.config.js
:这是 Nuxt 的配置文件,用于配置模块和选项。modules
:指定需要加载的模块,可以使用字符串或数组。['~/modules/copyright', { ... }]
:指定模块的路径和配置选项。publicRuntimeConfig
: 将配置项暴露给客户端,使得插件可以使用ctx.app.$config.copyrightYear
- 页面:在页面中使用
$copyright
访问模块注入的变量。
运行结果:
在页面中会显示 "Hello, Nuxt!" 和 "Copyright © 2023 你的大名. All rights reserved."。
更深入的例子:集成 Axios
咱们再来一个更实用的例子,集成 Axios,并提供一些默认配置。
1. 创建模块文件:
// modules/axios.js
import axios from 'axios'
export default function (moduleOptions) {
const options = {
baseURL: 'https://api.example.com', // 默认 API 地址
timeout: 10000, // 默认超时时间
...moduleOptions // 合并用户配置
}
// 1. 注册 Axios 插件
this.addPlugin({
src: require.resolve('./axios-plugin.js'),
fileName: 'axios.js',
options: options
})
// 2. 添加 buildPlugin
this.addPlugin({
src: require.resolve('./axios-build.js'),
fileName: 'axios-build.js',
mode: 'server', // 仅在服务器端构建
options: options
})
// 3. 添加 buildPlugin
this.addTemplate({
src: resolve(__dirname, 'axios-utils.js'),
fileName: 'axios-utils.js',
options: options
})
// 4. 注入到 context 和 store
this.nuxt.hook('vue:extend', (options) => {
options.methods = options.methods || {}
options.methods.$request = (url, config) => {
return axios.create(options).request(url, config)
}
})
}
2. 创建 Axios 插件文件:
// modules/axios-plugin.js
import axios from 'axios'
export default (ctx, inject) => {
const options = {
baseURL: ctx.app.$config.axiosBaseURL, // 从 nuxt.config.js 中读取
timeout: ctx.app.$config.axiosTimeout,
...ctx.$config.axios
}
const instance = axios.create(options)
// 请求拦截器
instance.interceptors.request.use(
config => {
// 在发送请求之前做些什么
config.headers['X-Requested-With'] = 'XMLHttpRequest'
return config
},
error => {
// 处理请求错误
return Promise.reject(error)
}
)
// 响应拦截器
instance.interceptors.response.use(
response => {
// 对响应数据做点什么
return response.data
},
error => {
// 处理响应错误
return Promise.reject(error)
}
)
inject('axios', instance) // 注入到 Vue 实例
ctx.$axios = instance // 注入到 context
}
3. 创建 Axios 构建文件 (可选,仅在服务器端使用):
// modules/axios-build.js
import axios from 'axios'
export default (ctx, inject) => {
const options = {
baseURL: ctx.app.$config.axiosBaseURL, // 从 nuxt.config.js 中读取
timeout: ctx.app.$config.axiosTimeout,
...ctx.$config.axios
}
const instance = axios.create(options)
// 请求拦截器 (仅在服务器端)
instance.interceptors.request.use(
config => {
// 在发送请求之前做些什么 (服务器端)
return config
},
error => {
// 处理请求错误 (服务器端)
return Promise.reject(error)
}
)
// 响应拦截器 (仅在服务器端)
instance.interceptors.response.use(
response => {
// 对响应数据做点什么 (服务器端)
return response
},
error => {
// 处理响应错误 (服务器端)
return Promise.reject(error)
}
)
ctx.$axiosServer = instance // 注入到 context (服务器端)
}
4. 创建 Axios Utils 文件 (可选):
// modules/axios-utils.js
export const get = (url, params, config) => {
return this.$axios.get(url, { params, ...config })
}
export const post = (url, data, config) => {
return this.$axios.post(url, data, config)
}
5. 在 nuxt.config.js
中配置模块:
// nuxt.config.js
export default {
modules: [
['~/modules/axios', { // 模块路径
baseURL: 'https://another-api.example.com', // 覆盖默认 API 地址
timeout: 5000 // 覆盖默认超时时间
}]
],
publicRuntimeConfig: {
axiosBaseURL: process.env.NUXT_PUBLIC_AXIOS_BASE_URL || 'https://default-api.example.com',
axiosTimeout: process.env.NUXT_PUBLIC_AXIOS_TIMEOUT || 10000,
axios: {
// 其他 Axios 配置
}
}
}
6. 在页面中使用:
<template>
<div>
<h1>Data from API:</h1>
<pre>{{ data }}</pre>
</div>
</template>
<script>
export default {
async mounted() {
try {
this.data = await this.$axios.get('/data'); // 使用注入的 $axios
// 或者使用
// this.data = await this.$request('/data', {method: 'get'});
} catch (error) {
console.error('Error fetching data:', error);
}
},
data() {
return {
data: null
};
}
};
</script>
代码解释:
modules/axios.js
:模块主文件,注册 Axios 插件,并配置默认选项。modules/axios-plugin.js
:Axios 插件,创建 Axios 实例,并添加请求和响应拦截器。将 Axios 实例注入到 Vue 实例和 context 中。nuxt.config.js
:配置模块和 Axios 选项。- 页面:使用
$axios
发起 API 请求。
Nuxt 模块的进阶技巧:
- 使用
require.resolve()
: 在模块中使用require.resolve()
可以确保找到模块依赖的包,即使这些包没有安装在项目根目录下。 - 使用
this.options
: 在模块中使用this.options
可以访问nuxt.config.js
中的所有配置选项。 - 使用
this.nuxt.callHook()
: 在模块中使用this.nuxt.callHook()
可以触发 Nuxt 的钩子函数,允许其他模块或插件对应用进行修改。 - 使用
addTemplate()
: 用于添加模板文件,这些文件会被复制到.nuxt/
目录下,并可以在应用中使用。
表格:常用 Nuxt 模块 API
API | 描述 | 示例 |
---|---|---|
this.addPlugin() |
添加 Vue 插件 | this.addPlugin({ src: '~/plugins/my-plugin.js', options: { ... } }) |
this.addMiddleware() |
添加中间件 | this.addMiddleware({ src: '~/middleware/auth.js' }) |
this.extendRoutes() |
扩展路由 | this.extendRoutes((routes, resolve) => { routes.push({ path: '/custom', component: resolve(__dirname, 'pages/custom.vue') }) }) |
this.nuxt.hook() |
监听 Nuxt 钩子 | this.nuxt.hook('build:before', () => { console.log('Building...') }) |
this.options |
访问 nuxt.config.js 中的配置 |
console.log(this.options.dev) |
this.addTemplate() |
添加模板文件 | this.addTemplate({ src: '~/templates/my-template.js', fileName: 'my-template.js' }) |
this.requireModule() |
加载其他 Nuxt 模块 | this.requireModule('@nuxtjs/axios') |
this.addModule() |
添加动态模块 | this.addModule({ src: '@nuxtjs/pwa' }) |
总结:
Nuxt 模块系统是扩展 Nuxt 功能的强大工具。通过自定义模块,你可以轻松地将常用的功能封装成独立的模块,并在多个项目里重复使用。希望今天的讲解能帮助你更好地理解 Nuxt 模块系统,并在你的项目中灵活运用。
记住,多实践,多尝试,你也能成为 Nuxt 模块大师! 谢谢大家!