神族九帝's blog 神族九帝's blog
首页
网盘 (opens new window)
线报 (opens new window)
商城 (opens new window)
  • 复习指导
  • HTML
  • CSS
  • JavaScript
  • 设计模式
  • 浏览器
  • 手写系列
  • Vue
  • Webpack
  • Http
  • 前端优化
  • 项目
  • 面试真题
  • 算法
  • 精选文章
  • 八股文
  • 前端工程化
  • 基础篇
  • 进阶篇
  • 高级篇
  • 计算机基础
  • 高频考点
  • 精简题
  • 综合问题
  • 复习题
  • vue
  • vue2源码学习
  • 剖析vuejs内部运行机制
  • TypeScript 入门实战笔记
  • vue3源码学习
  • 2周刷完100道前端优质面试真题
  • npm发包
  • 重学node
  • 前端性能优化方法与实战
  • webpack原理与实战
  • webGl
  • 前端优化
  • Web3
  • 更多
  • 网站
  • 资源
  • Vue资源
  • 收藏的一些API
  • 未来要做的事
  • 宝塔面板+青龙面板
  • 安卓手机当服务器使用
  • 京东自动评价代码
  • 搭建x-ui免流服务器(已失效)
  • 海外联盟
  • 好玩的docker
  • 导航
GitHub (opens new window)

神族九帝,永不言弃

首页
网盘 (opens new window)
线报 (opens new window)
商城 (opens new window)
  • 复习指导
  • HTML
  • CSS
  • JavaScript
  • 设计模式
  • 浏览器
  • 手写系列
  • Vue
  • Webpack
  • Http
  • 前端优化
  • 项目
  • 面试真题
  • 算法
  • 精选文章
  • 八股文
  • 前端工程化
  • 基础篇
  • 进阶篇
  • 高级篇
  • 计算机基础
  • 高频考点
  • 精简题
  • 综合问题
  • 复习题
  • vue
  • vue2源码学习
  • 剖析vuejs内部运行机制
  • TypeScript 入门实战笔记
  • vue3源码学习
  • 2周刷完100道前端优质面试真题
  • npm发包
  • 重学node
  • 前端性能优化方法与实战
  • webpack原理与实战
  • webGl
  • 前端优化
  • Web3
  • 更多
  • 网站
  • 资源
  • Vue资源
  • 收藏的一些API
  • 未来要做的事
  • 宝塔面板+青龙面板
  • 安卓手机当服务器使用
  • 京东自动评价代码
  • 搭建x-ui免流服务器(已失效)
  • 海外联盟
  • 好玩的docker
  • 导航
