分享人:Leo Liu(刘璐)云智慧前端开发工程师,负责云智慧ITSM产品的前端开发工作,拥有丰富的toB行业前端开发经验,致力于改善前端架构以及性能优化
云智慧数字化运维管理产品DOSM是面向企业IT服务管理领域的新一代ITSM服务管理产品。通过高效的流程引擎,支持多样化的工单流程、多种工单处理&分配方式以及动态表单等功能,帮助企业实现数字化转型。
问题描述:
在客户内网环境下,因带宽比较低,静态资源加载会很慢,导致加载页面的时候,白屏的时间比较长,大概有 15 到 30 秒这样一个时间,导致用户体验会很不好。
当用户在打开网页时, 最直观的感受就是页面内容出来的速度,我们要做的优化工作, 也主要是为了解决这个问题。那么如何提高页面加载和渲染速度呢?一般来说有 三个方面:
- 代码逻辑的优化。优秀的代码设计和编写可以有效减少渲染页面使用的内存和速度(比如虚拟DOM)。
- SSR服务器渲染。将首屏所有内容在服务器端渲染成html静态代码后,直接输出给浏览器,可以有效加快用户访问站点时首屏的加载时间。
- 提升静态文件的加载速度,而这方面大致又可分为下面几点:
— 加快静态文件下载速度
— 减少静态文件的文件大小
— 减少静态文件请求数量,从而减少发起请求的次数(对于浏览器页面来说,请求的开销比网速的开销要大)
解决方案:
加快静态文件下载速度:
gzip压缩,下面的代码会对文件大小大于10240,并且压缩率好于0.8的js、css文件进行gzip压缩。
var CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
增加nginx配置
gzip_static on;
gzip_http_version 1.1;
gzip_proxied expired no-cache no-store private auth;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
减少静态文件的文件大小
在dev模式下的打包文件分析如下:
发现代码结构中存在较大问题的两个文件为图中红框所示 ⬆️
(注:当前打包文件体积为DEV环境下的打包体积,生产环境下包的体积会等比例缩小)
进一步展开分析:
一. 打包方面
体积最大的文件(8.79MB)中存在两个较为明显的问题:
- antd中的icon静态资源库较大且有两个库分别引入打包导致较大的资源被重复打包两次
- Echarts图表库资源仅在部分页面中使用但依然被打包在了公共资源文件中
体积第二的文件(4.05MB)中存在以下两个问题
- wangEditor.js文件和iconfont.js文件过大
- pages文件依然存在被拆分的可能性
减少静态文件请求数量
使用splitChunks方式拆分后的结果如下 ⬇️
存在问题:文件虽然体积变小,但数量增多且重复代码数量增加。请求时依然会占用过多的网络资源,所以将splitChunks的处理方式进行优化
module.exports = {
configureWebpack:config =>{
return {
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: '~',
cacheGroups: {
vendors: {
name: `chunk-vendors`,
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: `chunk-common`,
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
}
}
}
};
用splitChunks插件来控制Webpack打包生成的js文件的内容的精髓就在于, 防止模块被重复打包,拆分过大的js文件,合并零散的js文件。最终的目的就是减少请求资源的大小和请求次数。因这两者是互相矛盾的,故要以项目实际的情况去使用SplitChunks插件,需切记中庸之道。
页面用户体验方面
使用谷歌浏览器开发者工具模拟 Fast 3G 网络条件下的页面加载过程
在JS没有解析加载完成之前展示当前页面,会处于长时间的白屏,带来了一定的用户体验问题。
针对页面白屏问题,在组件中添加 骨架屏动画过渡效果,增加用户等待时间的体验
推荐的性能优化分析处理方向
React Profiler
React Profiler API 会分析渲染和渲染成本,以帮助识别应用程序中卡顿的原因。
import React, { Fragment, unstable_Profiler as Profiler } from "react";
Profiler 接受一个 onRender 回调函数,当被分析的渲染树中的组件提交更新时,就会调用它。
const callback = (id, phase, actualTime, baseTime, startTime, commitTime) => {
console.log(`${id}'s ${phase} phase:`);
console.log(`Actual time: ${actualTime}`);
console.log(`Base time: ${baseTime}`);
console.log(`Start time: ${startTime}`);
console.log(`Commit time: ${commitTime}`);
}
const Demo = ({ props }) => (
)
列的宽度表示 component(和它的 children)最近一次渲染所花费的时间。如果这个 component 在本次 commit 中没有被重新渲染,那其所展示的时间表示上一次 render 的耗时。一个列越宽,其所代表的 component 渲染耗时就越长。
列的颜色表示在本次 commit 中该 component(和它的 children)所花费的时间。黄色代表耗时较长、蓝色代表耗时较短,灰色代表该 component 在这次 commit 中没有被(重新)渲染。
Profiler 的 onRender 回调接收描述渲染内容和所花费时间的参数:
- id: 生提交的 Profiler 树的 id。如果有多个 profiler,它能用来分辨树的哪一部分发生了“提交”。
- phase: "mount" (首次挂载) 或 "update" (重新渲染),判断是组件树的第一次装载引起的重渲染,还是由 props、state 或是 hooks 改变引起的重渲染。
- actualDuration: 次更新在渲染 Profiler 和它的子代上花费的时间。
- baseDuration: 在 Profiler 树中最近一次每一个组件 render 的持续时间。这个值估计了最差的渲染时间。
- startTime: 本次更新中 React 开始渲染的时间戳。
- commitTime: 本次更新中 React commit 阶段结束的时间戳。在一次 commit 中这个值在所有的 profiler 之间是共享的,可以将它们按需分组。
- interactions: 当更新被制定时,“interactions” 的集合会被追踪。
service worker
将一些静态资源及部分依赖项文件(react,antd等)使用service worker处理,增加前端本地资源的缓存能力,减少网络资源请求。
关于service worker:
- 它是一个 JavaScript 线程, 所以它不能直接接触DOM. service worker 可以与一些页面通信(这些页面通过postMessage接口发出信息),而这些页面可以操作DOM(如果需要的话)
- Service worker 是一个可编程的网络代理,它允许你控制网络如何从你的页面发出请求。
- 不使用时会停止运行,在你下一次需要时又会重启。所以,在一个service worker的 onfetch 和 onmessage处理函数里,你不能指望会有一个全局的service worker的状态标志。如果你的确需要存储并复用service worker的状态,它提供了一个API可以用来连接DB: IndexedDB API
可视化大屏开源地址:
飞鱼平台(FlyFish)是云智慧公司自主设计、研发的一款低门槛、高拓展性的低代码应用开发平台,为数据可视化开发场景提供了高效的一站式解决方案。
飞鱼提供丰富的组件和应用模板库,可通过拖拉拽的形式完成数据可视化开发,零开发背景的用户也可完成数据可视化开发工作。同时,飞鱼也提供了灵活的拓展能力,支持组件开发、自定义函数与全局事件等配置,面向复杂需求场景能够保证高效开发与交付。
Github地址: https://github.com/CloudWise-OpenSource
Gitee地址: https://gitee.com/CloudWise