最近沉迷逛某蓝色软件,收益良多!万分感谢博主 海阔_天空,写的太棒了
下面是原文链接,我在原文的基础上浅做个笔记,方便个人快速复习
前端性能优化——首页资源压缩63%、白屏时间缩短86% - 掘金提升首屏的加载速度,是前端性能优化中最重要的环节,这里笔者梳理出一些 `常规且有效` 的首屏优化建议 通过对比优化前后的性能变化,来验证方案的有效性,了解并掌握其原理https://juejin.cn/post/7188894691356573754#heading-6
目录
1、路由懒加载
打包对比
实现原理 import()(分离 chunk)
webpackChunkName(指定打包时生成的代码块名)
路由懒加载 - 示例
2、组件懒加载
为啥用组件懒加载
组件懒加载 - 示例
组件懒加载 - 使用场景(条件触发的组件、公共组件、大体积 .js)
3、Tree shaking
作用(删除无用代码)
原理(ES6 可静态分析)
局限(只对用 export 导出的变量生效)
4、骨架屏
原理(将骨架屏内容放到 html 文件的根节点中)
骨架屏插件(vue-skeleton-webpack-plugin)
5、虚拟滚动
原理(监听 scroll,根据 scrollTop 截取 list 并展示)
虚拟滚动插件
6、Web Worker 优化长任务
控制台查看长任务(执行时间超过 50ms 的任务)
Web worker 举例(计算 20w 条数据)
Web Worker 通信时长
7、requestAnimationFrame GUI 定时器
是什么?有什么作用?(解决动画卡顿)
定时器区别(引擎、时间准确度、性能)
8、JavaScript 的加载方式
1)正常模式(阻塞DOM)
2)async 模式(不阻塞DOM,加载无序)
3)defer 模式(不阻塞DOM,加载有序)
4)module 模式(不阻塞DOM,加载有序)
5)preload 模式(link 属性,首页[关键]资源提前加载)
6)prefetch 模式(link 属性,非首页[关键]资源提前加载)
9、图片优化
动态裁剪图片(压缩裁剪)
图片懒加载(到可视区域再加载)
使用字体图标(体积小、样式灵活、兼容性好)
图片转 base64 格式(减少 http 请求)
10、参考文章
普通路由 vs 路由懒加载,在 SPA 项目中:
路由懒加载实现原理:
webpackChunkName:是一个注释,用于指定 webpack 在打包时生成的代码块的名称。它的作用是让 webpack 在打包时,将具有相同名称的模块打包到同一个代码块中,从而实现代码分割和按需加载的效果;如果不指定名字,则生成的文件名将不够语义化
下面是设置 路由懒加载 的例子:
// 通过 webpackChunkName 设置分割后代码块的名字
const Home = () => import(/* webpackChunkName: "home" */ "@/views/home/index.vue");
const MetricGroup = () => import(/* webpackChunkName: "metricGroup" */ "@/views/metricGroup/index.vue");
const routes = [
{
path: "/",
name: "home",
component: Home
},
{
path: "/metricGroup",
name: "metricGroup",
component: MetricGroup
},
]
以 公共组件-弹框 为例,如果在多个页面中引入,则打包时会被重复打包进多个文件
同时,弹框组件不是一进入页面就加载,而是需要用户手动触发显示
此时,就应该考虑 组件懒加载 了
弹框组件懒加载示例:
重新打包后:
组件懒加载的使用场景:
PS:资源拆分的过细也不好,会造成浏览器 http 请求增多
Tree shaking 的作用:消除无用的 JS 代码,减少代码体积
举个:项目中只用了 targetType(),未使用 deepClone();则 deepClone() 不会被打包
// util.js
export function targetType(target) {
return Object.prototype.toString.call(target).slice(8, -1).toLowerCase();
}
export function deepClone(target) {
return JSON.parse(JSON.stringify(target));
}
依赖于 ES6 的模块特性,ES6 模块依赖关系是确定的,可以进行静态分析,和运行时状态无关(也就是说,不用执行代码,就能判断啥用了啥没用),这就是 tree-shaking 的基础
CommonJS 是动态加载,执行后才知道引用的什么模块,不能通过静态分析去优化
并不是说所有无用的代码都可以被消除,下面的 deepClone() 仍然会被打包
// util.js
export default {
targetType(target) {
return Object.prototype.toString.call(target).slice(8, -1).toLowerCase();
},
deepClone(target) {
return JSON.parse(JSON.stringify(target));
}
};
// 引入并使用
import util from '../util';
util.targetType(null)
原因分析:
使用骨架屏,可以缩短 FP 白屏时间(高达86%),提升用户体验
SPA 单页应用,无论 vue 还是 react,最初的 html 都是空白的,需要通过加载 JS 将内容挂载到根节点上,造成长时间的白屏
骨架屏插件基于这种原理,在项目打包时,将骨架屏内容放到 html 文件的根节点中(根节点内部为骨架屏)
使用骨架屏插件,打包后的 html 文件:
// 根节点内部为骨架屏
这里以 vue-skeleton-webpack-plugin 插件为例,可以给 不同页面 设置不同骨架屏
安装
npm i vue-skeleton-webpack-plugin
vue.config.js 配置
// 骨架屏
const SkeletonWebpackPlugin = require("vue-skeleton-webpack-plugin");
module.exports = {
configureWebpack: {
plugins: [
new SkeletonWebpackPlugin({
// 实例化插件对象
webpackConfig: {
entry: {
app: path.join(__dirname, './src/skeleton.js') // 引入骨架屏入口文件
}
},
minimize: true, // SPA 下是否需要压缩注入 HTML 的 JS 代码
quiet: true, // 在服务端渲染时是否需要输出信息到控制台
router: {
mode: 'hash', // 路由模式
routes: [
// 不同页面可以配置不同骨架屏
// 对应路径所需要的骨架屏组件id,id的定义在入口文件内
{ path: /^\/home(?:\/)?/i, skeletonId: 'homeSkeleton' },
{ path: /^\/detail(?:\/)?/i, skeletonId: 'detailSkeleton' }
]
}
})
]
}
}
新建 skeleton.js 入口文件
// skeleton.js
import Vue from "vue";
// 引入对应的骨架屏页面
import homeSkeleton from "./views/homeSkeleton";
import detailSkeleton from "./views/detailSkeleton";
export default new Vue({
components: {
homeSkeleton,
detailSkeleton,
},
template: `
`,
});
只渲染可视区域的列表项,非可视区域的不渲染,滚动时 动态更新 可视区域
虚拟滚动原理:
虚拟滚动好处:在渲染 10w 个文本节点的情况下,使用虚拟滚动可以缩短 78% 白屏时长
虚拟滚动的插件有很多,比如:
简单介绍 vue-virtual-scroller 的使用,该插件主要有 RecycleScroller.vue、DynamicScroller.vue 两个组件:
// 安装插件
npm install vue-virtual-scroller
// main.js
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
Vue.use(VueVirtualScroller)
// 使用
{{ item.name }}
浏览器 GUI 渲染线程与 JS 引擎线程是互斥的关系
当页面中有很多长任务(执行时间超过 50ms 的任务)时,会造成页面 UI 阻塞,出现界面卡顿、掉帧等情况
查看页面的长任务:
把下方代码丢到 主线程 中执行,计算过程中,页面一直处于卡死状态,无法操作
let sum = 0;
for (let i = 0; i < 200000; i++) {
for (let i = 0; i < 10000; i++) {
sum += Math.random()
}
}
使用 Web Worker 执行上述代码时,计算过程中,页面正常可操作、无卡顿
// worker.js
onmessage = function (e) {
// onmessage获取传入的初始值
let sum = e.data;
for (let i = 0; i < 200000; i++) {
for (let i = 0; i < 10000; i++) {
sum += Math.random()
}
}
// 将计算的结果传递出去
postMessage(sum);
}
Web Worker 具体的使用与案例,详情见下方文章:
一文彻底了解Web Worker,十万、百万条数据都是弟弟https://juejin.cn/post/7137728629986820126
并不是执行时间超过 50ms 的任务,就可以使用 Web Worker,还要考虑 通信时长
举个:一个运算执行时长为 100ms,但是通信时长为 300ms, 用了 Web Worker 可能会更慢
当任务的运算时长 - 通信时长 > 50ms,推荐使用 Web Worker
新建一个 Web worker,浏览器会加载对应资源,下图中的 Time 是这个资源的通信时长(也叫加载时长)
requestAnimationFrame 是浏览器专门为动画提供的 API,功能类似于 setTimeout/setInterval
可以使用 requestAnimationFrame 来执行一些需要高性能的操作,例如动画、滚动、拖拽等
requestAnimationFrame 的刷新频率,与显示器的刷新频率保持一致,因此,使用 requestAnimationFrame 可以解决用 setTimeout/setInterval 制作动画卡顿的情况
setTimeout/setInterval、requestAnimationFrame 区别:
正常模式下,JS 会阻塞 dom 渲染
浏览器必须等待 index.js 加载和执行完成后,才能去做其它事情
async 模式下,JS 的加载是异步的,不会阻塞 DOM 渲染
async 加载是无顺序的,当它加载结束,JS 会立即执行
使用场景:若该 JS 与 DOM 元素没有依赖关系,可以使用 async 模式,比如埋点统计
defer 可以用来控制 JS 文件的执行顺序,比如 element-ui.js 和 vue.js
因为 element-ui.js 依赖于 vue,所以必须先引入 vue.js,再引入 element-ui.js
defer 模式下,JS 的加载是异步的,不会阻塞 DOM 渲染
defer 资源会在 DOMContentLoaded 执行之前,如果有多个设置了 defer 的 script 标签存在,则会按照引入的前后顺序执行,即便是后面的 script 资源先返回
使用场景:一般情况下都可以使用 defer,特别是需要控制资源加载顺序时
在主流的现代浏览器中,script 标签的属性可以加上 type="module",浏览器会对其内部的 import 引用发起 HTTP 请求,获取模块内容
这时 script 的行为会像是 defer 一样,在后台下载,并且等待 DOM 解析
Vite 就是利用浏览器支持原生的 es module 模块,开发时跳过打包的过程,提升编译效率
link 标签的 preload 属性:提前加载某些依赖
preload 特点:
vue2 项目打包生成的 index.html 文件,会给 首页资源 全部添加 preload,实现关键资源的提前加载
link 标签的 prefetch 属性:利用浏览器的空闲时间,加载页面将来可能用到的资源
prefetch 特点:
可以用于加载其他页面(非首页)所需要的资源,以便加快后续页面的打开速度
PS:现代框架已经将 preload、prefetch 添加到打包流程中了,可以通过灵活的配置,去使用预加载功能
如何去压缩图片,让图片更快的展示出来,有很多优化工作可以做(比如淘宝首页的图片资源都很小)
经过动态裁剪后的图片,可能会从 1.8M 降低至 12.8KB,加载速度显著提升
很多云服务,比如 阿里云 或 七牛云,都提供了图片的动态裁剪功能
只需在图片的 url 地址上动态添加参数,就可以得到你所需要的尺寸大小,比如:
http://7xkv1q.com1.z0.glb.clouddn.com/grape.jpg?imageView2/1/w/200/h/200
JavaScript 图片懒加载 - Web前端工程师面试题讲解_哔哩哔哩_bilibiliJavaScript 图片懒加载 - Web前端工程师面试题讲解, 视频播放量 35335、弹幕量 150、点赞数 1291、投硬币枚数 899、收藏人数 1643、转发人数 107, 视频作者 技术蛋老师, 作者简介 技能和观点的长期分享,相关视频:转行前端后的第一次技术面试,凉凉咯,图片懒加载和预加载,Vue怎么做图片懒加载?,Async Await关键字 - 让我们更优化地写代码 - JavaScript前端Web工程师,面试官:假如有十万条数据,前端应该怎么处理?分页渲染?no,你应该回答这个...,前端面试:说几种图片懒加载的实现方式。追问:怎么知道是否进入可视区域了?这几点必说……,Cookie、Session、Token究竟区别在哪?如何进行身份认证,保持用户登录状态?,彻底搞懂图片懒加载和预加载,公司昨天入职的211大学毕业的程序员,昨天看了项目代码,今天就开始写了,而且还没有报错,真的很厉害,JavaScript基础语法-dom-bom-js-es6新语法-jQuery-数据可视化echarts黑马pink老师前端入门基础视频教程(500多集)持续https://www.bilibili.com/video/BV1FU4y157Li/?spm_id_from=333.999.0.0
什么是图片懒加载?
对于一些图片量较大的首页,只需呈现可视区域的图片,当用户滑动页面时,再去加载新的
图片懒加载原理?
![]()
这里以 vue-lazyload 插件为例
// 安装
npm install vue-lazyload
// main.js 注册
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// 配置项
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png', // 图片加载失败时的占位图
loading: 'dist/loading.gif', // 图片加载中时的占位图
attempt: 1
})
// 通过 v-lazy 指令使用
-
字体图标优点:
将小图片转换为 base64 编码字符串,并写入 HTML 或者 CSS 中,减少 http 请求
转 base64 格式的优缺点:
可以使用 url-loader 将图片转 base64:
// 安装
npm install url-loader --save-dev
// 配置
module.exports = {
module: {
rules: [{
test: /.(png|jpg|gif)$/i,
use: [{
loader: 'url-loader',
options: {
// 小于 10kb 的图片转化为 base64
limit: 1024 * 10
}
}]
}]
}
};
路由懒加载原理及使用
vue-skeleton-webpack-plugin 骨架屏插件使用
前端性能优化-虚拟滚动
requestAnimationFrame制作动画
浅谈script标签中的async和defer
Tree-Shaking性能优化实践 - 原理篇
使用 Preload&Prefetch 优化前端页面的资源加载
github.com/xy-sea/blog
再次鸣谢博主 海阔_天空,写的太棒了