【Vue】服务端渲染框架Nuxt入门

对着官方文档一顿敲= = 看官方文档就好,很简单,相比Vue和React那坑爹的服务端渲染配置,Nuxt简直是良心多了,配置灵活,最重要的是简单!!!于是近期会把 我的个人站首页用nuxt重(折腾)构一番,敬请期待~

Nuxt

Nuxt.js(后文Nuxt)是基于Vue.js的通用应用框架

通过对客户端/服务端基础架构的抽象组织,Nuxt主要关注的是应用的UI渲染。其预设了利用Vue开发服务端渲染的应用所需要的各种配置。
作为框架,Nuxt.js 为 客户端/服务端 这种典型的应用架构模式提供了许多有用的特性,例如异步数据加载、中间件支持、布局支持等。

基本介绍

如何运作

vue2+ vue-router vuex vue-meta
以及webpack、vue-loader、babel-loader来处理代码的自动化构建工作

特性

  • 基于Vue
  • 自动代码分层
  • 服务端渲染
  • 强大的路由功能,支持异步数据
  • 静态服务文件
  • ES6+语法支持
  • 打包和压缩JS/CSS
  • HTML头部标签管理
  • 本地开发支持热加载
  • 集成ESLint
  • 支持样式预处理

下图阐述了 Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过 <nuxt-link> 切换路由渲染页面)的流程:

请求到来 -> nuxt server拦截处理(事件派发store action) -> middleware处理中间件 (先配置,后布局,再页面) -> 验证 -> 获取异步数据 -> render渲染(通过Navigate作路由跳转)

服务端渲染

使用nuxt作UI渲染框架。当运行nuxt命令时会启动一个支持热加载服务端渲染(基于Vue的vue-server-renderer模块)的开发服务器。

静态化

nuxt generate

通过CDN缓存静态页面,进行全球CDN节点布局。相对传统的动态网站,静态化分散了对服务器的请求,降低服务器压力。

简而言之,页面静态文件CDN,数据通过API,前后端分离.

demo

安装

vue init nuxt-community/starter-template nuxt-demo

要注意,服务端渲染是前台服务端用相同组件,一份代码,我们跑的其实是静态文件,所以需要打包出来一份bundle

npm run build

npm run dev

现在浏览器打开 localhost:3000 就可以访问nuxt-demo了

目录结构

  • assets 组织静态资源 css js等

  • components 放公共组件。注:nuxt不会扩展该目录下的组件,所以不会有asyncData特性

  • layouts 用于组织布局,除配置外这个优先级很高

  • middleware 存放中间件

  • pages 组织页面及路由,在layouts后读取,配置这个就不用写routes了. 后面介绍很爽

  • plugins 插件,用于组织需要在根vue.js实例化之前运行的JS插件

  • static 静态文件目录,不会被nuxt调用构建。服务启动,该目录下文件会被映射到根路径下

  • store Vuex状态文件

  • nuxt.config.js nuxt配置,优先级最高,用于覆盖默认配置

更多请参考官方文档 https://zh.nuxtjs.org ~

路由

Nuxt根据pages目录结构自动生成vue-router模块路由配置,就问你叼不叼~

在pages下新建一个user文件夹,新建index.vue one.vue

访问 localhost:3000/user localhost:3000/user/one 就能显示了,简直不要太爽

动态路由

以前path里配置 /:id 这样的,在pages/user 里新建 _id.vue即可

路由参数校验

pages/users/_id.vue

1
2
3
4
5
export default {
validate({params}) {
return /^\d+$/.test(params.id)
}
}

嵌套路由

pages/
–| users/
—–| _id.vue
—–| index.vue
–| users.vue

过渡动效

参考文档 - - 我没跑成功,回头再看看

中间件

允许定义一个自定义函数运行在一个页面或一组页面渲染之前。放在middleware目录下,一个中间件接收context作为第一个参数:

1
2
3
4
5
export default function(context) {
context.userAgent = context.isServer
? context.req.headers['user-agent']
: navigator.userAgent
}

中间件执行顺序:

  1. nuxt.config.js
  2. 匹配布局
  3. 匹配页面

中间件可异步执行,只需返回一个Promise或使用第二个callback作为第一个参数:

middleware/stats.js

1
2
3
4
5
6
7
import axios from 'axios'

export default function({route}) {
return axios.post('http://xx.com', {
url: route.fullPath
})
}

然后在 上面3个任意地方使用中间件:

1
2
3
4
5
module.exports = {
router: {
middleware: 'stats'
}
}

stats中间件将在每个路由改变时被调用。

视图

默认页面

layouts/default.vue

错误页面

layouts/error.vue

个性化布局

在layouts里添加一个dark.vue文件,然后在pages里的组件写.

1
2
3
export default {
layout: 'dark'
}

页面

页面的一些api

属性名 描述
asyncData 最重要的一个键, 支持 异步数据处理,另外该方法的第一个参数为当前页面组件的 上下文对象
fetch 与 asyncData 方法类似,用于在渲染页面之前获取数据填充应用的状态树(store)。不同的是 fetch 方法不会设置组件的数据。详情请参考 关于fetch方法的文档
head 配置当前页面的 Meta 标签, 详情参考 页面头部配置API
layout 指定当前页面使用的布局(layouts 根目录下的布局文件)。详情请参考 关于 布局 的文档
transition 指定当前页面使用的布局(layouts 根目录下的布局文件)。详情请参考 关于 布局 的文档
scrollToTop 布尔值,默认: false。 用于判定渲染页面前是否需要将当前页面滚动至顶部。这个配置用于 嵌套路由的应用场景
middleware 指定页面的中间件,中间件会在页面渲染之前被调用, 请参考 路由中间件

