Nuxt

一、简介

​ Nuxt 是一个基于 Vue 生态的更高层的框架,为开发服务端渲染(SSR)的 Vue 应用提供了极其便利的开发体验。

​ Vue.js是开发SPA单页面应用的,传统的SPA应用是将bundle.js从服务端获取,然后在客户端解析并挂载到dom。

​ Nuxt这个框架是用Vue开发多页应用,并在服务端渲染。我们可以将组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记”激活”为客户端上完全可交互的应用程序。

​ Nuxt适合做新闻、博客、电影、咨询这样的需要搜索引擎提供流量的项目。如果你要做到是移动端项目,就没有必要用nuxt这个框架了。

Nuxt是基于Vue.js的服务端渲染框架,可以很好的解决SPA应用程序的首次加载问题。

1
2
3
4
5
6
Nuxt.js有如下特点:
Vue 2 : nuxt是基于Vue2开发的
Vue Router : nuxt整合了路由功能,配置非常简单
VueX : 支持vuex
Vue Server Renderer : 支持服务端渲染
Vue-meta : 支持meta标签配置

Nuxt机制图示

未使用Nuxt:

noNuxt

使用Nuxt:

nuxt

二、创建Nuxt项目

新手模板

确保安装了npx(npx在NPM版本5.2.0默认安装了)

1
$ npx create-nuxt-app <项目名>

或者使用yarn:

1
$ yarn create nuxt-app <项目名>

配置:

启动

1
npm run dev

访问

1
http://localhost:3000

三、Nuxt基本使用

3.1 路由

1
2
3
4
5
6
7
8
9
10
11
12
//pages文件夹下面,文件名即路由

#1.pages下新建search.vue
<template>
<div>
这是search页面
</div>
</template>


//2.index.vue使用路由 此处的search就是所创建的search.vue的文件名
<nuxt-link to="/search">到search页面</nuxt-link>

3.2 嵌套子模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#index.vue

<template>
<section class="container">
<div>
<logo/>
</div>
</section>
</template>

<script>
import Logo from '~/components/Logo.vue'

export default {
components: {
Logo
}
}
</script>

3.3 layouts布局文件

​ 默认情况下所有的页面都会默认使用layouts下面default.vue这个布局文件

1
2
3
4
5
6
7
8
9
#default.vue
<template>
<div>
<h1>头部</h1>
<!--nuxt就相当于 router-view-->
<nuxt/>
<h1>尾巴</h1>
</div>
</template>

当然也可以创建自定义的布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//自定义布局文件
//1.layouts下新建一个user.vue
<template>
<div>
<h1>用户页面头部</h1>
<nuxt/>
<h1>用户页面底部</h1>
</div>
</template>

<script>
export default{

}
</script>

<style lang="css" scoped>
h1{
color:green
}
</style>

//2.pages下的search.vue指定使用哪个布局文件
<script>
export default{
layout:"user"
}
</script>

3.4 全局CSS文件

​ 此css 文件将在所有组件中自动生效

1
2
3
4
5
6
7
8
9
10
11
12
#1.在assets下新建css/main.css

#2.修改nuxt.config.css配置文件后
/*
** global css
*/
css: [
'element-ui/lib/theme-chalk/index.css',
'~assets/css/main.css'
],

#3.重启服务(配置文件的修改,重启后才能生效)

3.5 Vuex的使用

​ 在nuxt中使用vuex的时候无需new Vue.Store实例,nuxt会帮我们自动创建。默认情况下,会使用index.js这个store。

​ 每次修改增加Vuex时要重启服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1.store/index.js
//state存放数据
export const state = () => ({
list: ['a','b'],
user:{}
})
//修改state
export const mutations = {
increment (state,text) {
state.list.push(text);
},
}
//异步提交mutations
export const actions = {

}
1
2
3
4
5
6
7
8
9
10
#2.store/city.js
export const state = () => ({
list: ['c','d']
})

export const mutations = {
increment (state,text) {
state.list.push(text);
}
}

在vue文件中使用vuex

  1. 使用store/index.js中的state时 $store.state.list
  2. 使用store/city.js中的state时 $store.state.city.list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#3.pages/city.vue
<template>
<div>
Page is city
<ul>
<li
v-for="(item,idx) in $store.state.list"
:key="idx"> {{ item }}
</li>

<input
type="button"
value="增加信息index"
@click="addIndex">

<li
v-for="(item,idx) in $store.state.city.list"
:key="idx"> {{ item }}
</li>
</ul>

<input
type="button"
value="增加信息city"
@click="addCity">
</div>
</template>

