Nuxt 项目性能优化调研分析

性能优化,这是面试中经常会聊到的话题。我觉得性能优化应该因具体场景而异,因不同项目而异,不同的手段不同的方案并不一定适合所有项目,当然这其中不乏一些普适的方案,比如耳熟能详的文件压缩,文件缓存,CDN,DNS 预解析,等等,但是我更希望听到的是因为不同的项目不同的需求,解决不同的问题而采取的不同的优化手段,比如 BigPipe,分段输出页面的各个部分,对于 SNS 网站是非常合适的,减少了用户的等待时间;相对应的还有一个 BigRender,这是一个大的延迟加载,360 导航首页目前还在使用,京东淘宝首页也是这个思路,对于一些类门户网站非常适用,但是如果你的网页内容不是非常多,就没有必要了

今天要说的是 Nuxt。Nuxt 是支持 Vue SSR 的一个框架,底层需要运行 Node 服务。大概描述一下 Vue 的渲染过程,首先每个组件都会被编译生成一个渲染函数(这部分基本 webpack 打包已经做掉),然后渲染函数生成虚拟 dom,最后虚拟 dom 通过 patch 方法将真实 dom 渲染到页面上。Nuxt 其实就是将这部分放到了服务端去做,在服务端拿到渲染页面所需要的 html,从而使得 html 能够直出,而客户端其实还是会运行整个 Vue 的生命周期,这就带来了一个问题,这部分操作放在了服务端其实是非常耗 cpu 的,创建组件实例和虚拟 DOM 节点的开销,无法与纯基于字符串拼接的模版的性能相当,如果是不加优化的 Nuxt 项目,高并发下是很脆弱的,毕竟 Node 运行在单线程下,不适合 cpu 操作密集型的场景

使用 Nuxt 的项目无非看中了它的两大优点,一是服务端渲染满足 SEO 的需求,二是首屏直出比 SPA 快,再加上如果如果公司是 Vue 系,使用 Nuxt 就更顺理成章。但是不要忘了性能,高并发下 Nuxt 性能确实不乐观,我测试了官网的 hackernews demo 项目,2 核 cpu + 4g 内存,400 并发下它的吞吐量不超过 50,就算是最简的 Nuxt 项目,吞吐量也就 300+,这就说明如果项目不做缓存,300+ 已经是最大的吞吐量了,而最小 express demo 可以轻松到 3000,这就决定了高流量项目并不会轻易去使用 Nuxt

我们的项目目前其实是一个不加优化的 Nuxt 项目,因为用户不多,平时并没有什么问题,但是一到展会,就会有不少用户同时访问,反馈页面会很卡。同条件下做了压测后,吞吐量也是 50 上下,平均响应时长七八秒,所以卡是正常现象

看了一下项目代码,发现了几个问题:

项目没做缓存,所以每次访问都会经历所有 Nuxt 生命周期,消耗 cpu,这点是最致命的

项目打包默认 gzip。Nuxt 项目打包会默认在服务端开启 gzip,因为我们网关层已经做了 gzip,所以这里是不必要的,测试了下关掉 gzip 吞吐量和响应时间都能提高 20% 左右。具体做法是在 nuxt.config.js 中配置(还是得看 英文文档,会告诉你如何不设置 To disable compression, use compressor: false,中文文档当时三月份我写这文的时候还没加这个选项,而目前中文文档也没有翻译这一句 2020-07-16)

render: {
 compressor: false
}

API 请求比较乱。很多请求并没有很好地区分客户端和服务端,而是都由服务端去做了,造成服务端压力过大,其实多数和用户有关的请求理应放到客户端。有的接口为了方便,一次性返回了所有内容,也没有做客户端/服务端区分。另外,服务端的接口请求可以并发,用类似 Promise.all 的形式去控制

