Nuxt.js 简单介绍
Nuxt.js 是一个基于 Vue.js 的服务端渲染框架。
Vue SSR 的原理图
Nuxt.js的优势
- 无需为了路由划分而烦恼,你只需要按照对应的文件夹层级创建 .vue 文件就行
- 无需考虑数据传输问题,Nuxt 会在模板输出之前异步请求数据(需要引入 axios 库),而且对 vuex 有进一步的封装
- 内置了 webpack,省去了配置 webpack 的步骤,nuxt 会根据配置打包对应的文件
下图阐述了 Nuxt.js 应用一个完整的服务器请求到渲染(或用户通过 <nuxt-link>
切换路由渲染页面)的流程:
-
Incoming Request:浏览器发出一个请求
-
nuxtServerInit:服务端接收到请求,检查当前有没有nuxtServerInit这个配置项,如果有的化就先执行这个方法,这个方法是用来操作vuex的
-
middleware:这是一个中间件,跟路由相关,这里可以做任何想要的功能
-
validate():验证,配合高级动态路由去做一些验证,比如A页面是否允许跳转到B页面去,如果没有验证通过可以跳转到别的页面之类的校验
-
asyncData()&fetch():获取数据。asyncData()获取的数据是用来渲染vue组件的,fetch通常用来修改vuex的数据
-
Render:渲染
-
Naviage:如果发起了一个非Server 的路由,那么在执行一遍middleware——Render的过程
主要说明
asyncData
asyncData
方法是 Nuxt.js 对 Vue 扩展的方法。 会在组件(限于页面组件)每次初始化前被调用的。所以该方法没有办法通过this
来引用组件的实例对象。它可以在服务端或路由更新之前被调用。可以利用asyncData
方法来获取数据并返回给当前组件。
asyncData (context) {
return { project: 'nuxt' }
}
asyncData
的参数(context
)有哪些?
- app:app 对象
- store:存储,可以拿到store里的数据
- route:路由,可以拿到参数之类的
- params:url 路径参数,主要获取id
- query:可以理解为url上问号后面的参数
- env:运行环境
- isDev:是否是开发环境
- isHMR:是否是热更新
- redirect:重定向
- error:错误
asyncData 可以做什么?
创建渲染器
Nuxt renderer 使用vue-server-renderer
插件创建渲染器并解析 webpack 打好的 bundle 文件
fetch
fetch
方法用于渲染页面前填充应用的状态树(store)数据,与asyncData
方法类似,不同的是它不会设置组件的数据。
async fetch({ store, params }) {
const { data } = axios.get('http://abc.com/api/stara')
store.commit('setStars/set', data)
}
fetch或者asyncData这2个方法在layouts和components中是失效的。
server.js做了哪些事情
每个用户通过浏览器访问 Vue 页面时,都是一个全新的上下文,但在服务端,应用启动后就一直运行着,处理每个用户请求的都是在同一个应用上下文中。为了不串数据,需要为每次 SSR 请求,创建全新的 app, store, router。
我们主要关注最后一部分asyncData部分
首先会根据上下文环境中的url调用 将匹配的component返回
接着遍历每个component,根据component的asyncData配置,执行 promisify()来promise化asyncData方法并将上下文对象赋给asyncData方法
promisify()方法接受两个参数:第一个组件中配置的asyncData()方法;第二个是挂载到新vue实例上的上下文对象
执行后通过方法将得到的数据同步一份给页面中定义的data,asyncData只是在首屏的时候调用一次,后续交互还是交给client处理
window和document对象的使用
在开发nuxt项目的时候,我们难免会使用到window
或者document
来获取dom元素。如果直接在文件中使用就会报错。这是因为window
或者document
是浏览器端的东西服务端并没有。
解决方法:我们只需要在使用的地方通过process.browser
/process.server
来判断
if (process.browser) {
// 浏览器中运行代码
}
nuxtServerInit
如果在状态树中指定了 nuxtServerInit
方法,Nuxt.js 调用它的时候会将页面的上下文对象作为第 2 个参数传给它(服务端调用时才会酱紫哟)。当我们想将服务端的一些数据传到客户端时,这个方法是灰常好用的。
举个例子,假设我们服务端的会话状态树里可以通过 req.session.user
来访问当前登录的用户。将该登录用户信息传给客户端的状态树,我们只需更新 store/index.js
如下:
actions: {
nuxtServerInit ({ commit }, { req }) {
if (req.session.user) {
commit('user', req.session.user)
}
}
}
性能优化
keep-alive
nuxt
在 1.2.0
上添加了这个功能的。在layouts/default.vue
:
<template>
<Nuxt keep-alive/>
</template>
服务端渲染合理应用
应用到的特性主要包括asyncData
异步获取数据、mounted
不支持服务端渲染、client-only
组件不在服务端渲染中呈现;
通过相关特性做到API数据和页面结构合理拆分,首屏所需数据和结构通过服务端获取并渲染,非首屏数据和结构通过客户端获取并渲染。
<template>
<div>
<!-- 顶部banner -->
<banner :banner="banner" />
<!-- 非首屏所需结构,通过client-only组件达到不在服务端渲染目的-->
<client-only>
<!-- 列表 -->
<prod-list :listData="listData"/>
</client-only>
</div>
</template>
组件缓存
将渲染后的组件DOM结构存入缓存,定时刷新,有效期通过缓存获取组件DOM结构,减小生成DOM结构所需时间;
适用于渲染后结构不变或只有几种变换、并不影响上下文的组件。
nuxt.config.js
配置项修改
const LRU = require('lru-cache')
module.exports = {
render: {
bundleRenderer: {
cache: new LRUCache({
max: 1000, // 缓存队列长度
maxAge: 1000 * 60 // 缓存1分钟
})
}
}
}
需要做缓存的 vue 组件, 需增加name
以及serverCacheKey
字段,以确定缓存的唯一键值。
export default {
name: 'componentName',
props: ['item'],
serverCacheKey: props => props.item.id
}
-
如果组件依赖于很多的全局状态,或者状态取值非常多,缓存会因频繁被设置而导致溢出,这样的组件做缓存就没有多大意义了;
-
组件缓存,只是缓存了dom结构,如
created
等钩子中的代码逻辑并不会被缓存,如果其中逻辑会影响上下边变更,是不会再执行的,此种组件也不适合缓存。
页面整体缓存
当整个页面与用户数据无关,依赖的数据基本不变的情况下,可以对整个页面做缓存,减小页面获取时间;
页面整体缓存前提是在使用Nuxt.js脚手架工具create-nuxt-app
初始化项目时,必须选择集成服务器框架,如express
、koa
,只有这样才具有服务端中间件扩展的功能。
对于使用nuxt框架而言,用脚手架初始化完,框架对于vue服务端渲染的res.end()
函数做了高度封装,
服务端中间件middleware/page-cache.js
import LRUCache from "lru-cache";
const cache = new LRUCache({
maxAge: 1000 * 60 * 2, // 有效期2分钟
max: 100 // 最大缓存数量
});
export default function(req, res, next) {
// 本地开发环境不做页面缓存
if (process.env.NODE_ENV !== "development") {
try {
const cacheKey = req.url;
const cacheData = cache.get(cacheKey);
if (cacheData) {
return res.end(cacheData, "utf8");
}
const originalEnd = res.end;
res.end = function(data) {
cache.set(cacheKey, data);
originalEnd.call(res, ...arguments);
console.log(data);
};
} catch (error) {
console.log(`page-cache-middleware: ${error}`);
next();
}
}
next();
}
nuxt.config.js
配置项修改,引入服务端中间件
serverMiddleware:[
{ path: '/app', handler: '~/middleware/page-cache' }
]
服务器端渲染中间件(serverMiddleware) vs 中间件(middleware)。不要将它与客户端或SSR中Vue在每条路由之前调用的routes middleware
混淆。serverMiddleware
只是在vue-server-renderer
之前在服务器端运行,可用于服务器特定的任务,如处理API请求或服务资产。
问题
服务端的 axios 不会自动携带 Cookie
在 axios 封装那里新增一个接口来注入 Cookie,然后靠 router-middleware 从 context.headers.cookie
获取并注入