HTML头部

nuxt使用vue-meta更新应用头部标签和html属性

1
2
3
4
5
6
{
keyName: 'head', // 设置meta信息的组件对象的字段,vue-meta会根据key值获取meta信息
attribute: 'n-head', // vue-meta在监听标签时所添加的属性名
ssrAttribute: 'n-head-ssr', // 让vue-meta获知meta信息已完成服务端渲染的属性名
tagIDKeyName: 'hid' // 让vue-meta用来决定是否覆盖还是追加tag的属性名
}

异步数据

Nuxt扩展了Vue增加一个asyncData方法,使得我们可以在设置组件数据前异步获取处理数据

asyncData

该方法会在组件(限于页面组件)每次加载前被调用。它可以在服务端或路由更新之前被调用。在该方法被调用时,第一个参数被设定为当前页面的上下文对象,可利用asyncData方法来获取数据,Nuxt会将asyncData返回的数据融合组件data方法返回的数据一并返回给当前组件。

由于asyncData方法是在组件初始化前被调用的,所以在方法内是无法通过this来引用组件实例对象的。

使用:

  1. 返回Promise
1
2
3
4
5
6
7
8
9
asyncData({params, error}) {
return axios.get(`/api/${params.id}`)
.then(res => {
return {title: res.data.title}
})
.catch(err => {
error({ statusCode: 404, message: 'not found' })
})
}
  1. 使用async/await
1
2
3
4
5
6
7
8
asyncData({params, error}) {
try {
let {data} = await axios.get(`/api/${params.id}`)
return {title: data.title}
} catch (err) {
error({ statusCode: 404, message: 'not found' })
}
}

资源文件

默认Nuxt使用vue-loader、file-loader及url-loader来处理文件的加载和引用。对于不需要通过webpack处理的静态资源文件,可以放置在static目录中

webpack构建

默认情况下, vue-loader自动使用 css-loader 和Vue模板编译器来编译处理vue文件中的样式和模板。在此编译过程中,所有的资源URL例如 <img src="...">background: url(...) 和 CSS中的 @import 均会被解析成模块通过 require 引用。

静态文件

如果你的静态资源文件需要 Webpack 做构建编译处理,可以放到 assets 目录,否则可以放到 static 目录中去。

Nuxt 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下,像 robots.txt 或 sitemap.xml 这种类型的文件就很适合放到 static 目录中。

你可以在代码中使用根路径 / 结合资源相对路径来引用静态资源:

1
2
3
4
5
<!-- 引用 static 目录下的图片 -->
<img src="/my-image.png"/>

<!-- 引用 assets 目录下经过 webpack 构建处理后的图片 -->
<img src="/assets/my-image-2.png"/>

插件

使用第三方模块

在页面内直接 import。但是,如果在另一页面也import相同第三方模块,在打包时该模块会被重复打包,而实际上我们只需要打包一次。该问题可以通过在nuxt.config.js里配置build.vendor来解决:

1
2
3
4
5
module.exports = {
build: {
vendor: ['axios']
}
}

使用Vue插件

plugins里增加相应插件的js文件,如xxx.js

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
plugins: [
// '~/plugins/xxx',
{
src: '~/plugins/xxx',
ssr: false
}
// 有些插件可能只是在浏览器里使用,所以你可以用 ssr: false
// 来配置插件只从客户端还是服务端运行
]
}

使用Vuex

Nuxt已内置了Vuex,所以不需额外安装和use了。

模块方式

状态树还可以拆分成为模块,store 目录下的每个 .js 文件会被转换成为状态树指定命名的子模块

使用状态树模块化的方式,store/index.js 不需要返回 Vuex.Store 实例,而应该直接将 state、mutations 和 actions 暴露出来:

fetch方法

fetch 方法会在渲染页面前被调用,作用是填充状态树 (store) 数据,与 asyncData 方法类似,不同的是它不会设置组件的数据。

nuxtServerInit方法

如果在状态树中指定了nuxtServerInit方法,Nuxt调用它时会将页面的上下文对象作为第2个参数传给它(仅在服务端调用时)。当我们想将服务端数据传到客户端时,就可用此方法。

如,服务端会话状态树可以通过 req.session.user 来访问当前登录用户。将该登录用户信息传给客户端的状态树,只需要更新 store/index.js,如下:

1
2
3
4
5
6
7
actions: {
nuxtServerInit({commit}, {req}) {
if (req.session.user) {
commit('user', req.session.user)
}
}
}

如果你使用状态树模块化的模式,只有主模块(即 store/index.js)适用设置该方法(其他模块设置了也不会被调用)。nuxtServerInit 方法接收的上下文对象和 fetch 的一样,但不包括 context.redirect() 和 context.error()。

命令

命令 描述
nuxt 启动一个热加载的Web服务器(开发模式) localhost:3000
nuxt build 利用webpack编译应用,压缩JS和CSS资源(发布用)
nuxt start 以生成模式启动一个Web服务器 (nuxt build 会先被执行)
nuxt generate 编译应用,并依据路由配置生成对应的HTML文件 (用于静态站点的部署)

大致就这样了,试着自己折腾一下吧。对了,正好可以把个人网站首页用nuxt重构静态发布~~

生命不息,折腾不止!~

Author: Fridolph
Link: http://blog.fridolph.wang/2018/04/17/【Vue】服务端渲染框架Nuxt入门/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.