SEO。有的内容页面,很长,有五个部分,除了内容外,还有猜你喜欢等其他部分,询问了 SEO 同事,说这几部分都是需要 SEO 的,我不是很懂 SEO,但是在我看来,ssr 只应该渲染首屏内容,而 UI 在设计的时候应该把主要内容设计到首屏,从而满足 SEO

对此我觉得可以从两个方向去优化:

缓存。缓存是最重要的方案,针对 Nuxt 项目可以做三级缓存,页面缓存、组件缓存以及 API 缓存。页面缓存是最重量级的缓存方案,能不能做页面缓存可以从以下两个点判断:

同一个 URL,对于 登录 / 非登录 用户,服务端渲染的内容是相同的(注意是服务端渲染内容,而非前端)

同一个 URL,对于不同的登录用户,服务端渲染的内容是相同的,即没有一些个性化的渲染(常见的个性化渲染,比如针对不同用户渲染不同的猜你喜欢内容等)

其实也就是返回的 html 代码相同就好,主要关注下返回的全局 store 是否一致,另外也不能做一些服务端才能做的操作,比如 set-cookie 等

控制好首屏模块个数,对返回的结果进行精简,最小化,保证吐出到浏览器的内容足够小。这就是前面说的并不要对所有模块都做 ssr,需要首屏呈现的/需要爬虫爬的,我们直出,其他部分做 CSR 就行了

而我们的网站大部分页面是满足做页面缓存条件的,测试了下如果做页面缓存,吞吐量能到 500+,这个数据这个时候其实是和页面大小有关系了,页面缓存的性能是能满足需求的。而有另一类页面,相同的 URL 会返回不同的内容,而且整页都是不同内容,它的实现是获取 cookie 中的不同 city-id,渲染不同城市的内容,很显然这部分页面做不了页面缓存了,API 缓存和组件缓存理论上都是可以试试的

做缓存优化,至少需要访问一次,第二次才能生效,那么还有另一种情况,对于这样的路由 /store/:id,并发打开 id 0~1000,很显然每个页面都是不一样的店铺数据,并不能命中缓存(可能命中组件缓存,暂时忽略),这个时候只能从 Nuxt 生命周期上去优化了,那么以上方向的第二点,控制首屏模块个数就能用到了。所以本文一开始我就说,不同的方案是适配不同的场景的,解决不同的问题会采取不同的手段

补充知识:Nuxt实现的SSR页面性能优化的进一步探索与实践

前言

本文之前,先简单介绍以下几个概念:

SSR指服务端渲染,即页面是通过服务端渲染生成后返回给客户端的,SSR主要为了提高页面加载速度,改善用户体验,也可用于SEO搜索引擎优化。

Nuxt.js 官方定义: Nuxt.js 是一个基于 Vue 的通用应用框架。 通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 UI渲染。我们的目标是创建一个灵活的应用框架,你可以基于它初始化新项目的基础结构代码,或者在已有 Node.js 项目中使用 Nuxt.js。

个人理解:Nuxt.js 就是预设了开发服务端渲染应用所需要的各种配置, 使用 Webpack 和 Node.js 进行封装的基于Vue的SSR框架。

背景

我们部门从事的都是面对用户的业务需求开发,面对用户,意味着对页面的体验要求会更高,最直观体验是页面首屏的加载速度,加载速度优化是我们体验优化的长期、重要的一部分;本文的起源正是首屏加载速度优化。

页面加载速度优化的核心包括三点:减少资源文件的请求数量;减小每个资源文件的大小;提高每个资源的加载速度;

诸如合并API访问,压缩混淆文件,支持webp图片,资源cdn缓存等等常用办法,都是以上面三个核心为出发点的; 这些常用办法基本都可以通过webpack配置,公司基础服务,代码较小的变更完成。

我们负责的各主流量入口页面,已基本做过以上常用的优化,但由于主入口页面资源量较大的原因,优化后并不能达到预期的效果,我们需要探索其它优化方案。 我们快速想到了用SSR的方案进一步解决加载速度问题,从零开始的搭建服务端渲染应用相当复杂,肯定会涉及到服务端的开发,作为独立的前端团队,成本较高昂; 我们决定尝试是否能找到一种成本较低的现有SSR框架,以达到目的;