GitHub (opens new window)
  • vue2源码学习

  • 剖析vuejs内部运行机制

  • TypeScript 入门实战笔记

  • vue3源码学习

  • 2周刷完100道前端优质面试真题

  • npm发包

  • 重学node

  • 前端性能优化方法与实战

    • 00 开篇词 开启刻意练习之路,进阶前端性能技术专家
    • 01 体系总览:性能优化体系及关键指标设定
    • 02 性能瓶颈点:从 URL 输入到页面加载整过程分析
    • 03 案例分析:移动端 M 站性能优化落地注意事项
    • 04 指标采集:首屏时间指标采集具体办法
    • 05 指标采集:白屏、卡顿、网络环境指标采集方法
    • 06 工具实践:性能 SDK 及上报策略设计
    • 07 平台实践:如何从 0 到 1 搭建前端性能平台
    • 08 诊断清单:如何实现监控预警并进行问题诊断
    • 09 优化手段:首屏秒开的 4 重保障
    • 10 优化手段:白屏 300m 和界面流畅优化技巧
    • 11 工具实践:如何进行性能专项测试
    • 12 理论分析:Hybrid 下的性能优化整体分析
    • 13 高级进阶:保证首次加载为秒开的离线包设计
    • 14 高级进阶:瞒天过海的骨架屏及 SSR 优化手段
      • 15 高级进阶:WebView 层及代码架构层面优化
      • 16 黑科技:详解预请求、预加载及预渲染机制
      • 17 横向对比:百度、阿里云、美团性能方案对比
      • 18 性能演进:RN、Flutter、小程序和 Enhance Hybrid
      • 19 成长之路:前端技术专业能力与业务产出平衡
      • 20 结束语 开启性能优化实践之路
    • webpack原理与实战

    • webGl

    • 前端优化

    • Web3

    • 更多

    • 学习笔记
    • 前端性能优化方法与实战
    wu529778790
    2021-09-20

    14 高级进阶:瞒天过海的骨架屏及 SSR 优化手段

    前面我已经提到过,骨架屏和 SSR 在首屏优化方面有较好的效果,骨架屏可以使用户预期到接下来要展示的内容和结构,让用户觉得页面加载快了,而 SSR 则可以白屏时间大幅缩短。

    那么具体该怎么实现骨架屏和 SSR 呢?在实际工作当中如何落地操作呢?这一讲我们就来详细介绍下。

    使用骨架屏方案优化页面性能

    为了让骨架屏内容和页面结构更类似,提升用户体验,我们一般采用的是图片骨架屏。

    图片骨架屏的实现

    想要了解如何实现,我们先来看一下图片骨架屏的加载流程。在 App 业务功能设计时,设计师会针对这个页面制作一张离线包的图片,在 WebView 启动时,客户端把这张图片覆盖在页面上,页面开始进入请求资源的流程。当页面 WebView 加载完成或者前端页面通知客户端加载完成时,客户端通过渐变动画隐藏这张图片,将准备好的页面展现给用户。

    所以,如果想要实现骨架屏,大致有这么几步。

    第一步:先让 UI 设计师设计一张当前页面对应离线包的图,作为骨架屏展示图片。

    第二步,在业务需求开发过程中,前端工程师拿到图后,把这张图片上传到 CDN 上面。

    第三步,在客户端代码,增加启动时读取图片骨架屏的配置文件。如下面代码所示。

    首先是传入设备分辨率,比如 400 *500,然后设置状态码 code。code 是 0 代表成功,-1 代表关闭图片骨架屏功能。 data 对象是具体的数据,m.58.com/enjoy-given/eg/index.html 是对应的页面 URL,骨架屏那个图片的地址是https://m.58.com/pic.png?400*500。

    当用户打开 WebView 时,客户端对 URL 进行解析,取得该 URL 对应的 host 和pathname,然后将这两个数据分别与 data 和 routes 中的数据做比较。

    如果都匹配上,说明要展示骨架屏,此时可根据 routes下面的 id 和 imgName 获取到对应的图片文件。当三个字符串拼起来,即zzSkeleton + id +imgName,就可获得最终的图片名字。其中 zzSkeleton 就是一个字符串, ID 我设置成 10001,imgName 表示具体的图片名称,比如代码中的 pic.png,最终图片名称为 “zzSkeleton10001pic.pn”。

    在实现过程中要注意以下三点。

    第一,注意区分首次使用和二次使用。首次使用的话,客户端遍历上述配置文件,下载数据中对应图片即可;二次使用时,需要拿当前的配置文件对比之前的配置文件,如果图片名称不同,需要下载新的图片。

    第二,需要客户端在内存中建立图片,以加快图片骨架屏的加载速度。

    第三,图片骨架屏展示过程中会出现拉伸问题。这是因为分辨率不同造成的,我们可以让 App

    在获取配置文件时,加上当前页面的分辨率,这样接口会根据分辨率返回最合适的图片。

    以上是具体的实现方案,在实际当中,我们该如何借助它来进行性能优化呢?

    使用及注意事项

    首先骨架屏方案,非常适合资源加载时间长的页面,比如列表页的首屏内容有很多个数据接口、筛选项、精选数据、列表数据等,这时候一定要用骨架屏。而对于一些 SSR 页面,因为白屏阶段比较短,可用可不用。

    骨架屏使用起来比较简单,前端工程师只要在开发项目时,让 UI 对应出一个骨架图的图片,然后上传到 CDN,并给出页面路径和文件路径,生成配置文件给客户端,客户端根据配置加载骨架屏即可。

    不过在实操时需要注意两点:

    • 骨架屏中的内容结构,应该只是首屏的内容结构,不是整个页面的内容结构,反之会造成骨架图体积过大,加载骨架屏图片时间过长的问题;

    • 骨架屏的问题,比如骨架屏代码异常加载时没有展示,或者展示时间过长,因为它不会影响白屏时间,我们很难发现。所以骨架屏什么时候展示和什么时候销毁,客户端要以日志形式记录下来,上传到性能监控平台,这样定期去看一下日志,则可以发现问题。

    使用 SSR 方案优化页面性能

    一般来说,一个 Web 页面的渲染主要由客户端或者浏览器端来完成,大致过程是:客户端先从服务端请求到 index.html,然后加载脚本文件,Web 应用通过 ajax 请求到页面数据,并把相应的数据填充到模板,最终形成完整的页面来呈现给用户。

    SSR(Server Side Rende,服务端渲染) 则把数据请求(也就是前面提到 ajax 请求)放在了服务端,服务端收到返回结果时,把数据填充到模板形成完整的页面,由服务器端把渲染完成的完整页面返回给客户端。这样减少了一些客户端到服务器端的数据接口请求,加快了首屏展现速度。

    SSR 的实现及使用

    如何实现 SSR 呢?我们选用了 nuxt.js 方案。

    为什么选择它?因为手机业务迭代开发速度比较快,我们希望将 SSR 开发的一些配置都能够封装起来,让前端工程师介入成本降低。由于目前业界移动端使用 Vue 的比较多,所以寻找能和它配套的方案可以大大降低接入成本。而 Nuxt.js 正是这种方案,它是一款基于 Vue 的 SSR 开源框架,使用 Webpack 和 Node.js 进行封装,其中预设了开发服务端渲染应用所需要的各种配置。

    具体到开发方面,官网文档介绍得比较详细,通过脚手架 npx 即可创建一个项目,然后进入 service-life目录,运行npm run dev,即可看到页面效果。

    npx create-nuxt-app service-life
    cd service-life
    npm run dev
    

    接下来我们来看如何在项目中接入 SSR 方案。因为移动端我们经常使用 Vue CSR ,和 Vue CSR 一致的地方(如 client 的 webpack 配置,项目基础结构等),在这里我就不过多介绍了,重点放在不同点上。

    首先,一些基础要求要满足。一是如果没引入 Vuerouter、Vuex 和 axios,则需要引入,确保客户端拿到的数据和客户端一致。二是由于服务端也需要接收 URL,然后传递给 vue 程序进行处理,所以要做好跨平台的路由配置,具体可使用 Vuerouter。三是数据端也需要跨平台库,对应的技术是 axios + vuex。

    其次,我们需要为 webpack 提供两个打包入口文件, client 端和 server 端各一份。

    由于用户在客户端每次访问 vue 时,都是一个新的上下文,但在用户访问服务端时,用于服务端渲染的 Node.js 启动后就一直在运行。也就是说,每一个用户请求处理都在同一个应用上下文中进行。为了确保不串数据,需要为每次 SSR 请求,创建全新的 app、store 和 router。

    然后,我们重构组件的数据获取方式,在页面开发渲染之前,利用服务端进程,做好数据获取和解析工作,然后把状态和数据存存储于 store 中。

    这里面需要注意的是,SSR 应用在挂载(mount)到客户端应用程序之前,需要获取到与服务器端应用程序完全相同的数据。否则,客户端应用程序会因为使用与服务器端应用程序不同的状态,导致混合失败。

    白屏时间 100ms 的 SSR 优化

    做好上述工作后,我们白屏时间基本可以达到 200ms(标准是 300ms)。之后,我们还可以做一些升级优化,确保白屏时间达到 100ms 以内。具体来说,可以实施以下两个方面的工作:

    • 利用服务端的性能优势,尽量在服务端完成资源加载、首屏切分等工作

    • 利用服务端统一缓存机制,对数据接口、页面和组件做缓存

    怎么理解呢?

    第一,服务端渲染的最大优势,就是后端服务性能要远高于手机,所以请求数据接口和渲染时,耗时会更短。以我们手机业务列表页为例,CSR 下面渲染需要 600ms,到了 SSR下,渲染只需要 300ms。为此,我们可以把很多原本客户端做到的事情挪到了服务端,比如模块文件加载,首屏切分等。

    第二,服务器端缓存与客户端最大不同是,服务端属于统一公用,也就是说,只要某一个用户访问过一次,后续所有用的访问都可以使用这份缓存。我们可以利用这一特点,采用 LRU(Least Recently Used,最近最少使用缓存机制)和 Redis 做好缓存功能,降低白屏时间。

    具体来说,LRU 属于页面级缓存,对于数据统一性页面(有别于千人千面数据的页面),利用 LRU-Cache 可以缓存当前请求的数据资源。为了降低缓存的颗粒度,提高缓存的服用行,我们还可以用它来对渲染后的 vue 组件进行缓存。

    而使用 Redis 可以对跨页面的数据接口进行缓存,将整体渲染时间再减少 100ms。为什么呢?因为 SSR 应用程序部署在多服务、多进程下,该进程下的缓存并不是共享的,这就造成缓存命中效率低下,而使用 Redis 可以解决这个问题,进而更好实现跨页面数据缓存(关联上跨云接口缓存这里,呼应主题)。

    在实施过程中,还要注意以下几点:

    第一,前端工程师因为对后端服务了解不多,在实际开发中可能会出现一些问题,例如我们之前遇到的,在服务端取后端数据接口时,取到的订单信息直接展示在页面源码中,导致乌云系统爆出了一个安全漏洞。所以建议你系统学习一些服务端安全知识,避免类似问题出现。

    第二,在 SSR 服务出现高并发问题后,服务扩容是一种解决方案,但前端工程师对这方面并不擅长(如估算服务 QPS),容易出现问题。怎么办呢?一方面可以让运维和后端工程师一起协助,另外一方面也要做好 CSR 的降级,一旦遇到问题,可以快速降级。

    小结

    溪风的思维导图14.png

    好了,以上就是骨架屏和 SSR 性能优化方案。在实际当中,你可能会问了:SSR 说了这么多优点,为什么没全量普及,替代掉 CSR?

    这是因为, SSR 需要你对后端知识,尤其是 Node.js 知识有很好地把握,且具备一定的数据接口设计规划和设计能力,但许多前端工程师很容易忽视这方面的学习。还有,SSR 渲染进行的页面,一些事件还在绑定中,有可能会出现操作没反应的情形;一些环境变量(如 window、document )获取不到,稍不注意也会遇到内存泄漏的问题。

    当然,这些对于白屏 100ms 的效果实现,都是非常值得的,而且前者可以通过提前安排Node.js 服务端开发相关培训来解决,后者可以通过整理一份 SSR 开发规范,将一步步蹚过的坑和宝贵经验沉淀下来。

    下面给你留一个思考题:

    SSR 方案和前面提到离线化方案有什么区别?

    可以写到下面的留言区哦。接下来我们将进入下一讲 —— WebView 层及代码架构层面优化。


    # 精选评论

    编辑 (opens new window)
    上次更新: 2022/12/12, 22:56:40
    13 高级进阶:保证首次加载为秒开的离线包设计
    15 高级进阶:WebView 层及代码架构层面优化

    ← 13 高级进阶:保证首次加载为秒开的离线包设计 15 高级进阶:WebView 层及代码架构层面优化→

    最近更新
    01
    好玩的docker
    07-04
    02
    我的第一个NFT
    07-03
    03
    海外迅雷
    06-01
    更多文章>
    Power by vuepress | Copyright © 2015-2023 神族九帝
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×