前端性能优化

前端性能优化,第1张

经过自己的学习以及参考各类文章,发现前端的很多知识,都是能够串联起来的,而性能优化则可以从一个常见的面试题展开。(深究的大佬请路过,本篇文章就是给大伙搭一个大致的框架)

从输入 URL 到页面加载完成,发生了什么?

DNS 解析TCP 连接HTTP 请求服务端处理请求,HTTP响应返回浏览器拿到响应数据,解析响应内容,把解析结果展示给用户

不难看出,对于 DNS 解析 ,与 TCP 连接,我们前端能做的努力非常有限。但是对于后面三点,才是我们可以施展手脚的地方。


目录 HTTP的请求与响应部分1、webpack 性能优化方案1. 不要让 loader 做的事情太多2. 不要放过第三方数据库3. 将 loader 由单进程转为多进程4. 删除冗余代码5. 按需加载6. 开启Gzip压缩 浏览器部分1、浏览器底层(内核)1. 渲染引擎Ⅰ. HTML 解释器Ⅱ. CSS解释器Ⅲ. 图层布局计算模块Ⅳ. 视图绘制模块 2. JS 引擎 2、优化方案1. CSS优化2. DOM优化3. 防抖与节流4. 懒加载 Lazy-Load5. 缓存

HTTP的请求与响应部分 减少请求次数减少单次请求所花费的时间

对于第一点,次数问题,我们能否将多次请求合并到一次请求中呢?

对于第二点,时间开销问题,一次传输的内容过多,体积过大,是否可以让体积变小呢?

这两点的矛头都指向了 webpack,回归 打包 压缩。(我们这里不谈如何使用webpack,将注意力放到如何优化 webpack 性能上)

webpack 构建过程非常耗时webpack 打包的结果体积太大 1、webpack 性能优化方案 1. 不要让 loader 做的事情太多

我们这里以 babel-loader 为例

转译功能是非常强大的,但是它也是非常慢的。

最简单的优化方式就是避免不必要的转译,利用 exclude

2. 不要放过第三方数据库

我们这里以 node_modules 为例

依赖包对于整个项目来讲非常重要,但它确实非常大。

这里推荐大家使用 DllPlugin,使用这个插件后,会将第三方库单独打包到一个文件中,这个文件就是一个单纯的依赖库。这个依赖库不会跟着你的业务代码一起被重新打包,只有当依赖自身发生版本变化时才会重新打包。

3. 将 loader 由单进程转为多进程

利用 Happypack

我们知道,webpack是单线程的,所有任务只能排队一次执行。而 Happypack 便可以解决这个问题,它会充分利用我们的 CPU,将任务分解给多个子进程去并发执行,大大提升打包效率。

4. 删除冗余代码

一个比较典型的案例 Tree-Shaking

Tree-Shaking 可以在编译的过程中获悉哪些模块并没有真正被使用,这些没用的代码,在最后打包的时候会被去除。

而在 webpack 中,我们可以使用 UglifyJsPlugin

5. 按需加载

举个例子,如果我们有十个页面,且都非常复杂。如果我们把整个项目打包,当用户打开我的网站时,大概率会卡死。那该怎么办呢?是不是只需要加载用户当前所看到的页面就行了呢?

webpack 的按需加载也是如此。写好异步回调函数,当加载到的时候,再获取资源进行打包。

6. 开启Gzip压缩
浏览器部分 1、浏览器底层(内核) 1. 渲染引擎 Ⅰ. HTML 解释器

将 HTML 经过词法分析,解析出 DOM 树。

Ⅱ. CSS解释器

将 CSS 经过词法分析, 解析出 CSSOM 树。

CSSOM 树与 DOM 树结合,形成 Render 树(渲染树)

Ⅲ. 图层布局计算模块

从根节点递归调用,布局计算每个元素的精确位置和大小,给每个节点所应该出现在屏幕上的精确坐标,基于 Render 渲染树的布局形成 布局渲染树(Layout of the render tree)

Ⅳ. 视图绘制模块

遍历 Render 渲染树,进行具体节点的图像绘制,将像素渲染到屏幕上。整个过程形成 绘制渲染树(Painting the render tree)

最后整合图层,将数据由 CPU 输出给 GPU ,最终绘制在屏幕上。

2. JS 引擎

编译执行 Javascript 代码。

2、优化方案

回流与重绘大家都不会陌生。

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。

我们的优化 *** 作,可以说都是基于以上两点来进行。

1. CSS优化 避免使用通配符,只对需要用到的元素进行选择关注可以通过继承实现的属性,避免重复匹配重复定义少用标签选择器。如果可以,用类选择器替代 2. DOM优化

简而言之,就是减少对dom的 *** 作。

我们来看以下例子

for(var count=0;count<10000;count++){ 
	document.getElementById('container').innerHTML+='我是一个小测试'
} 

显然,这里我们获取dom的次数太多了,10000次

我们可以通过缓存变量的方式来解决

// 只获取一次container
let container = document.getElementById('container')
for(let count=0;count<10000;count++){ 
  container.innerHTML += '我是一个小测试'
} 

到这里其实还有一个问题。每次放入一个 span ,就会引起渲染树的变化,会使得页面重新进入回流与重绘的 *** 作,这样子消耗的性能是十分昂贵的。

解决方案:

let container = document.getElementById('container')
let content = ''
for(let count=0;count<10000;count++){ 
  // 先对内容进行 *** 作
  content += '我是一个小测试'
} 
// 内容处理好了,最后再触发DOM的更改
container.innerHTML = content

以上做的两种优化,就是为了减少dom的 *** 作,总而减少回流与重绘。

3. 防抖与节流

为了防止用户的 *** 作频繁触发,导致的大量计算,引发页面的抖动甚至卡顿,防抖与节流出现了

防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时

节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效

以下是简单手写案例:

防抖:

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
head>

<body>
    <input type="text" id="input">
body>
<script>
    let input = document.getElementById("input")
    function debounce() {
        let timer
        return function (value) {
            clearTimeout(timer)
            timer = setTimeout(() => {
                console.log(value)
            }, 2000);
        }
    }
    let fn = debounce()
    input.addEventListener('keyup', (e) => {
        // console.log(e.target.value)
        fn(e.target.value)
    })
script>

html>

节流:

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
head>

<body>
    <button id="btn">点击button>
body>
<script>
    let btn = document.getElementById("btn")
    function throttle(delay) {
        let lasttime = 0
        return function () {
            let nowtime = new Date()
            if (nowtime - lasttime > delay) {
                setTimeout(() => {
                    console.log(nowtime)
                }, delay);
                lasttime = nowtime
            }
        }
    }
    btn.onclick = throttle(2000)
script>

html>
4. 懒加载 Lazy-Load

Lazy-Load,懒加载。主要是针对图片加载时机的优化。当用户一进到页面就显示所有的图片的话,那么必然会造成白屏、卡顿等现象。大伙都知道图片是非常大的,加载所需要消耗的时间不言而喻。而懒加载,其与按需加载非常相似,仅加载显示用户看得到的图片,这样一来,性能的压力小了,用户的体验却没有变差。

5. 缓存

这里是 Chrome 官方给出的解释

通过网络获取内容既速度缓慢又开销巨大。较大的响应需要在客户端与服务器之间进行多次往返通信,这会延迟浏览器获得和处理内容的时间,还会增加访问者的流量费用。因此,缓存并重复利用之前获取的资源的能力成为性能优化的一个关键方面。


参考文章:修言大佬——掘金小册

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/940924.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-17
下一篇 2022-05-17

发表评论

登录后才能评论

评论列表(0条)

保存