因主入口页面技术栈为vue,方案调研中自然而然的看到了Nuxt.js此种基于Vue的SSR框架; Nuxt.js和项目技术栈匹配度急高,学习成本极低,自然成为我们的第一选择;

我们引入Nuxt.js,最初只是利用了服务端异步获取API接口数据和服务端渲染两项功能,去重构了我们的项目,重构后效果基本达到我们的预期,正常网络状态下,基本可以达到秒开; 入口页面,团队是作为一个长期的项目进行不定期优化的,我们逐步围绕Nuxt.js框架,对项目做了进一步优化升级,本文主要介绍我们Nuxt.js页面优化的进一步探索与实践; 至于如何搭建初步的Nuxt项目,需要感兴趣的各位自行查看官方文档及自我实践了,本文不做赘述。

探索与实践

我们主要的探索与实践可行方向主要有两个:

一、Nuxt.js特性合理应用

应用到的特性主要包括asyncData异步获取数据、mounted不支持服务端渲染、no-ssr组件不在服务端渲染中呈现;

通过相关特性做到API数据和页面结构合理拆分,首屏所需数据和结构通过服务端获取并渲染,非首屏数据和结构通过客户端获取并渲染。

示例代码:

no-ssr结构拆分

<template>
 <div>
 <!-- 顶部banner -->
 <banner :banner="banner" />
 <!-- 非首屏所需结构,通过no-ssr组件达到不在服务端渲染目的-->
 <no-ssr>
  <!-- 商品列表 -->
  <prod-list :listData="listData"/>
 </no-ssr>
 </div>
</template>

API数据拆分

export default {
 async asyncData({ app, query }) {
 try {
  // 获取页面顶部轮播图信息
  const getBanner = () => {
  return app.$axios.$get('zz/zy/banner')
  }
  // 获取底部配置信息
  const getFooter = () => {
  return app.$axios.$get('zz/zy/footer', {
   params: {
   smark: query.smark
   }
  })
  }
  // 并发获取首屏数据,服务端获取
  const [banner, footer] = await Promise.all([getBanner(), getFooter()])
  return {banner: banner, footer: footer}
 } catch (e) {
  console.log('interface timeout or format error => ', e)
  return {}
 }
 },
 mounted() {
 // 非首屏使用的数据, 客户端获取
 this.loadListData()
 },
 methods: {
 loadListData() {
  this.$axios.$get('zz/zy/list').then(() => {
  // 数据处理逻辑
  })
 }
 }
}

二、服务端引入缓存

服务端开发意味着缓存可作为性能优化的最直接法门,Nuxt.js作为一种服务端渲染框架,也不例外;针对不同的页面,不同的数据状态,可主要区分为下面三类缓存:

1、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 {}
  }
 }
 }

2、组件级别缓存

将渲染后的组件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: 'zzZyHome',
 props: ['type'],
 serverCacheKey: props => props.type
}

如果组件依赖于很多的全局状态,或者状态取值非常多,缓存会因频繁被设置而导致溢出,这样的组件做缓存就没有多大意义了;

另外组件缓存,只是缓存了dom结构,如created等钩子中的代码逻辑并不会被缓存,如果其中逻辑会影响上下边变更,是不会再执行的,此种组件也不适合缓存。

3、页面整体缓存

当整个页面与用户数据无关,依赖的数据基本不变的情况下,可以对整个页面做缓存,减小页面获取时间;

页面整体缓存前提是在使用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配置项修改,引入服务端中间件

//针对home路由做缓存
serverMiddleware: [
 { path: '/home', handler: '~/middleware/page-cache.js' },
]

总结

本文主要是针对Nuxt.js框架实现的页面,性能优化方案进一步探索和实践的总结,汇总一些思路与方向;期望各位小伙伴在其它SSR相关页面优化过程中,能起到一定的启发作用。

