Nuxt入门实践

282 阅读5分钟

Nuxt 是一个基于 Vue 的服务端渲染框架,它的用法与直接使用 Vue 构建单页面应用有很大的不同。主要体现在:

  • 可以编写后台中间件
  • 文件目录决定路由
  • 添加了特有的生命周期,数据获取可以在服务器阶段执行。
  • 插件自动注入

本文以建立一个项目为线索,详细讲述了一次 Nuxt 的入门实践。

建立项目

通过 npm 创建项目:

npm init nuxt-app project-name

根据需求选择需要的特性。

路由

Nuxt 使用 vue-router 管理路由,但是不需要手动编写路由表。pages 目录下的 vue 和 js 文件会按照路径自动配置为路由,也称为页面

比如,默认的pages/index.vue配置成/pages/a.vue配置成/apages/a/index.vue也配制成/apages/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 实例作为了一个中间件。

参考资料