作者:前端迷
转发链接:https://mp.weixin.qq.com/s/01yNoEmHdMLtTmUKoj8oXw
最近花了一些时间在项目的性能优化上,背后做了很多工作,但是最后依然没有达到自己想要的结果,有些失望,但是还是记录下自己的执着。
性能优化总结:减少请求次数、减小资源大小、提高响应和加载速度、优化资源加载时机、优化加载方式。
前一段时间,小编也曾发布过几篇关于前端性能优化相关文章:
「优化篇」8000字总结的前端性能优化
前端性能优化之雅虎35条军规
2020前端性能优化清单(一)
2020前端性能优化清单(二)
2020前端性能优化清单(三)
2020前端性能优化清单(四)
2020前端性能优化清单(五)
2020前端性能优化清单(六)
对前端工程性能的优化,我觉得可以分为两类:
对于用户来说,用户的感知性能才是最重要的,简单讲,就是让用户感觉你的网站访问很快,并且感知性能没有衡量标准。
不过,凡事总有例外,如果一个页面的加载时间就会很长,我们也可以通过一些方式让用户觉得没有那么慢。
总之一句话,你的页面可以做的不快,但是你可以让你的用户觉得你很快。
对于开发者来说,性能指标是可以客观度量的,我们可以通过一些手段来优化 Web 性能,使这些度量指标达到开发者设定的标准。
客观性能是指,从用户输入url开始,到下载、解析和执行所有资源以及最终绘制的整个过程的时间度量。
性能指标是个很复杂的标准,后续我会单独整理出一篇文章介绍性能指标。
由于我司使用的是 @vue/cli,所以许多webpack配置脚手架已经帮你完成了,我就不叙述了,这里只讲基于 @vue/cli 做的一些优化配置
gzip 压缩效率非常高,通常可以达到 70% 的压缩率,也就是说,如果你的网页有 30K,压缩之后就变成了 9K 左右。
//npm i -D compression-webpack-pluginconfigureWebpack: config => { const CompressionPlugin = require('compression-webpack-plugin') config.plugins.push(new CompressionPlugin())}
线上项目自然不应该被看到控制台的打印日志,所以我们需要将 console.log 都去除掉。
//npm i -D terser-webpack-plugin configureWebpack: config => { const TerserPlugin = require('terser-webpack-plugin') config.optimization.minimizer.push( new TerserPlugin({ extractComments: false, terserOptions: { compress: { drop_console: true } }, }) ) }
由于打包后的文件经过了压缩、合并、混淆、babel编译后的代码不利于定位分析bug。
module.exports = { productionSourceMap: false,}
使用 cdn 文件来减少工程到打包体积,也可以按需加载。
在 /public/index.html 中引入需要的js和css文件
去掉 package.json 中对于 vue、element-ui 等相关资源的依赖
src/main.js ,去掉 vue、element-ui 等相关资源的 import 和 vue.use 这些语句
配置externals。由于使用 Vue Cli 3 默认配置,新建出来的项目没有了 build 目录,首先得在项目根目录下,新建 vue.config.js 文件,里面添加以下代码:
module.exports = { configureWebpack:{ externals:{ 'Vue': 'Vue', 'element-ui': 'element-ui', 'clipboard':'VueClipboard' } }}
渲染方式分为三种,客户端渲染,服务端渲染,预渲染。
我们默认的开发方式是通过客户端渲染,但是客户端渲染页面内容,关键链路较长,首屏渲染会有一定延迟,并且对 SEO 非常不友好,对于C端的产品来说,是不可行的。
所以很多公司都会通过服务端渲染(SSR)或是 预渲染的方式来解决这两点问题,由于公司技术栈原因,我们采用预渲染的方式来做优化。
什么是预渲染?
简单说,就是将浏览器解析 javascript 动态渲染页面的这部分工作,在打包阶段就完成了,(只构建了静态数据)换个说法在构建过程中,webpack 通过使用 prerender-spa-plugin 插件生成静态结构的 html。
// npm i -D prerender-spa-plugin configureWebpack: config => { const path = require('path') const PrerenderSPAPlugin = require('prerender-spa-plugin') config.plugins.push( new PrerenderSPAPlugin({ staticDir: path.join(__dirname, 'dist'), routes: ['/'], minify: { collapseBooleanAttributes: true, collapseWhitespace: true, keepClosingSlash: true, decodeEntities: true, sortAttributes: true, }, renderer: new PrerenderSPAPlugin.PuppeteerRenderer({ renderAfterDocumentEvent: 'render-event', renderAfterTime: 5000, // headless: false, }), }) ) }
注意:路由模式必须为 history ,如果不设置 history 模式,也能运行和生成文件,每个 index.html 文件的内容都会是一样的。
ServiceWorker 是运行在浏览器后台进程里的一段 JS,它可以做许多事情,比如拦截客户端的请求、向客户端发送消息、向服务器发起请求等等,其中最重要的作用之一就是离线资源缓存。
ServiceWorker 拥有对缓存流程丰富灵活的控制能力,当页面请求到 ServiceWorker时,ServiceWorker 同时请求缓存和网络,把缓存的内容直接给用户,而后覆盖缓存,我司已经使用了 ServiceWorker 替换 HTTP缓存策略。
注意:需要HTTPS才可以使用 ServiceWorker
HTTP 缓存一般分为两类:强缓存(也称本地缓存) 和 协商缓存(也称304缓存)。
普通刷新会启用 协商缓存,忽略 强缓存。只有在地址栏或收藏夹输入网址、通过链接引用资源等情况下,浏览器才会启用 强缓存。
本地缓存是最快速的一种缓存方式,只要资源还在缓存有效期内,浏览器就会直接在本地读取,不会请求服务端。
协商缓存,顾名思义是经过浏览器与服务器之间协商过之后,再决定是否读取本地缓存,如果服务器通知浏览器可以读取本地缓存,会返回304状态码,并且协商过程很简单,只会发送头信息,不会发送响应体。
缓存位置一般分为:Memory Cache(内存缓存)和 Disk Cache(硬盘缓存)
内存缓存:读取快、持续时间短、容量小
硬盘缓存:读取慢、持续时间长、容量大
Service Worker -> Memory Cache -> Disk Cache -> Push Cache
HTTP2 四个新特性:
server { listen 443 ssl http2;}
注意:使用 http2 的前提是必须是 https。
简单说,提前加载资源,当用户需要查看时可直接从本地缓存中渲染。
总结:对当前页面需要的资源,使用 preload 进行预加载,对其它页面需要的资源进行 prefetch 预加载。
preload 页面加载的过程中,在浏览器开始主体渲染之前加载。
prefetch 页面加载完成后,利用空闲时间提前加载。
注意:vue-cli 默认开启 prefetch ,可在 vue.config.js 中全局禁用 prefetch ,再针对指定模块开启。
chainWebpack: config => { config.plugins.delete('prefetch')}
页面加载完成后,利用空闲时间提前加载。
异步加载 js 文件,并且不会阻塞页面的渲染。
先来看一个普通的 script 标签解析过程。
webp 是一种新的图片格式,它的体积只有只有 JPEG 的2/3,将图片资源大量换成 webp 格式可以加快请求的速度。
我司的图片资源大部分都放在阿里的 OSS 上,并且阿里提供了接口,可以在线将 png/jpeg 转为 webp 格式。
注意:webp 格式在浏览器兼容上还有一定的问题,所以需要判断浏览器是否支持 webp 格式哦。
function check_webp_feature(feature, callback) { var kTestImages = { lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA", lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==", alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==", animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA" }; var img = new Image(); img.onload = function () { var result = (img.width > 0) && (img.height > 0); callback(feature, result); }; img.onerror = function () { callback(feature, false); }; img.src = "data:image/webp;base64," + kTestImages[feature];}
江湖人称菊花图 ....
不过,现在对于加载的设计体验有了比菊花加载体验更棒的方法 。
骨架屏可以带来更好的用户体验,有很强的加载感。
可惜的是,ElementUI 并没有提供骨架屏组件,反观 Antd ,真香...
第一个为骨架屏(前端骨架屏都是如何生成的),第二个为菊花图(关于图片懒加载的几种方案),第三个为无优化,可以看到相比于传统的菊花图会在感官上觉得内容出现的流畅而不突兀,体验更加优良。
如果这篇文章帮助到了你,欢迎点赞和关注.
《「实践」浏览器中的画中画(Picture-in-Picture)模式及其 API》
《「多图」一文带你彻底搞懂 Web Workers (上)》
《「多图」一文带你彻底搞懂 Web Workers (中)》
《深入细聊前端下载总结「干货」》
《细品西瓜播放器功能分析(上)「实践」》
《细品西瓜播放器功能分析(下)「实践」》
《细聊50道JavaScript基础面试题「附答案」》
《webpack4主流程源码解说以及动手实现一个简单的webpack(上)》
《webpack4主流程源码解说以及动手实现一个简单的webpack(下)》
《细聊前端架构师的视野》
《细聊应用场景再谈防抖和节流「进阶篇」》
《前端埋点统一接入方案实践》
《细聊微内核架构在前端的应用「干货」》
《一种高性能的Tree组件实现方案「干货」》
《进击的JAMStack》
《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上》
《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中》
《前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下》
《一文带你搞懂 babel-plugin-import 插件(上)「源码解析」》
《一文带你搞懂 babel-plugin-import 插件(下)「源码解析」》
《JavaScript常用API合集汇总「值得收藏」》
《推荐10个常用的图片处理小帮手(上)「值得收藏」》
《推荐10个常用的图片处理小帮手(下)「值得收藏」》
《JavaScript 中ES6代理的实际用例》
《12 个实用的前端开发技巧总结》
《一文带你搞懂搭建企业级的 npm 私有仓库》
《教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?》
《细说前端开发UI公共组件的新认识「实践」》
《细说DOM API中append和appendChild的三个不同点》
《细品淘系大佬讲前端新人如何上王者「干货」》
《一文带你彻底解决背景跟随弹窗滚动问题「干货」》
《推荐常用的5款代码比较工具「值得收藏」》
《Node.js实现将文字与图片合成技巧》
《爱奇艺云剪辑Web端的技术实现》
《我再也不敢说我会写前端 Button组件「实践」》
《NodeX Component - 滴滴集团 Node.js 生态组件体系「实践」》
《Node Buffers 完整指南》
《推荐18个webpack精美插件「干货」》
《前端开发需要了解常用7种JavaScript设计模式》
《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》
《了不起的 Webpack HMR 学习指南(上)「含源码讲解」》
《了不起的 Webpack HMR 学习指南(下)「含源码讲解」》
《10个打开了我新世界大门的 WebAPI(上)「实践」》
《10个打开了我新世界大门的 WebAPI(中)「实践」》
《10个打开了我新世界大门的 WebAPI(下)「实践」》
《「图文」ESLint 在中大型团队的应用实践》
《Deno是代码的浏览器,你认同吗?》
《前端存储除了 localStorage 还有啥?》
《Javascript 多线程编程的前世今生》
《微前端方案 qiankun(实践及总结)》
《「图文」V8 垃圾回收原来这么简单?》
《Webpack 5模块联邦引发微前端的革命?》
《基于 Web 端的人脸识别身份验证「实践」》
《「前端进阶」高性能渲染十万条数据(时间分片)》
《「前端进阶」高性能渲染十万条数据(虚拟列表)》
《图解 Promise 实现原理(一):基础实现》
《图解 Promise 实现原理(二):Promise 链式调用》
《图解 Promise 实现原理(三):Promise 原型方法实现》
《图解 Promise 实现原理(四):Promise 静态方法实现》
《实践教你从零构建前端 Lint 工作流「干货」》
《高性能多级多选级联组件开发「JS篇」》
《深入浅出讲解Node.js CLI 工具最佳实战》
《延迟加载图像以提高Web网站性能的五种方法「实践」》
《比较 JavaScript 对象的四种方式「实践」》
《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》
《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》
《使用Service Worker让你的 Web 应用如虎添翼(下)「干货」》
《前端如何一次性处理10万条数据「进阶篇」》
《推荐三款正则可视化工具「JS篇」》
《如何让用户选择是否离开当前页面?「JS篇」》
《JavaScript开发人员更喜欢Deno的五大原因》
《仅用18行JavaScript实现一个倒数计时器》
《图文细说JavaScript 的运行机制》
《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》
《推荐Web程序员常用的15个源代码编辑器》
《10个实用的JS技巧「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》
《深入JavaScript教你内存泄漏如何防范》
《手把手教你7个有趣的JavaScript 项目-上「附源码」》
《手把手教你7个有趣的JavaScript 项目-下「附源码」》
《JavaScript 使用 mediaDevices API 访问摄像头自拍》
《手把手教你前端代码如何做错误上报「JS篇」》
《一文让你彻底搞懂移动前端和Web 前端区别在哪里》
《63个JavaScript 正则大礼包「值得收藏」》
《提高你的 JavaScript 技能10 个问答题》
《JavaScript图表库的5个首选》
《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》
《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》
《教你如何用动态规划和贪心算法实现前端瀑布流布局「实践」》
《可视化的 js:动态图演示 Promises & Async/Await 的过程》
《原生JS封装拖动验证滑块你会吗?「实践」》
《如何实现高性能的在线 PDF 预览》
《细说使用字体库加密数据-仿58同城》
《Node.js要完了吗?》
《Pug 3.0.0正式发布,不再支持 Node.js 6/8》
《纯JS手写轮播图(代码逻辑清晰,通俗易懂)》
《JavaScript 20 年 中文版之创立标准》
《值得收藏的前端常用60余种工具方法「JS篇」》
《箭头函数和常规函数之间的 5 个区别》
《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》
《「前端篇」不再为正则烦恼》
《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》
《深入细品浏览器原理「流程图」》
《JavaScript 已进入第三个时代,未来将何去何从?》
《前端上传前预览文件 image、text、json、video、audio「实践」》
《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》
《推荐13个有用的JavaScript数组技巧「值得收藏」》
《前端必备基础知识:window.location 详解》
《不要再依赖CommonJS了》
《犀牛书作者:最该忘记的JavaScript特性》
《36个工作中常用的JavaScript函数片段「值得收藏」》
《Node + H5 实现大文件分片上传、断点续传》
《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》
《【实践总结】关于小程序挣脱枷锁实现批量上传》
《手把手教你前端的各种文件上传攻略和大文件断点续传》
《字节跳动面试官:请你实现一个大文件上传和断点续传》
《谈谈前端关于文件上传下载那些事【实践】》
《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》
《最全的 JavaScript 模块化方案和工具》
《「前端进阶」JS中的内存管理》
《JavaScript正则深入以及10个非常有意思的正则实战》
《前端面试者经常忽视的一道JavaScript 面试题》
《一行JS代码实现一个简单的模板字符串替换「实践」》
《JS代码是如何被压缩的「前端高级进阶」》
《前端开发规范:命名规范、html规范、css规范、js规范》
《【规范篇】前端团队代码规范最佳实践》
《100个原生JavaScript代码片段知识点详细汇总【实践】》
《关于前端174道 JavaScript知识点汇总(一)》
《关于前端174道 JavaScript知识点汇总(二)》
《关于前端174道 JavaScript知识点汇总(三)》
《几个非常有意思的javascript知识点总结【实践】》
《都2020年了,你还不会JavaScript 装饰器?》
《JavaScript实现图片合成下载》
《70个JavaScript知识点详细总结(上)【实践】》
《70个JavaScript知识点详细总结(下)【实践】》
《开源了一个 JavaScript 版敏感词过滤库》
《送你 43 道 JavaScript 面试题》
《3个很棒的小众JavaScript库,你值得拥有》
《手把手教你深入巩固JavaScript知识体系【思维导图】》
《推荐7个很棒的JavaScript产品步骤引导库》
《Echa哥教你彻底弄懂 JavaScript 执行机制》
《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》
《深入解析高频项目中运用到的知识点汇总【JS篇】》
《JavaScript 工具函数大全【新】》
《从JavaScript中看设计模式(总结)》
《身份证号码的正则表达式及验证详解(JavaScript,Regex)》
《浏览器中实现JavaScript计时器的4种创新方式》
《Three.js 动效方案》
《手把手教你常用的59个JS类方法》
《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》
《深入浅出讲解 js 深拷贝 vs 浅拷贝》
《手把手教你JS开发H5游戏【消灭星星】》
《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》
《手把手教你全方位解读JS中this真正含义【实践】》
《书到用时方恨少,一大波JS开发工具函数来了》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《手把手教你JS 异步编程六种方案【实践】》
《让你减少加班的15条高效JS技巧知识点汇总【实践】》
《手把手教你JS开发H5游戏【黄金矿工】》
《手把手教你JS实现监控浏览器上下左右滚动》
《JS 经典实例知识点整理汇总【实践】》
《2.6万字JS干货分享,带你领略前端魅力【基础篇】》
《2.6万字JS干货分享,带你领略前端魅力【实践篇】》
《简单几步让你的 JS 写得更漂亮》
《恭喜你获得治疗JS this的详细药方》
《谈谈前端关于文件上传下载那些事【实践】》
《面试中教你绕过关于 JavaScript 作用域的 5 个坑》
《Jquery插件(常用的插件库)》
《【JS】如何防止重复发送ajax请求》
《JavaScript+Canvas实现自定义画板》
《Continuation 在 JS 中的应用「前端篇」》