Nuxt
简介
Nuxt.js 是一个基于 Vue.js 的轻量级应用框架,可用来创建服务端渲染 (SSR) 应用,也可充当静态站点引擎生成静态站点应用,具有优雅的代码结构分层和热加载等特性。
SSR
SSR,即服务器渲染,就是在服务器端将对Vue页面进行渲染生成html文件,将html页面传递给浏览器。
SSR两个优点:
- SEO 不同于SPA的HTML只有一个无实际内容的HTML和一个app.js,SSR生成的HTML是有内容的,这让搜索引擎能够索引到页面内容。
- 更快内容到达时间 传统的SPA应用是将bundle.js从服务器获取,然后在客户端解析并挂载到dom。而SSR直接将HTML字符串传递给浏览器。大大加快了首屏加载时间。
Nuxt.js的官方网站是这样介绍的:
Nuxt.js 是一个基于 Vue.js 的通用应用框架。 通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI渲染。
Nuxt.js是特点(优点):
- 基于 Vue.js
- 自动代码分层
- 服务端渲染
- 强大的路由功能,支持异步数据
- 静态文件服务
- ES6/ES7 语法支持
- 打包和压缩 JS 和 CSS
- HTML头部标签管理
- 本地开发支持热加载
- 集成ESLint
- 支持各种样式预处理器: SASS、LESS、 Stylus等等
性能优化
方案
常规设置
以下设置保证SSR输出的HTML是最小体积。
- 开启HTML 压缩
- 抽离JS、CSS资源
- 关闭gzip压缩
其中,关闭gzip压缩,是降低cpu压力,提高Nuxt性能,通常压缩已经在Nginx开启。
Api接口缓存
将服务端获取的数据,全部缓存到node进程内存中,定时刷新,有效期内请求都通过缓存获取API接口数据,减小数据获取时间;
此种缓存适用于缓存的部分API数据,基本保持不变,变更不频繁,与用户个人数据无关。
示例代码:
import LRU from 'lru-cache'
const CACHED = new LRU({
max: 100, // 缓存队列长度
maxAge: 1000 * 60 // 缓存时间
})
export default {
async asyncData({ app, query }) {
try {
let banner, footer
if (CACHED.has('baseData')) {
// 存在缓存,使用缓存数据
let data = CACHED.get('baseData')
data = JSON.parse(data)
banner = data.banner
footer = data.footer
} else {
// 获取页面顶部轮播图信息
const getBanner = () => {
return app.$axios.$get('zz/zy/banner')
}
// 获取底部配置信息
const getFooter = () => {
return app.$axios.$get('zz/zy/footer', {
params: {
smark: query.smark
}
})
}
[banner, footer] = await Promise.all([getBanner(), getFooter()])
// 将数据写入缓存
CACHED.set('baseData', JSON.stringify({ banner: banner, footer: footer}))
}
return {mods: mods, footer: footer}
} catch (e) {
console.log('interface timeout or format error => ', e)
return {}
}
}
}
组件缓存
将渲染后的组件DOM结构存入缓存,定时刷新,有效期通过缓存获取组件DOM结构,减小生成DOM结构所需时间;
适用于渲染后结构不变或只有几种变换、并不影响上下文的组件。
示例代码:
nuxt.config.js配置项修改
const LRU = require('lru-cache')
module.exports = {
render: {
bundleRenderer: {
cache: LRU({
max: 1000, // 缓存队列长度
maxAge: 1000 * 60 // 缓存1分钟
})
}
}
}
需要做缓存的 vue 组件, 需增加 name 以及 serverCacheKey 字段,以确定缓存的唯一键值。
export default {
name: 'modal',
props: ['type'],
serverCacheKey: props => props.type
}
如果组件依赖于很多的全局状态,或者状态取值非常多,缓存会因频繁被设置而导致溢出,这样的组件做缓存就没有多大意义了;
另外组件缓存,只是缓存了dom结构,如created等钩子中的代码逻辑并不会被缓存,如果其中逻辑会影响上下边变更,是不会再执行的,此种组件也不适合缓存。
页面缓存
当整个页面与用户数据无关,依赖的数据基本不变的情况下,可以对整个页面做缓存,减小页面获取时间;
页面整体缓存前提是在使用Nuxt.js脚手架工具create-nuxt-app初始化项目时,必须选择集成服务器框架,如express、koa,只有这样才具有服务端中间件扩展的功能。
示例代码:
服务端中间件middleware/page-cache.js
const LRU = require('lru-cache')
let cachePage = new LRU({
max: 100, // 缓存队列长度
maxAge: 1000 * 60 // 缓存1分钟
})
export default function(req, res, next){
let url = req._parsedOriginalUrl
let pathname = url.pathname
// 通过路由判断,只有首页才进行缓存
if (['/home'].indexOf(pathname) > -1) {
const existsHtml = cachePage.get('homeData')
if (existsHtml) {
return res.end(existsHtml.html, 'utf-8')
} else {
res.original_end = res.end
// 重写res.end
res.end = function (data) {
if (res.statusCode === 200) {
// 设置缓存
cachePage.set('homeData', { html: data})
}
// 最终返回结果
res.original_end(data, 'utf-8')
}
}
}
next()
}
nuxt.config.js配置项修改,引入服务端中间件
//针对 / 路由做缓存
serverMiddleware: [
{ path: '/', handler: '~/middleware/page-cache.js' },
]
缓存部分参考资料:Nuxt实现的SSR页面性能优化的进一步探索与实践
坑
虽然已经设置缓存,但是在首次生成缓存需要请求接口,如果在接口返回的50ms内,进来1000个请求,则这1000个请求依旧会被发出,因此需要优化请求队列,并且做好错误处理与重试机制。
Comments