Nuxt 踩坑记

2021-12-28 11:35:17 浏览数 (1)

最近开始学习 Nuxt 框架,写此博文记录学习中遇到的坑。

Nuxt 默认路由与自定义 API 路由

Nuxt 是一个服务端渲染框架,与普通的前后端分离不同(需要同时开两个端口进行开发),而 Nuxt 只需要开一个服务端的端口。默认是 3000。使用 Nuxt 脚手架建立一个 Express.js 模板,打开 server/index.js,发现其中有一行为 app.use(nuxt.render),这行为 Vue-ssr 全部路由的捕获,在这一行下面的所有应用的路由都无法生效,因为 Express 会从上往下检测路由的可达性,一旦有就停止搜索。所以自定义的 API 路由需要放在这行上面。

js

代码语言:javascript复制
1 // bind routes
2  require('../routes/index')(app)
3
4  // Give nuxt middleware to express
5  app.use(nuxt.render)

COPY

内置的axios 与 asyncData

使用 axios 获取数据并挂载

Nuxt 中内置了 axios,并挂载在 Vue 实例中的 $axios 上,通过在 nuxt.config.js 添加配置可以增加前缀,代理等。

js

代码语言:javascript复制
1 axios: {
2    prefix: '/api/', // 前缀
3    proxy: true // 将通过 proxy 对象进行转发
4  },
5 proxy: {
6    '/api/': 'http://api.example.com',
7    '/api2/': 'http://api.another-website.com'
8  }

COPY

一般的我们可以在 this.$axios 获取到 axios 实例,如果你指定了 prefix 属性,在每次请求地址时会自动加上你指定的前缀。

注意:在asyncData中用 this 取得 Vue 实例,因为这时出于 BeforeCreate,你可以通过接受一个参数来取得

js

代码语言:javascript复制
1  async asyncData ({ app }) {
2    const { data } = await app.$axios.get('/user')
3    return { user: data.data }
4  },
5  data () {
6    return {}
7  }

COPY

解构属性 app ,app中取得 axios 实例。在 async 中返回的对象将直接挂载到 data 上。如果 data 中原先有相同的键,将会被覆盖。

axios 拦截器

Nuxt/axios 同样为我们提供了拦截器,与原生的大同小异。

首先在 plugins 中新建一个 axios.js 的文件用于指定 axios 附加配置。

由于没有 axios 实现,所以我们先要接收 axios 实例。

js

代码语言:javascript复制
1export default ({ $axios }) => {
2  $axios.onRequest((config) => {
3    console.log('Making request to '   config.url)
4  })
5
6    $axios.onError((error) => {
7      console.log('error.'   error.response   err.response.status)
8  })
9}

COPY

不用的是,请求拦截器用 onRequest 代替,响应拦截器用 onResponse代替,错误拦截器可以用 onError代替。

除此之外,一共提供了5个拦截器。

代码语言:javascript复制
1  onRequest: [Function: bound onRequest],
2  onResponse: [Function: bound onResponse],
3  onRequestError: [Function: bound onRequestError],
4  onResponseError: [Function: bound onResponseError],
5  onError: [Function: bound onError],

COPY

Nuxt 中的子路由

在 Vue 中,我们可以使用在父组件中引入 <router-view /> 的标签创建一个子路由视图,然后通过 vue-router 来控制 router-view 的显示。

在 Nuxt 中,要实现这样的效果,只需要引入 <nuxt-child />,在 Nuxt 中路由自动生成,文件夹即父路由,文件夹里的即子路由,在外层文件夹中加入一个与路由同名的 Vue 文件即可。

代码语言:javascript复制
1pages
2	index // index 文件夹
3		child.vue  // index 中的子路由
4	index.vue // index 父路由

COPY

Vuex 与 Nuxt

在 Nuxt 中使用 Vuex,只需要在 store 文件夹下建立 index.js 即可。

切记要重启环境。

否则会出现 "(error during evaluation)"的错误。

