Nuxt 是一个基于 Vue 的服务端渲染框架,它的用法与直接使用 Vue 构建单页面应用有很大的不同。主要体现在:
- 可以编写后台中间件
- 文件目录决定路由
- 添加了特有的生命周期,数据获取可以在服务器阶段执行。
- 插件自动注入
本文以建立一个项目为线索,详细讲述了一次 Nuxt 的入门实践。
建立项目
通过 npm 创建项目:
npm init nuxt-app project-name
根据需求选择需要的特性。
路由
Nuxt 使用 vue-router 管理路由,但是不需要手动编写路由表。pages 目录下的 vue 和 js 文件会按照路径自动配置为路由,也称为页面。
比如,默认的pages/index.vue配置成/,pages/a.vue配置成/a,pages/a/index.vue也配制成/a,pages/a/b.vue配置成/a/b。
忽略路径
这带来方便,也带来麻烦。因为如果我们在 pages 文件夹中编写供页面用的组件,比如/pages/a/components/button.vue,那么这个组件也会被配置为路由/a/components/button,这不是我们的本意。我们可以通过配置忽略项来避免这种情况。
比如,我们规定一些常用的文件夹中的文件不需要配置为路由,可以在根目录的 .nuxtignore 文件中配置:
**/~components
**/~styles
**/~utils
**/~data
**/~images
**/~types
注意:.nuxtignore 文件只对 pages 目录有效。
手动配置
如果需要手动设置路由,可以在 nuxt.config.js 文件中配置 router.extendRoutes:
export default {
router: {
extendRoutes(routes, resolve) {
routes.push({
path: '/',
component: resolve(__dirname, 'pages/home/index.vue')
})
}
},
}
这会在原本自动生成的路由的基础上扩展。
跳转
与单页面应用中使用router-link不同的是,nuxt 内部路由跳转使用 NuxtLink 组件,在 to 属性中传递 URL,也可以使用 a 标签,但是会失去一些 nuxt 的特性。
组件
components 目录中的第一层 vue 文件或包含 index.vue 的文件夹会被自动编译为组件,并且在页面 vue 中使用时不需要显示注册。
比如有这样一个文件:components/button.vue,那在页面 vue 中可以直接使用 <Button/>。
使用时可以使用连字符或帕斯卡命名方式,如果组件声明了name属性,那么使用该属性作为组件名,否则使用文件名。
如果是在 pages 中编写的组件,需要使用普通方法在页面中注册后使用。
模板
模板是指每个页面的都会有的结构,比如默认模板为:
<template>
<Nuxt />
</template>
<Nuxt />表示页面内容的插入位置,类似于router-view。在 pages 中的页面组件匹配了路由会被插入到这个位置。
修改默认模板
如果要修改默认模板,可以在根目录下创建layouts/default.vue文件,然后修改内容。比如:
<template>
<Nuxt />
<Toolbar />
</template>
这样,每个页面都会有我们自定义的Toolbar组件。此处使用 components 中的组件也不需要显式注册。
创建新模板
可以在 layouts 文件夹中创建新模版,结构与上述类似,必须包含 <Nuxt />。
需要在页面中使用这个模板时,可以在页面中配置layout: 'layout-name'。
样式
除了在组件和模板中编写样式外,也可以编写全局样式,在 nuxt.config.js 文件中配置引入:
export default {
css: ['~/styles/index.scss'],
}
Nuxt 会根据样式文件的扩展名来自动调用相应的解析器。比如解析 scss 需要安装:
npm install --save-dev sass sass-loader
插件
在编写页面和组件时,常需要一些小工具,即代码片段,比如解析查询字符串的 qs。
一般我们可以用模块导入的方式使用:
import Qs from 'qs'
有时我们想要这些工具不需要导入就可用,特别是在编写 template 时。我们可以使用 nuxt 的插件。
在 plugins 文件夹中编写插件,比如:
qs.js
import Qs from "qs";
const plugin = (context, inject) => {
inject("qs", Qs)
}
export default plugin;
然后在 nuxt.config.js 文件中注册插件:
export default {
plugins: [
'~/plugins/qs.js',
],
}
这样 qs 会被自动注入到 vue 组件中,以前缀$的成员形式使用。比如:
<template>
<div>Url query: {{ $qs.stringify(query)}}</div>
<template>
<script>
export default {
data(){
return {
query: this.$qs.parse(window.location.search.replace('?', ''))
}
}
}
</script>
如果某些插件引入了只能在浏览器或 node 使用的包,不希望注入到不能使用的环境,我们可以指定只注入浏览器或 node。比如:
const config = {
plugins: [
{ src: '~/plugins/qs.js', mode: 'client' },
],
}
mode的值,client表示浏览器,server表示 node,即后台。
Store
Vuex 写在 store 目录中,其中的每个 js 文件或文件夹中的 index.js 文件会被认为是一个模块并被自动注册。
store/index.js比较特殊,它可以声明一个会只在服务端执行的生命周期函数nuxtServerInit,用来初始化状态。比如:
const actions = {
async nuxtServerInit({ commit }, { req }) {
commit('user/set', req.session.user)
}
}
}
export { actions }
后台中间件
作为一个 SSR 应用,我们经常需要处理请求状态和转发数据接口。我们可以在 server-middleware 目录中编写中间件。比如,我们需要记录日志:
log.js
export default async function(req, res, next) {
log(req) // 记录请求
next()
}
再在 nuxt.config.js 文件中注册:
export default {
serverMiddleware: ['~/server-middleware/log.js'],
}
默认情况下,所有请求都会经过这个中间件,包括 XHR,我们可以对其进行代理转发。
完整的服务器
如果我们需要建立一个完整的服务器,可能需要类似 express 的框架,这一点 nuxt 也是支持的。
示例:
server.js
import Express from 'express'
const app = Express()
app.use(Express.json())
app.use(Express.raw({ limit: '5000kb' }))
app.use(Express.urlencoded({ extended: false }))
app.use(Express.text())
app.use('/api', async (req, res, next) => {
let data = await request(req) // 转发请求
res.json(data)
})
export default app
在 nuxt.config.js 文件中注册:
export default {
serverMiddleware: ['~/server-middleware/server.js'],
}
这样,nuxt 就把这个 express 实例作为了一个中间件。