如果数据(列表、树状)很多特别卡的时候,可参考: 列表参考:npm包react-window,https://react-window.vercel.app/#/examples/list/fixed-size 树状参考:npm包react-vtree,https://lodin.github.io/react-vtree/index.html?path=/story/tree--fixedsizetree
提取第三方库(利用webpack的splitChunks,下方有代码demo)。各种第三方的库,变化很少,我们完全可以单独打包,并设置强缓存(expires、cache-control)和协商缓存(lastmodify、Etag)。当项目迭代时,不会再将第三方库打包进包里面了,可以减小包的大小,且配合缓存,即可增加打包速度及页面的加载速度。拆分包后势必会增加http请求,我们可以将资源上传至不同域名或cdn,增加浏览器的并行下载能力,或者使用http2,多路复用
- 针对低频率基本不变化的文件,并且所有页面都需要用的包(如react、ant),单独打包,并且配置固定版本号
- 针对中频率变化很少的文件,部分页面才用的包(如lodash,echarts等),单独打包,并且配置固定版本号
- 针对高频率变化的文件,基本是业务代码,单独打包,并且配置动态版本号
- 针对其他npm动态加载的包,但是项目中没用到的包,单独打包,并且不引入页面,减小打包文件大小
去除没用到的npm包
- 通过工具分析(利用npm包depcheck),找出没用到的npm包,进行删除处理
- 使用Tree Shaking
启用gzip压缩
- 通过webpack配置,将能在构建时压缩的文件(如css、js等,图片不能gzip压缩),构建时进行压缩(压缩等级为1-9,我们设置为9),避免服务器动态实时压缩,消耗服务器的cpu资源,而且也能节省服务器压缩所需要的时间
- 压缩好文件后,通过Nginx配置gzip_static,如果已经压缩文件(比如.gz结尾的文件),服务器直接返回压缩文件至浏览器即可;同时配置gzip,对一些构建时无法压缩的文件,进行动态压缩
- 入口html不是直接引人.gz文件,而是会引入和.gz文件一样文件名的文件
其他
- 固定package.js中npm包的版本。避免由于第三方库的升级而导致bug
- 修改webpack配置,使得打包时不生成sourceMap和License文件,优化打包时间及包大小
- 域名预解析
- 很少变更的数据做localstorage缓存,比如城市编码、行业分类等
- 通过配置,能够展示包的构成情况(webpack-bundle-analyzer),将公用的包提出,将无用的包删除;
- 明细化的展示各个包的打包时间(speed-measure-webpack-plugin),然后进行响应的优化(比如cacheDirectory),优化打包速度
- 通过配置,能够将开发环境构建时的文件写入硬盘(默认是写入内存的),方便查看优化
- babel配置增加@babel/transform-runtime、["@babel/preset-env", {modules: false}]、"compact": false
- webpack的optimization增加usedExports: true
Tree-shaking。参考:https://zhuanlan.zhihu.com/p/417686391;参考:https://blog.csdn.net/weixin_45047039/article/details/110384743
webpack5可以更好的打包。比如optimization.usedExports删除未使用的属性等等;
// 开发环境方便源码调试devtool: isDev ? 'cheap-module-source-map' : false,// 提取第三方库。splitChunks配置参考// 如果碰见报错:Cannot read property 'pop' of undefined,只要加上enforce: true就好了,参考echarts
optimization: {
...config.optimization,splitChunks: {// chunks: 'initial',cacheGroups: {// 第三方不变的包,单独打包插入HTML。当项目迭代时,不会再将第三方库打包进包里面了,可以减少包的大小,提高性能'ant-react': {chunks: 'all',test: /ant|react/,name: "ant-react",priority: -1,reuseExistingChunk: true,},echarts: {chunks: 'all',test: /echarts/,name: "echarts",priority: -1,reuseExistingChunk: true,enforce: true},// 以下是使用不到的包(比如其他npm包内部依赖的包),将其单独打包,避免打包进入bundle.js'monaco-editor': {chunks: 'all',test: /monaco-editor/,name: "monaco-editor",priority: -1,reuseExistingChunk: true,},// 以下是可能变化的包nodeModules: {// 将node_modules打包在一起chunks: 'all',test: /[\\/]node_modules[\\/]/,priority: -3,// reuseExistingChunk: true,name: 'nodeModules',},qikecommon: {// 将其他代码打包在一起chunks: 'all',priority: -4,enforce: true,// reuseExistingChunk: true,name: 'qikecommon'}}}}// webpack-bundle-analyzer配置参考。注意判断环境,debug环境才配置这个
plugins: [
...config.plugins,newBundleAnalyzerPlugin({analyzerMode: 'server',analyzerHost: '127.0.0.1',analyzerPort: 8889,reportFilename: 'report.html',defaultSizes: 'parsed',openAnalyzer: true,generateStatsFile: false,statsFilename: 'stats.json',statsOptions: null,logLevel: 'info'}),]// webpack的gzip压缩。注意判断环境,生产环境才配置这个constCompressionPlugin=require("compression-webpack-plugin");
plugins: [
...config.plugins,newCompressionPlugin({test: /\.(js|css|html|svg)$/i,// 使用不到的包,禁用压缩,节省时间exclude: /monaco\-editor|tinymce|exceljs/,algorithm: 'gzip',compressionOptions: {level: 9},// minRatio: 0.8,filename: '[file].gz[query]'}),]// 删除CSS的sourcemaps。开发环境和生成环境都需要constCssMinimizerPlugin=require('css-minimizer-webpack-plugin');webpackConfig.optimization.minimizer.push(newCssMinimizerPlugin({sourceMap: false,minimizerOptions: {sourceMap: false},minify: async(data,inputMap,minimizerOptions)=>{// eslint-disable-next-line global-requireconstCleanCSS=require('clean-css');const[[filename,input]]=Object.entries(data);constminifiedCss=awaitnewCleanCSS({sourceMap: false}).minify({[filename]: {styles: input,sourceMap: inputMap,},});return{css: minifiedCss.styles,map: minifiedCss.sourceMap ? minifiedCss.sourceMap.toJSON() : '',warnings: minifiedCss.warnings,};},}));// 删除license文件。开发环境和生成环境都需要webpackConfig.optimization.minimizer.push(newTerserPlugin({terserOptions: {parse: {ecma: 8,},compress: {ecma: 5,warnings: false,comparisons: false,inline: 2,},mangle: {safari10: true,},output: {ecma: 5,comments: false,ascii_only: true,},},// 不打包LICENSE文件extractComments: false,sourceMap: false}))- 为了减少页面白屏时间,须JS放页面后端,也要注意JS处理事件效率等,减少阻塞。资源延迟加载等提高首屏展示时间
- 减少HTTP请求数。请求数多时,为了提高浏览器的并行能力,可采用多域名及cdn,配置域名预解析
- 图片选择合适的格式(如webp,注意优雅降级)、大小及质量,icon可以通过雪碧图、font icon及base64等方式
- 压缩静态资源。对于敏感资源,可以采用混淆压缩
- 页面布局应语义化,也应避免标签、css层级太深
- 善于使用本地存储(localStorage、sessionStorage)来缓存一些配置数据,比如国家、省份等信息
- 可以通过performance.timing和performance.getEntries()查看页面资源的请求情况
- 写代码时,时刻注意闭包、重绘、重排等,善于启用GPU,善于使用requestAnimationFrame、createDocumentFragment等等
- 页面静态化
- 更多建议请参考pagespeed或者yslow
- 尽量减少转发代理等次数
- 对资源设置Content-Length属性,可以让浏览器提前知道数据的多少,而无需自己去实时检测数据是否传输完毕,提高其效率。
- 对实时性要求不高的数据做缓存
- 尽量瘦身接口返回内容,避免将一些用不到的数据也返回前端,导致传输数据量太大
- 增加Server-Timing响应标头,方便我们了解请求的数据各个阶段所花费的时间,比如db时间等
- 接口缓慢原因,是否因为配置太低,太多计算耗费CPU,导致性能降低,考虑更换更高性能的服务器
- 可以采用http2协议。多路复用、头部压缩等等优势
- 对静态资源启用gzip压缩,压缩等级看需求而定,等级越高压缩比例越大,但是也越耗CPU。注意图片别gzip压缩
- 对于常年不变或变化很少的资源,启用强制缓存(expires、cache-control),不请求网络;其他资源可以启用协商缓存,尽量使用lastModify,304返回头部,不返回body。尽量少用eTag,比较耗CPU,一定要etag时,尽量部分内容etag,避免全量etag
- 对于重要项目或者pv、uv高项目,采用负载均衡
- CDN
- dns缓存
- 域名预解析
- 负载均衡