Nuxt 中的 vuex 会根据文件自动分成若干个模块。这里说几个我遇到的问题。

js

代码语言:javascript复制
1/store/viewport.js
2export const state = () => ({
3  viewport: null
4})
5
6
7export const actions = {
8  updateViewport({ state }) {
9    state.viewport = {
10      w: window.innerWidth,
11      h: window.innerHeight,
12      mobile: window.innerWidth <= 568,
13      laptop: window.innerWidth <= 768,
14      desktop: window.innerWidth <= 1024
15    }
16  }
17}
18
19export default { state, actions }

COPY

store/viewport.js 是一个模块。在 Nuxt 中每个模块又被设定为 namespaced: true,直接用...mapActions(['viewport']) 是不行的,需要加上空间名。这可能会导致错误。[vuex] unknown action type

js

代码语言:javascript复制
1...mapActions({
2      updateViewport: 'viewport/updateViewport'
3    })

COPY

在模块中,state 应返回一个函数,函数返回一个对象。否则会有一个警告。

Nuxt 中默认在开发环境中设定了严格模式,在严格模式下外部不能直接调用 action 去改变 state 的值。

Error: form binding with Vuex - Do not mutate vuex store state outside mutation handlers

我们需要在 store/index.js 下关闭严格模式。

js

代码语言:javascript复制
1export const strict = false

COPY

首次请求渲染页面与 SSR

首次请求的过程总体来说分为以下步骤:

所以注意的是,第一次请求的时候完全是在服务端完成渲染的,在 axios 中根本拿不到 window localStorage 这些对象的,因为在服务器里这些对象根本不存在。

所以我们在配置 axios 的时候,需要关闭 ssr 模式。

nuxt.config.js 中,修改为

js

代码语言:javascript复制
1 plugins: [
2    {
3      src: '~/plugins/axios', // axios 配置文件路径
4      ssr: false
5    }
6  ],

COPY

首次请求携带中文的路由报错

代码语言:javascript复制
1Request path contains unescaped characters 
2Nuxt

COPY

原因是在首次加载的时候是服务端渲染页面的,通过 http 模块去请求接口,携带中文的接口请求时会报错,需要转码一下。

来到前端 api 文件,在请求地址前面加上 encodeURI 函数。如

js

代码语言:javascript复制
1async getWithSlug(category, slug) {
2      const { data } = await $axios.get(encodeURI(`posts/${category}/${slug}`))
3      return data
4}

COPY

按需引入 ElementUI

首先建立 plugins/element-ui.js,在上面加你需要引入的组件

js

代码语言:javascript复制
1import Vue from 'vue'
2import { Message } from 'element-ui'
3import 'element-ui/lib/theme-chalk/icon.css'
4import 'element-ui/lib/theme-chalk/message.css'
5
6Vue.use(Message)

COPY

nuxt.config.js 中加入到插件列表。

当然这还不够,因为 webpack 打包的时候还是会全部引入。

我们需要使用 babel 的 babel-plugin-component 组件。

代码语言:javascript复制
1 yarn add -D babel-plugin-component 

COPY

然后在 nuxt.config.js 中加入 babel 配置。

如果你没有这个对象,就加一个。但是这个列表一定要注意了,新版 babel 必须要,否则会报错。

js

代码语言:javascript复制
1babel: {
2      plugins: [
3        [
4          'component',
5          {
6            libraryName: 'element-ui',
7            styleLibraryName: 'theme-chalk'
8          }
9        ]
10      ]
11    }

COPY

webpack 按需加载 Moment.js 时区

一般的使用 Moment.js 都会引入全部 locale 文件,占了差不多 70% 的大小。但是一般我们只需要一个就完事了。

同样在 webpack 中配置,在 nuxt.config.js 中加入 extend 对象,配置 webpack

加入

js

代码语言:javascript复制
1config.plugins.push(
2        new webpack.ContextReplacementPlugin(/moment[/\]locale$/, /zh-cn/)
3      )

COPY

优化后:

未待完续。

0 人点赞