最近开始学习 Nuxt 框架,写此博文记录学习中遇到的坑。
Nuxt 是一个服务端渲染框架,与普通的前后端分离不同(需要同时开两个端口进行开发),而 Nuxt 只需要开一个服务端的端口。默认是 3000。使用 Nuxt 脚手架建立一个 Express.js 模板,打开 server/index.js,发现其中有一行为 app.use(nuxt.render),这行为 Vue-ssr 全部路由的捕获,在这一行下面的所有应用的路由都无法生效,因为 Express 会从上往下检测路由的可达性,一旦有就停止搜索。所以自定义的 API 路由需要放在这行上面。
js
1 // bind routes
2 require('../routes/index')(app)
3
4 // Give nuxt middleware to express
5 app.use(nuxt.render)COPY
Nuxt 中内置了 axios,并挂载在 Vue 实例中的 $axios 上,通过在 nuxt.config.js 添加配置可以增加前缀,代理等。
js
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
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 中原先有相同的键,将会被覆盖。
Nuxt/axios 同样为我们提供了拦截器,与原生的大同小异。
首先在 plugins 中新建一个 axios.js 的文件用于指定 axios 附加配置。
由于没有 axios 实现,所以我们先要接收 axios 实例。
js
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个拦截器。
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
在 Vue 中,我们可以使用在父组件中引入 <router-view /> 的标签创建一个子路由视图,然后通过 vue-router 来控制 router-view 的显示。
在 Nuxt 中,要实现这样的效果,只需要引入 <nuxt-child />,在 Nuxt 中路由自动生成,文件夹即父路由,文件夹里的即子路由,在外层文件夹中加入一个与路由同名的 Vue 文件即可。
1pages
2 index // index 文件夹
3 child.vue // index 中的子路由
4 index.vue // index 父路由COPY
在 Nuxt 中使用 Vuex,只需要在 store 文件夹下建立 index.js 即可。
切记要重启环境。
否则会出现 "(error during evaluation)"的错误。
Nuxt 中的 vuex 会根据文件自动分成若干个模块。这里说几个我遇到的问题。
js
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
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
1export const strict = falseCOPY
首次请求的过程总体来说分为以下步骤:

所以注意的是,第一次请求的时候完全是在服务端完成渲染的,在 axios 中根本拿不到 window localStorage 这些对象的,因为在服务器里这些对象根本不存在。
所以我们在配置 axios 的时候,需要关闭 ssr 模式。
在 nuxt.config.js 中,修改为
js
1 plugins: [
2 {
3 src: '~/plugins/axios', // axios 配置文件路径
4 ssr: false
5 }
6 ],COPY
1Request path contains unescaped characters
2NuxtCOPY
原因是在首次加载的时候是服务端渲染页面的,通过 http 模块去请求接口,携带中文的接口请求时会报错,需要转码一下。
来到前端 api 文件,在请求地址前面加上 encodeURI 函数。如
js
1async getWithSlug(category, slug) {
2 const { data } = await $axios.get(encodeURI(`posts/${category}/${slug}`))
3 return data
4}COPY
首先建立 plugins/element-ui.js,在上面加你需要引入的组件
js
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 组件。
1 yarn add -D babel-plugin-component COPY
然后在 nuxt.config.js 中加入 babel 配置。

如果你没有这个对象,就加一个。但是这个列表一定要注意了,新版 babel 必须要,否则会报错。
js
1babel: {
2 plugins: [
3 [
4 'component',
5 {
6 libraryName: 'element-ui',
7 styleLibraryName: 'theme-chalk'
8 }
9 ]
10 ]
11 }COPY
一般的使用 Moment.js 都会引入全部 locale 文件,占了差不多 70% 的大小。但是一般我们只需要一个就完事了。
同样在 webpack 中配置,在 nuxt.config.js 中加入 extend 对象,配置 webpack

加入
js
1config.plugins.push(
2 new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn/)
3 )COPY
优化后:

未待完续。