前端性能优化是一个非常大的体系,可以通过各种各样的方式来实现性能优化,比如:webpack 打包、网络请求、图片压缩、css 优化等等方式。
💡 欢迎加入「🌍」,一起学习交流前端和Node端技术
页面渲染的性能优化,大部分人会想到的是输入 URL 到游览器显示页面发生了什么?
这是一个非常大的体系,会从网络请求到游览器渲染,这次我们只讨论游览器渲染的流程:
我们知道在游览器碰到 script 标签时,如果没有defer
和async
,游览器就会立即加载并执行对应的 js 文件,就会造成阻塞。 因此我们可以使用defer
和async
是去异步加载外部的 JS 脚本文件,他们都不会阻塞页面的解析。
defer
和async
他们有啥差别?回流和重绘对游览器的性能消耗都是比较大的,回流必将引起重绘,重绘不一定会引起回流。因为,我们需要尽量避免造成页面的回流。
当元素的尺寸、结构、位置等信息发生变化时,游览器需要重新渲染部分或全部文档,进行重新布局的过程叫做回流。 引起回流的操作有很多:
当页面中元素的样式属性发生改变但不影响元素的布局位置时,游览器对这个元素的样式重新绘制的过程叫做重绘。
触发重绘的方式也很多:
页面中可能会存在一些通过 js 实现的动画效果,我们应该避免使用setTimeout()
和setInterval()
来实现,因为这种回调可能会导致丢失帧而发生卡顿。 requestAnimationFrame
是浏览器用于定时循环操作的一个接口,类似于setTimeout
,主要用途是按帧对网页进行重绘。显示器有固定的刷新频率(60Hz 或 75Hz),也就是说,每秒最多只能重绘 60 次或 75 次,requestAnimationFrame
的基本思想就是与这个刷新频率保持同步,利用这个刷新频率进行页面重绘。
页面中可能存在一些 CSS3 的动画属性,我们就可以考虑使用一些优化手段:
创建一个新的渲染层(减少回流)
创建合成层。合成层会开始 GPU 加速页面渲染,但不能滥用
其实就是减少图片的体积大小,提高网络请求速度。
就是像一些 icon 小图片,我们可以将这些图片集成在一张图片里,通过定位等相关技术实现展示需要的图标。
SVG 是可缩放矢量图形。(矢量:既有大小又有方向的量),可以按比例缩小,并支持压缩。
优点:
缺点:
和 canvas 的区别:
如果页面中存在大量的图片,一次性全部加载就会变的很慢,因此我们可以让页面先加载一个占位图,然后游览页面的时候,随着可视区域的变化,将原先的占位图替换成真实的图片。
能有效地提高页面加载速度,并且减少服务器负载。
实现方案一:
var imgs = document.querySelectorAll("img");
//offsetTop是元素与offsetParent的距离,循环获取直到页面顶部
function getTop(e) {
var T = e.offsetTop;
while ((e = e.offsetParent)) {
T += e.offsetTop;
}
return T;
}
function lazyLoad(imgs) {
var H = document.documentElement.clientHeight; //获取可视区域高度
var S = document.documentElement.scrollTop || document.body.scrollTop;
for (var i = 0; i < imgs.length; i++) {
if (H + S > getTop(imgs[i])) {
imgs[i].src = imgs[i].getAttribute("data-src");
}
}
}
window.onload = window.onscroll = function () {
//onscroll()在滚动条滚动的时候触发
lazyLoad(imgs);
};
实现方案二:
var imgs = document.querySelectorAll("img");
//用来判断bound.top<=clientHeight的函数,返回一个bool值
function isIn(el) {
var bound = el.getBoundingClientRect();
var clientHeight = window.innerHeight;
return bound.top <= clientHeight;
}
//检查图片是否在可视区内,如果不在,则加载
function check() {
Array.from(imgs).forEach(function (el) {
if (isIn(el)) {
loadImg(el);
}
});
}
function loadImg(el) {
if (!el.src) {
var source = el.dataset.src;
el.src = source;
}
}
window.onload = window.onscroll = function () {
//onscroll()在滚动条滚动的时候触发
check();
};
将图片转换为 webp 格式,减少体积提高速度。
代码实现参考:
防抖是在时间被触发 n 秒后,再执行回调,在这 n 秒内如果再被触发,则重新计时,像一些点击事件产生的网络请求等等。
防抖的应用场景:
节流则是在一个规定的时间内,只会触发一次事件的回调,像我们的页面滚动等等,可以用节流来控制事件调用的频率。
节流的应用场景:
在我们开发中,我们有很多的打包工具可以使用,像一些 webpack、vite、Gulp 等等,在这里我们以 webpack 为例。
既然说到了打包,那我们先来简单了解一下打包的流程: webpack 的运行是一个串行的过程
Compiler 对象
,加载所有配置的插件,执行 run 方法开始编译。entry 参数
找到所有的入口文件。Loader
对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。Loader
翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。Chunk
,再把每个 Chunk
转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。总结就是 3 个阶段:
Plugin
,实力化Compiler
那我们如何利用 webpack 提高打包效率呢,我们此处列举部分内容
当我们的项目体量逐渐变大的时候,打包就会变得非常漫长,因为他是单线程模式的,只能逐个文件处理。因此开启多进程去打包是非常有必要的。常见的 loader 有thread-loader
使用:只要把 thread-loader
放置在其他 loader 之前, 那 thread-loader
之后的 loader 就会在一个单独的 worker 池(worker pool)
中运行。
webpack 缓存的方法有很多,比如cache-loader
、HardSourceWebpackPlugin
、babel-loader
的cacheDirectory
标志。这些都可以在重新运行期间节约大量的时间,但是在初次运行的时候会比较慢。
使用起来比较简单,在一些性能开销大的 loader 之前添加此 loader
module.exports = {
module: {
rules: [
{
test: /\.js$/,
use: ["cache-loader", ...loaders],
include: path.resolve("src"),
},
],
},
};
第一次构建花费正常的时间,第二次构建显著加快
const HardSourceWebpackPlugins = require("hard-source-webpack-plugin");
const clientWebpackConfig = {
// ...
plugins: [
new HardSourceWebpackPlugin({
// cacheDirectory是在高速缓存写入。默认情况下,将缓存存储在node_modules下的目录中
// 'node_modules/.cache/hard-source/[confighash]'
cacheDirectory: path.join(
__dirname,
"./lib/.cache/hard-source/[confighash]"
),
// configHash在启动webpack实例时转换webpack配置,
// 并用于cacheDirectory为不同的webpack配置构建不同的缓存
configHash: function (webpackConfig) {
return require("node-object-hash")({ sort: false }).hash(webpackConfig);
},
// 当加载器、插件、其他构建时脚本或其他动态依赖项发生更改时,
// hard-source需要替换缓存以确保输出正确。
// environmentHash被用来确定这一点。如果散列与先前的构建不同,则将使用新的缓存
environmentHash: {
root: process.cwd(),
directories: [],
files: ["package-lock.json", "yarn.lock"],
},
info: {
mode: "none",
level: "debug",
},
cachePrune: {
maxAge: 2 * 24 * 60 * 60 * 1000,
sizeThreshold: 50 * 1024 * 1024,
},
}),
new HardSourceWebpackPlugins.ExcludeModulePlugin([
{
test: /.*\.DS_Store/,
},
]),
],
};
HMR 也就是热模块替换:在程序运行中,替换、添加、删除模块,而无需重新加载整个页面,从而提高了开发时的构建速度。
devServer: {
static: {
directory: path.join(__dirname, '../public'), // 通过 static.directory 配置项告诉 dev-server 监听文件。默认启用,文件更改将触发整个页面重新加载。可以通过将 watch 设置为 false 禁用。
},
client:{
progress: true, // 在游览器中以百分比显示编译进度
},
hot: true, // 启用webpack的热模块替换
compress: true, // 启用 gzip compression
host:"localhost", // 启动服务器域名
port: 9000, // 启动服务器端口号
open:true, // 自动打开浏览器
}
开发的时候我们定义了一些工具函数库,或者引入第三方工具函数库或组件。如果没有处理的话,打包时会引入整个库,但是实际上我们可能只是用上极小部分的功能。
这样将整个库都打包进去,体积就太大了。
Tree Shaking
是一种术语,通常用于描述一处 Javascript 中没有用上的代码。webpack 已经默认开启该功能,无需其他配置。
我们从 4 个角度对 webpack 和代码进行了优化:
作者:https://juejin.cn/post/7357909187953311785
因篇幅问题不能全部显示,请点此查看更多更全内容
怀疑对方AI换脸可以让对方摁鼻子 真人摁下去鼻子会变形
女子野生动物园下车狼悄悄靠近 后车司机按喇叭提醒
睡前玩8分钟手机身体兴奋1小时 还可能让你“变丑”
惊蛰为啥吃梨?倒春寒来不来就看惊蛰
男子高速犯困开智能驾驶出事故 60万刚买的奔驰严重损毁