以上这篇Nuxt 项目性能优化调研分析就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 解决element-ui的下拉框有值却无法选中的情况

    问题描述: 在使用Vue框架和element-ui开发时,下拉框遇见一个问题,在函数中改变了页面中的某个值,在函数中查看是修改成功了,但在页面中没有及时刷新改变后的值,也就是下拉框值无法选中.(踩坑踩得莫名其妙) 代码段: <el-select v-model="value" placeholder="请选择" @change="change()"> <el-option v-for="item in options&

  • 解决Element中el-date-picker组件不回填的情况

    1.问题描述 当我们在实用ElementUI组件完成项目的时候可能会遇到这样的需求,比如: 新建一个活动,需要定义活动的时间范围: 因此我们在新建活动的操作过程中需要选择一段时间区间以及活动名称等信息提交,新建完成: 网页上出现了新建好的活动,其他人想查看详细信息,打开页面,发现时间区间并没有实现回填! 2.问题分析 时间信息没有回填,首先要检查,后台数据返回情况以及页面上字段信息是否有差异等细节: 如果没有以上的情况,那就是我碰到的这种情况了, 后端数据返回没有差异,而且页面字段也没有错,其他

  • 基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)

    nuxt.js 是一个基于 Vue.js 的服务端渲染应用框架,使用nuxt.js在做同构项目开发时,需要考虑的一些点总结如下: 一.node服务端性能优化(提高node应用程序处理高流量的能力) 基于nuxt.js的服务端渲染项目我们能做的服务端性能优化有以下几点(需要注意的是持久化缓存不应该在本地开发环境去做,这样在缓存期间不会暴露本地开发中代码的问题) 优化点 参考文档及思路 优化场景/条件 特别说明 检测方法 1. 页面缓存 vue官方文档 页面内容不是用户特定(即对于相同的 URL,总

  • 解决VUE 在IE下出现ReferenceError: Promise未定义的问题

    问题原因: IE一些低版本的浏览器对于ES6语法不支持 Promise是es6语法里为了解决异步函数多重嵌套的问题(回调地狱) 说明: 或许你并不没有使用Promise,但是有可能你使用Axios等,其中原理也是Promise,进行了封装而已 解决办法: 步骤一: 安装 babel polyfill npm install --save babel-polyfill 如果你使用的是yarn安装的话: yarn add babel-polyfill 备注: 使用yarn 安装的话不需要加–save

  • Nuxt 项目性能优化调研分析

    性能优化,这是面试中经常会聊到的话题.我觉得性能优化应该因具体场景而异,因不同项目而异,不同的手段不同的方案并不一定适合所有项目,当然这其中不乏一些普适的方案,比如耳熟能详的文件压缩,文件缓存,CDN,DNS 预解析,等等,但是我更希望听到的是因为不同的项目不同的需求,解决不同的问题而采取的不同的优化手段,比如 BigPipe,分段输出页面的各个部分,对于 SNS 网站是非常合适的,减少了用户的等待时间:相对应的还有一个 BigRender,这是一个大的延迟加载,360 导航首页目前还在使用,京

  • Vue2.x 项目性能优化之代码优化的实现

    众所周知,Vue项目采用了数据双向绑定和虚拟DOM基础,在数据驱动代替DOM频繁渲染已经算是非常高效了,对开发者而言已经非常优化了,那为什么还会有Vue性能优化这一说呢? 因为目前Vue 2.x使用了webpack等第三方打包构建工具,并且支持其他第三方的插件,我们在项目中使用这些工具时可能不同的操作在运行或打包效率上会有不同的效果,下面就来详细说明优化的方向. 1 v-if 和 v-show 的使用 v-if 为false的时候不会渲染DOM到视图,为true的时候才会渲染到视图: v-sho

  • jQuery性能优化技巧分析

    本文较为详细分析了jQuery性能优化技巧.分享给大家供大家参考.具体分析如下: 一.使用最新版本的jQuery类库 jQuery新版本会较上个版本进行Bug修复和一些优化,不过需要注意的是,在更换版本之后,不要忘记测试你的代码,毕竟有时候不是完全向后兼容的. 二.使用合适的选择器 jQuery选择器性能最佳到最差方式如下: id选择器,如$('#id', context) 标签选择器,如$('p', context) class选择器,如$('.class', context) 属性选择器,如

  • Vue 项目性能优化方案分享

    目录 前言 一.代码层面的优化 1.1.v-if 和 v-show 区分使用场景 1.2.computed 和 watch  区分使用场景 1.3.v-for遍历必须为item添加key,且避免同时使用v-if 1.4.长列表性能优化 1.5.事件的销毁 1.6.图片资源懒加载 1.7.路由懒加载 1.8.第三方插件的按需引入 1.9.优化无限列表性能 1.10.服务端渲染 SSR or 预渲染 二.Webpack 层面的优化 2.1.Webpack 对图片进行压缩 2.2.减少 ES6 转为

  • vue项目中一定会用到的性能优化技巧

    目录 引言 性能优化标准 Lighthouse 通用常规优化手段 通用性能优化分析 FCP(First Contentful Paint) LCP(Largest Contentful Paint) SpeedIndex(速度指数) 排查性能瓶颈 分析包内容 利用chorme devtool 的代码覆盖率 针对vue 的特殊优化 图片懒加载 虚拟滚动 vue 中的函数式组件 利用v-show .KeepAlive 复用dom 分批渲染组件 最后 引言 提起性能优化 很多人眼前浮现的面试经验是不是

  • javascript性能优化之DOM交互操作实例分析

    本文实例讲述了javascript性能优化之DOM交互操作技巧.分享给大家供大家参考,具体如下: 在javascript各个方面,DOM毫无疑问是最慢的一部分.DOM操作与交互要耗费大量时间,因为它们往往需要重新渲染整个页面或者某一部分.理解如何优化与DOM的交互可以极大提高脚本完成的速度. 1.最小化DOM更新 看下面例子: var list = document.getElementById("ul"); for (var i=0; i < 10; i++){ var ite

  • Android APP性能优化分析

    本文通过Android APP性能优化的四个方面做了详细分析,并对原理和重点做了详细解释,以下是全部内容: 说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才感觉运行速度稍微提高了点,就算手机在各种性能跑分软件面前分数遥遥领先,还是感觉无论有多大的内存空间都远远不够用.相信每个使用 Android 系统的用户都有过以上类似经历,确实,Android

  • 利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化)

    背景 我们经常使用 Chrome Dev Tools 来开发调试,但是很少知道怎么利用它来分析页面性能,这篇文章,我将详细说明怎样利用 Chrome Dev Tools 进行页面性能分析及性能报告数据如何解读. 分析面板介绍 上图是 Chrome Dev Tools 的一个截图,其中,我认为能用于进行页面性能快速分析的主要是图中圈出来的几个模块功能,这里简单介绍一下: Network : 页面中各种资源请求的情况,这里能看到资源的名称.状态.使用的协议(http1/http2/quic...).

  • Vue.js 应用性能优化分析+解决方案

    目录 一.介绍 二.为什么我们需要 Vue JS 性能优化? 三.Vue 性能不佳背后的主要原因 1. 生成报告 2. 运行命令和npm 运行生成 四.如何优化 Vue js 应用程序的性能? 1. 在 Vue js 中懒加载 2. 基于路线的代码拆分 3.Vue js预加载组件 4. 优化第三方库 5. 使用浏览器缓存 6. 优化和压缩图像 前言: 假设我们在开发整个 Vue 应用程序方面非常努力.js.但没有优先考虑其性能:现在,我们的应用程序需要一段时间来加载.导航.提交或执行任何用户操作

随机推荐