<script>
import axios from "axios"
export default{
data(){
return {
list:[]
}
},
methods: {
addIndex(){
this.$store.commit('increment', "kk")
},
addCity(){
this.$store.commit('city/increment', "jj")
}
}
}
</script>

四、SPA\SEO\SSR

4.1 SPA:单页面应用

SPA 时代,主要是在客户端端使用了historyhash(主要是为了低版本浏览器的兼容)API,在首次请求经服务端路由输出整个应用程序后,接下来的路由都由前端掌控了,前端通过路由作为中心枢纽控制一系列页面(组件)的渲染(DOM的生成)加载和数据交互。

优点:

  1. 页面之间的切换非常快
  2. 一定程度减少了后端服务器的压力
  3. 实现前后端分离,后端程序只需要提供api,不需要客户端到底是web端还是手机等

缺点:

  1. 首屏打开速度很慢
  2. 不利于SEO搜索引擎优化

4.2 SEO:搜索引擎优化

SEO是一种通过了解搜索引擎的运作规则(如何抓取网站页面,如何索引以及如何根据特定的关键字展现搜索结果排序等)来调整网站,以提高该网站在搜索引擎中某些关键词的搜索结果排名。

由于SPA使用Ajax动态获取数据,很难保证搜索引擎的正常爬取,并且有些搜索引擎不支持js和Ajax获取的数据,因此SSR诞生

4.3 SSR:服务器端渲染

为了解决SPA不支持SEO的问题,我们也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,但是这样的HTML页面还不具备交互能力,所以还需要与SPA框架配合,在浏览器上“混合”成可交互的应用程序。

基本流程:当客户端向服务器发送请求后,web服务器根据路由拿到对应数据渲染并输出,且输出部分中包含两部分:

  1. 路由页对应的页面及已渲染好的数据(解决首屏加载)
  2. 完整的SPA程序代码 (本地路由跳转,而不请求服务器)

优点:

  1. 更好的 SEO(搜索引擎爬虫抓取工具可以直接查看完全渲染的页面)
  2. 更快的内容到达时间 (不用等待所有的JS都下载完成,浏览器便能显示比较完整的页面了)

缺点:

  1. 占用更多的cpu和内存资源
  2. 一些常用的浏览器的api可能无法正常使用 (如:window,document,alert)
  3. 开发调试会有一些麻烦 (因为涉及到了浏览器及服务器,对于SPA的一些组件的声明周期的管理会变得复杂)

4.4 Nuxt对SSR支持

从头搭建一个服务端渲染的应用是相当复杂的。幸运的是,我们有一个优秀的社区项目 Nuxt.js 让这一切变得非常简单。Nuxt 是一个基于 Vue 生态的更高层的框架,为开发服务端渲染的 Vue 应用提供了极其便利的开发体验。

Nuxt.js 是使用 Webpack 和 Node.js 进行封装的基于Vue的SSR框架,预设了利用Vue.js开发服务端渲染的应用所需要的各种配置,使用它你可以不需要自己搭建一套 SSR 程序,而是通过其约定好的文件结构和API就可以实现一个首屏渲染的 Web 应用。

Nuxt.js 主要关注的是应用的 UI渲染。

实现基于 Nuxt.js 的 SSR 应用

五、数据预取

5.1 准备服务器接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#1.server/interface/city.js
var express = require('express')
var router = express.Router()
router.get('/info', function (req, res) {
return res.status(200).json(['北京','天津'])
})
module.exports = router

#2.server/index.js
const cityInterface = require("./interface/city")
//在app.use(nuxt.render)上面添加app.use("/city",cityInterface)
app.use("/city",cityInterface);
app.use(nuxt.render)

#.重启服务
localhost:3000/city/info 测试接口

5.2 不适用数据预取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#1.客户端pages下新建city.vue
<template>
<div>
Page is city
<ul>
<li
v-for="(item,idx) in list"
:key="idx"> {{ item }} </li>
</ul>
</div>
</template>

<script>
import axios from "axios"
export default{
data(){
return {
list:[]
}
},
//async表示方法返回一个Promise
//await 同步等待
async mounted(){
let {status,data} = await axios.get("/city/info");
this.list = data;
console.log(this.list);
}
}
</script>

5.3 Nuxt.js的工作流

