最近开始学习 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 文件即可。
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
组件。
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
优化后:
未待完续。