编写 Nuxt 模块代码片段
在本主题中,我们将把已创建的自定义模块拆解为小型代码片段。
|
您可以在我们的 |
使用顶层选项
还记得我们在 "编写基础模块" 章节提到的可传入模块的配置选项吗?模块选项是在 Nuxt 配置文件中注册模块时的顶级选项。我们甚至可以组合不同模块的多个选项,并实现选项共享。让我们通过以下步骤尝试结合使用 @nuxtjs/axios 和 @nuxtjs/proxy 模块的示例:
-
使用
npm同时安装这两个模块:$ npm i @nuxtjs/axios $ npm i @nuxtjs/proxy这两个模块深度整合可避免
CORS问题(我们将在后续开发跨域应用时详细讨论)。虽然无需手动注册@nuxtjs/proxy模块,但必须确保其存在于package.json的依赖项中。 -
在
Nuxt配置文件中注册@nuxtjs/axios模块并设置两个模块的顶级选项:// nuxt.config.js export default { modules: [ '@nuxtjs/axios' ], axios: { proxy: true }, proxy: { '/api/': { target: 'https://jsonplaceholder.typicode.com/', pathRewrite: {'^/api/': ''} }, } }axios自定义选项中的proxy: true会启用@nuxtjs/proxy模块。proxy选项中的/api/: {...}配置指示@nuxtjs/axios模块将 https://jsonplaceholder.typicode.com/ 作为API服务器目标地址,而pathRewrite选项会在HTTP请求时移除地址中的/api/前缀(因为目标API不存在该路由)。 -
在任意组件中无缝使用:
// pages/index.vue <template> <ul> <li v-for="user in users"> {{ user.name }} </li> </ul> </template> <script> export default { async asyncData({ $axios }) { const users = await $axios.$get('/api/users') return { users } } } </script>
通过这种组合使用方式,我们只需书写简化的 API 地址(如 /api/users 替代完整的 https://jsonplaceholder.typicode.com/users )。这使得代码更加整洁,无需每次调用都书写完整 URL。注意配置的 /api/ 前缀会在所有 API 请求中自动添加,因此需要通过 pathRewrite 在发送请求时移除(如前文所述)。
|
更多关于这两个模块的顶级选项信息可参考:
本示例代码片段存放于 |
使用 addPlugin 辅助函数
还记得在 "编写基础模块" 章节中提到的 ModuleContainer 实例和可通过 this 关键字访问的 this.addPlugin 辅助方法吗?在以下示例中,我们将创建一个通过该辅助方法提供 bootstrap-vue 插件的模块,该插件将被注册到 Vue 实例。让我们通过以下步骤创建这个模块片段:
-
安装
Bootstrap和BootstrapVue:$ npm i bootstrap-vue $ npm i bootstrap -
创建插件文件来导入
vue和bootstrap-vue,并使用use方法注册bootstrap-vue:// modules/bootstrap/plugin.js import Vue from 'vue' import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm' Vue.use(BootstrapVue) -
创建模块文件,使用
addPlugin方法添加刚创建的插件文件:// modules/bootstrap/module.js import path from 'path' export default function (moduleOptions) { this.addPlugin(path.resolve(__dirname, 'plugin.js')) } -
在
Nuxt配置文件中添加该bootstrap模块的路径:// nuxt.config.js export default { modules: [ ['~/modules/bootstrap/module'] ] } -
在任意组件中使用
bootstrap-vue,例如创建一个切换警告文本的Bootstrap按钮:// pages/index.vue <b-button @click="toggle"> {{ show ? '隐藏' : '显示' }} 警告 </b-button> <b-alert v-model="show"> 你好 {{ name }}! </b-alert> import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap-vue/dist/bootstrap-vue.css' export default { data () { return { name: 'BootstrapVue', show: true } } }
通过这个模块片段,我们无需在每次需要使用 bootstrap-vue 时都进行导入,因为它已通过上述模块全局添加。我们只需要导入其 CSS 文件。在使用示例中,我们使用 Bootstrap 的自定义 <b-button> 组件来切换 Bootstrap 的自定义 <b-alert> 组件。<b-button> 组件将切换按钮上的 "隐藏" 或 "显示" 文本。
|
有关 |
使用 Lodash 模板
这再次呼应了我们在 "编写基础模块" 章节创建自定义模块时的做法——利用 Lodash 模板通过条件判断块来改变注册插件的输出行为。Lodash 模板正是通过 <%= %> 插值分隔符实现数据属性动态注入的代码块。让我们通过以下步骤尝试另一个简单示例:
-
创建插件文件导入
axios,并添加条件判断块确保为axios提供请求URL,同时在开发模式 (npm run dev) 下将请求结果打印到终端用于调试:// modules/users/plugin.js import axios from 'axios' let users = [] <% if (options.url) { %> users = axios.get('<%= options.url %>') <% } %> <% if (options.debug) { %> // 仅开发环境代码 users.then((response) => { console.log(response); }) .catch((error) => { console.log(error); }) <% } %> export default ({ app }, inject) => { inject('getUsers', async () => { return users }) } -
创建模块文件,使用
addPlugin方法添加刚创建的插件文件,并通过options参数传递请求URL和this.options.dev布尔值:// modules/users/module.js import path from 'path' export default function (moduleOptions) { this.addPlugin({ src: path.resolve(__dirname, 'plugin.js'), options: { url: 'https://jsonplaceholder.typicode.com/users', debug: this.options.dev } }) } -
在
Nuxt配置文件中添加该模块路径:// nuxt.config.js export default { modules: [ ['~/modules/users/module'] ] } -
在任意组件中使用
$getUsers方法,例如:// pages/index.vue <li v-for="user in users"> {{ user.name }} </li> export default { async asyncData({ app }) { const { data: users } = await app.$getUsers() return { users } } }
在此示例中,Nuxt 在复制插件到项目时会用 https://jsonplaceholder.typicode.com/users 替换 options.url。而 options.debug 的条件判断块在生产构建时会被移除,因此在生产模式(npm run build 和 npm run start)下不会在终端看到 console.log 输出。
|
该示例模块片段存放于 |
添加 CSS 库
在《使用 addPlugin 辅助方法》章节的模块片段示例中,我们创建了一个全局集成 bootstrap-vue 插件的模块,使得组件中无需重复导入该插件,如下所示:
// pages/index.vue
<b-button size="sm" @click="toggle">
{{ show ? '隐藏' : '显示' }} 警告
</b-button>
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
export default {
//...
}
看起来相当简洁,因为我们不必每次都导入 bootstrap-vue,而只需要导入 CSS 样式。然而,我们仍然可以通过模块将样式添加到应用程序的全局 CSS 堆栈中来节省几行代码。让我们创建一个新的示例,看看如何通过以下步骤来实现:
-
创建一个模块文件,其中包含一个名为
options的常量变量,用于将模块和顶级选项传递给插件文件,以及一个if条件块,用于确定是否使用普通的JavaScript push方法将CSS文件推送到Nuxt配置文件的css选项中:// modules/bootstrap/module.js import path from 'path' export default function (moduleOptions) { const options = Object.assign({}, this.options.bootstrap, moduleOptions) if (options.styles !== false) { this.options.css.push('bootstrap/dist/css/bootstrap.css') this.options.css.push('bootstrap-vue/dist/bootstrap-vue.css') } this.addPlugin({ src: path.resolve(__dirname, 'plugin.js'), options }) } -
创建插件文件注册
bootstrap-vue,并添加Lodash模板条件块打印模块处理的选项:// modules/bootstrap/plugin.js import Vue from 'vue' import BootstrapVue from 'bootstrap-vue/dist/bootstrap-vue.esm' Vue.use(BootstrapVue) <% if (options.debug) { %> <% console.log (options) %> <% } %> -
在
Nuxt配置中添加模块路径,通过moduleOptions控制CSS注入,并通过顶级选项传递debug模式状态:// nuxt.config.js export default { modules: [ ['~/modules/bootstrap/module', { styles: true }] ], bootstrap: { debug: process.env.NODE_ENV === 'development' ? true : false } } -
移除组件中的
CSS导入语句:// pages/index.vue <script> - import 'bootstrap/dist/css/bootstrap.css' - import 'bootstrap-vue/dist/bootstrap-vue.css' export default { //... } </script>
所以,最终,我们可以在我们的组件中使用 bootstrap-vue 插件及其 CSS 文件,而无需导入所有这些文件。这里是另一个通过模块代码片段将 Font Awesome CSS 选项推送到 Nuxt 配置文件中的快速示例:
// modules/bootstrap/module.js
export default function (moduleOptions) {
if (moduleOptions.fontAwesome !== false) {
this.options.css.push('font-awesome/css/font-awesome.css')
}
}
|
更多关于 完整示例代码存放于 |
注册自定义 webpack 加载器
当我们需要扩展 Nuxt 的 webpack 配置时,通常会在 nuxt.config.js 中使用 build.extend 实现。但通过模块中的 this.extendBuild 方法同样可以达成目标,以下是基础模板:
export default function (moduleOptions) {
this.extendBuild((config, { isClient, isServer }) => {
//...
})
}
例如,假设我们需要通过 svg-transform-loader 扩展 webpack 配置(该 loader 用于修改 SVG 图像的标签和属性),主要功能包括填充颜色(fill)、描边(stroke)等操作。该 loader 支持在 CSS/Sass/Less/Stylus/PostCSS 中使用,例如:
/* 填充白色 */
.img {
background-image: url('./img.svg?fill=fff');
}
/* Sass变量描边,如果你想在 Sass 中使用变量来描边 SVG 图像,你可以这样做: */
$stroke-color: fff;
.img {
background-image: url('./img.svg?stroke={$stroke-color}');
}
让我们创建一个示例模块,将此加载器注册到 Nuxt webpack 的默认配置中,以便我们可以通过以下步骤在我们的 Nuxt 应用程序中操作 SVG 图像:
-
安装
loader:$ npm i svg-transform-loader -
创建模块文件:
// modules/svg-transform-loader/module.js export default function (moduleOptions) { this.extendBuild((config, { isClient, isServer }) => { //... }) } -
在
this.extendBuild回调中移除默认svg规则:const rule = config.module.rules.find( r => r.test.toString() === '/\\.(png|jpe?g|gif|svg|webp)$/i' ) rule.test = /\.(png|jpe?g|gif|webp)$/i -
在前面的代码块之后添加以下代码块,将
svg-transform-loader加载器推送到默认webpack配置的模块规则中:config.module.rules.push({ test: /\.svg(\?.*)?$/, // 匹配img.svg和img.svg?param=value use: [ 'url-loader', 'svg-transform-loader' ] }) -
在
Nuxt配置中注册模块:// nuxt.config.js export default { modules: [ ['~/modules/svg-transform-loader/module'] ] } -
在组件中使用 SVG 转换功能:
<!-- pages/index.vue --> <template> <div> <div class="background"></div> <img src="~/assets/bug.svg?stroke=red&strokewidth=4&fill=blue"> </div> </template> <style lang="less"> .background { height: 100px; width: 100px; border: 4px solid red; background-image: url('~assets/bug.svg?stroke=red&strokewidth=2'); } </style>
|
你可以在 https://www.npmjs.com/package/svg-transform-loader 找到更多关于 svg-transform-loader 的信息。如果你想了解更多关于规则测试的信息,并查看 Nuxt 默认 webpack 配置的完整内容,请访问以下链接:
你可以在我们的 GitHub 仓库的 /chapter-6/nuxt-universal/module-snippets/webpack-loader/ 找到我们刚刚创建的示例模块代码片段。 |
注册自定义 webpack 插件
Nuxt 模块不仅能注册 webpack 加载器,还能通过 this.options.build.plugins.push 注册 webpack 插件,其基础架构如下:
export default function (moduleOptions) {
this.options.build.plugins.push({
apply(compiler) {
compiler.hooks.<hookType>.<tap>('<PluginName>', (param) => {
//...
})
}
})
}
其中 <tap> 依赖钩子类型,钩子类型可以是 tapAsync、tapPromise 或 tap。下面通过 Nuxt 模块创建一个简单的 "Hello World" webpack 插件:
-
使用我们提供的模块/插件架构创建一个模块文件,用于打印 "Hello World!",如下所示:
// modules/hello-world/module.js export default function (moduleOptions) { this.options.build.plugins.push({ apply(compiler) { compiler.hooks.done.tap('HelloWordPlugin', (stats) => { console.log('Hello World!') }) } }) }注:当
done钩子触发时,会传入stats(统计信息)作为参数。 -
在
Nuxt配置中添加模块路径:// nuxt.config.js export default { modules: [ ['~/modules/hello-world/module'] ] } -
运行
npm run dev后,终端将显示 "Hello World!"。
请注意,apply 方法、编译器(compiler)、钩子(hooks)和 taps 都是构建 webpack 插件的关键部分。
|
如果您是 webpack 插件的新手,并想了解更多关于如何开发 webpack 插件的信息,请访问 https://webpack.js.org/contribute/writing-a-plugin/。 您可以在我们的 |
在特定钩子上创建任务
如果你需要在 Nuxt 启动时,在特定的生命周期事件(例如,当所有模块加载完毕后)执行某些任务,你可以创建一个模块并使用 hook 方法来监听该事件,然后执行相应的任务。请看以下示例:
-
如果你想在所有模块加载完成后执行某些操作,请尝试以下方法:
export default function (moduleOptions) { this.nuxt.hook('modules:done', moduleContainer => { //... }) } -
如果你想在渲染器创建完成后执行某些操作,请尝试以下方法:
export default function (moduleOptions) { this.nuxt.hook('render:before', renderer => { //... }) } -
在编译器(默认使用
webpack)启动前执行操作:export default function (moduleOptions) { this.nuxt.hook('build:compile', async ({ name, compiler }) => { //... }) } -
在
Nuxt生成页面之前执行操作:export default function (moduleOptions) { this.nuxt.hook('generate:before', async generator => { //... }) } -
当
Nuxt准备就绪时执行操作:export default function (moduleOptions) { this.nuxt.hook('ready', async nuxt => { //... }) }
以下步骤演示如何创建一个监听 modules:done 钩子的简单模块:
-
创建模块文件,在所有模块加载完成后打印 'All modules are loaded':
// modules/tasks/module.js export default function (moduleOptions) { this.nuxt.hook('modules:done', moduleContainer => { console.log('All modules are loaded') }) } -
创建其他模块,分别打印 'Module 1'、'Module 2' 等:
// modules/module1.js export default function (moduleOptions) { console.log('Module 1') } -
在
Nuxt配置文件中添加钩子模块及其他模块路径:// nuxt.config.js export default { modules: [ ['~/modules/tasks/module'], ['~/modules/module3'], ['~/modules/module1'], ['~/modules/module2'] ] } -
运行
Nuxt应用($npm run dev),终端将输出:Module 3 Module 1 Module 2 All modules are loaded
可见钩子模块始终最后执行,其余模块按模块配置顺序执行。
钩子模块支持异步操作(可使用 async/await 或返回 Promise)。
|
更多关于
示例模块代码存放于 |