1546049173524

  • nuxtServerInit:如果在状态树(store)中指定了 nuxtServerInit 方法,Nuxt.js 调用它的时候会将页面的上下文对象作为第2个参数传给它(仅在服务端调用)。当我们想将服务端的一些数据传到客户端时,这个方法是非常好用的

  • middleware:中间件允许您定义一个自定义函数运行在一个页面或一组页面渲染之前,服务端首屏渲染和路由跳转前均执行对应中间件。可以用作页面跳转时验证用户信息操作(登陆拦截)。

  • asyncData会在组件加载前(限于页面组件)调用,可以在服务端首屏渲染或者在路由跳转时执行,专门用来请求数据,Nuxt.js 会将 asyncData 返回的数据融合组件 data 方法返回的数据一并返回给当前组件。asyncData应该是用于影响SEO的内容,也就是需要让爬虫读取的内容。

5.4 服务端数据预取

nuxtServerInit

nuxtServerInit可以将服务端的数据通过vuex同步到客户端,该方法仅会在服务端首屏渲染时执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#store/index.js
export const state = () => ({
list: [],
})

export const mutations = {
setlist (state,text) {
text.forEach(item=>{
state.list.push(item)
})
},
}

export const actions = {
//将菜单信息写入到vuex实例中
async nuxtServerInit ({ commit }, { req ,app }) {
{
let {status, data} = await app.$axios.get("/city/info");
commit('setlist', data)
}
}
}

#2.pages/city.vue
<template>
<div>
Page is city
<ul>
<li
v-for="(item,idx) in $store.state.list"
:key="idx"> {{ item }} </li>
</ul>
</div>
</template>

asyncData和fetch方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//使用asyncData进行ssr渲染
async asyncData(){
//给data取别名,请求网址写全,因为在刷新浏览器的时候需要全路径请求服务器
let {status,data:list} = await axios.get("http://127.0.0.1:3000/city/info");
if(status == 200) {
//这边不能使用this.list = list,因为获取不到this
//通过return给页面返回数据
return {
list
}
}
}

async fetch ({ store, params }){
//当页面加载时触发可以执行请求来触发action来修改state
//不可以return数据给页面
//页面可以从this.$store中获取action修改的state
let {status,data:list} = await axios.get("http://127.0.0.1:3000/city/info");
store.commit('setlist', list)
}
注意点:asyncData 和 fetch都只能够在页面组件中使用,如果想要在页面组件的子组件中使用ssr,可以使用nuxtServerInit
#注意点:由于 asyncData方法是在组件初始化前被调用的,所以在方法内是没有办法通过this来引用组件的实例。
#注意点:当用户请求页面时候服务端会先使用SSR来生成对应的页面文档结构,而在用户切换路由则是使用了SPA的模式。这意味着如果用户刷新页面,asyncData方法会在服务端执行;如果用户通过nuxt-link路由导航到当前页面,asyncData会在客户端执行

5.5客户端数据预取

当asyncData方法是由路由跳转触发的时候,则使用客户端数据预取,asyncData方法会在客户端执行。可以通过控制台打印来查看是在浏览器执行还是在node执行就可以验证了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async asyncData(context){
//给data取别名,请求网址写全
let {status,data:list} = await axios.get("http://127.0.0.1:3000/city/info");
//如果是客户端执行,则可以使用window对象
//如果是服务端执行,则没有window对象
if(process.client){
console.log(window)
}

if(status == 200) {
//这边不能使用this.list = list,因为获取不到this
//通过return给页面返回数据
return {
list
}
}
}

六、SSR原理

img

官方文档

如上图所示:webpack将 Source 打包出两个bundle文件。

  • 服务端渲染:Server Bundle用于服务端渲染,主要是获取异步数据,同步到组件中,并将组件渲染成HTML返回到前端,但是vue-ssr不能绑定javascript事件,也就是说服务器端使用vue-ssr渲染出来的返回到浏览器的也只能是HTML+CSS。
  • 客户端渲染:Client Bundle 用于客户端渲染,之前说过服务器不能增加事件,那只能前端增加。所以我们看到SSR渲染的网页源码中有window.__NUXT__=...代码,这是后端和前端在使用vuex共享数据。后端从vuex里面取到数据之后渲染成真正的HTML和css返回。客户端也是从vuex里面取到数据,客户端的渲染主要做2件事:
    • 拿到数据,使用 virtual-dom进行预渲染,然后和服务端渲染出来的进行比对,比对两边渲染的内容是不是一致的
    • 对DOM元素的事件进行绑定,也就是回答的问题,事件在这块进行的处理

最后更新: 2019年09月02日 11:22

原始链接: https://HowlCN1997.github.io/2018/06/07/Nuxt/

× 请我吃糖~
打赏二维码