20230616----重返学习-前端性能优化方案-辅助知识

8 篇文章 0 订阅
5 篇文章 0 订阅
文章详细介绍了前端性能优化的各种方案,包括通过DLLPlugin缓存依赖、使用Vite或Turbopack提升打包速度、代码分割、减少HTTP请求次数和大小、CSSSprite、懒加载、服务器端渲染、CDN部署、HTTP/2.0协议、防抖节流等技术,旨在加快打包速度、提高页面渲染速度和运行时性能。
摘要由CSDN通过智能技术生成

day-093-ninety-three-20230616-前端性能优化方案-辅助知识

前端性能优化方案

CRP思想

  • 当代前端性能优化的主要方案:CRP思想「按照关键的渲染路径进行优化」;再结合一些性能监测工具,可以分析出性能消耗较大的地方,有针对性的进行优化;但最后整体来讲,我们主要还是围绕以下几方面进行优化:
    • 第一部分:加快打包速度和降低打包后文件的体积

      • 基于DllPlugin插件,把一些固定不变的依赖进行缓存,后期打包的时候就不会再打包这些文件了!
      • 可基于新一代打包工具 vite/turbopack 代替 webpack!
      • 基于 SplitChunksPlugin 插件对代码进行分割,让页面首次加载的JS文件变的更小!
      • 在webpack打包编译的时候,尤其是生产环境下,我们应该取消ESLint的检测、取消sourceMap文件的生成,以此来加快项目的打包速度!
      • 应该尽可能忽略对 node_modules 的处理,加快打包的速度
    • 第二部分:提高页面第一次渲染的速度

      • 减少HTTP请求的次数和大小

        • 使用 CSS Sprite(雪碧图)技术,把N多小图合并为一张大图
        • 对于非关键性资源(例如:图片、第三方插件、音视频等等),在页面首次渲染的时候,进行懒加载
        • 尽可能的进行封装,减少页面冗余代码,降低打包后文件的体积(还需要对代码进行压缩)
        • 第三方UI组件库需要按需导入
        • 服务器端开启GZIP压缩,加快HTTP的传输效率
        • 把CSS合并为一个,JS也合并为一个,以此来减少HTTP请求的次数!
      • 前端骨架屏技术

        1. 这种方式,可以让页面第一次更快的渲染出来,减少白屏等待时间!
        • SSR服务器渲染的骨架屏「主流模式」:vue + nuxt.js + node.js
        • 在服务器抗压能力较强的情况下,页面的首屏信息是服务器渲染的;第一次加载页面,只要向服务器发送请求,请求回来的HTML页面中,首屏的内容已经是在服务器渲染好的,客户端直接呈现即可!
          其余屏的数据,依然借用客户端骨架屏方案,由客户端动态获取数据进行绑定!
          • 客户端骨架屏:在页面第一次渲染的时候,把需要呈现内容的部分,用一些骨架(可以是灰色背景,也可以是Loading效果)先占位,第一次渲染完,再向服务器发送请求,把获取的真实数据,渲染到指定的区域!
      • 开启静态资源文件的CDN部署

      • 基于 Connection:keep-alive 保持TCP通道的长链接,防止每一次请求都要进行三握四挥,加快资源的获取

      • 减少对Cookie的使用,因为每一次向服务器发送请求,都会把本地的cookie自动传递给服务器

      • 资源要分服务器进行部署,在增加了DNS解析次数的基础上,使用DNS预解析,来加快域名解析速度!

      • 针对于图片方面的处理

        • 使用字体图标或者矢量图,来代替位图
        • 网站中使用位图,优先使用 webp 格式,其次是 jpeg/jpg 、png 、gif 这些格式
        • 基于webpack,对小于一定范围的图片进行BASE64处理,减少HTTP请求的次数,加快图片的渲染!「但是不要乱用,因为使用了BASE64,会增加html/css等文件的体积;而且增加了代码维护的成本(我们一般都需要基于webpack进行base64编译)」
      • 对于数据请求,可以采用分批次(例如:分页、触底加载)和异步加载(或数据的延迟加载)

      • 建议减少对 @import 的使用,因为其会阻塞GUI的渲染!对于移动端页面,如果样式代码较少,可以使用内嵌式!

      • 尽量把<link>放在页面的头部,让CSS资源的获取和GUI的渲染同时进行,加快页面的渲染;尽量把<script>放在页面的尾部,因为其会阻碍GUI的渲染,当然也可以基于设置async/defer,让<script>变为异步的!

      • 在SPA单页面应用开发的时候,一定要做 路由懒加载!「目的:把各组件的代码分开打包,减少主JS的体积,加快页面第一次渲染的速度」

      • 网站可以采用 HTTP/2.0 协议!

      • 减少HTML的层级嵌套,加快DOM-Tree的构建!

      • 在Vue/React的项目开发中,我们可以把一些文件,以单独的JS形式导入,例如:

        <script src="../vue.min.js"></script>
        <script src="../main.deqwewe.js"></script>
        
        • 这样做,可以让不会改变的框架代码,一直使用的是缓存机制!!
    • 第三部分:提高项目运行时的性能

      • 使用 防抖、节流 处理:防抖是只触发一次,节流是降低触发频率!
      • 合理使用闭包,并且手动清除不用的内存,以减少内存的开销「注意内存泄漏问题」!
      • 减少直接操作DOM,降低页面的重排(回流)和重绘!
      • 基于事件委托实现事件的绑定,可以提高产品运行的性能「提高40%~60%」
      • 对静态资源文件开启强缓存和协商缓存,对ajax请求的数据,基于本地存储进行临时性的缓存!
      • 在编写CSS样式的时候,要精简选择器(尤其是选择器前缀),因为CSS选择器是从右到左渲染的
      • 尽可能的使用CSS3动画(尤其是配合 transform 修改元素的样式),减少对JS动画(比如定时器、requestAnimationFrame)的使用,坚决不用flash动画,因为CSS动画性能更好!
      • 减少对CSS表达式的使用「例如:expression、calc」,这些东西都比较消耗性能
      • 项目开发中,一定要规避死循环/死递归,而且减少for/in循环的使用;基于循环处理指定的逻辑时,要考虑“时间复杂度 O(n)”,减少循环的嵌套!
      • 在项目开发中(尤其是Vue/React框架开发中),在组件销毁的时候,记得清除手动设置的 定时器、监听器 等,释放无用的内存!
      • 减少页面中 iframe 的使用!「<iframe>相当于视图框架,可以允许在父页面中,嵌入子页面;window.length 记录的就是页面中iframe的数量」
      • 超长列表的虚拟滚动处理「以后讲」
      • 对于需要实时通信的需求(例如:微信聊天、股市走向图、直播类、支付中的某些需求…), 尽可能基于 scoket.io 代替 定时器的长轮询!
      • 在Vue/React中,循环创建的元素一定要设置唯一的key值,而且最好不要用索引,因为这个key值是用来优化DOM-DIFF的!对于计算属性和监听器(computed/watch、useMemo/useCallback),也需要合理的使用,因为他们本身就消耗一些性能!
      • JS中不要使用 with/eval 等语句,因为这些语句非常非常消耗性能!
      • 对于较大的图片上传,我们可以使用切片上传和断点续传
      • 减少对Table标签的使用!
      • vue中合理选择v-show和v-if,v-show适用于频繁切换节点,v-if适合只更改一次的情况!
      • 在React框架中,基于 shouldComponentUpdate 进行组件更新优化!{例如:继承PureComponent来对新老属性/状态进行浅比较}
      • 在Vue开发中,把从服务器获取的数据基于 Object.freeze() 进行冻结,让其不成为响应式数据!
      • 在SPA单页面项目中,对于某些组件采用 <keep-alive> 实现组件的缓存

辅助知识

事件基础知识

  • 什么是事件?

    • 事件是浏览器赋予元素的默认行为,即便什么都不处理,元素该具备的事件都有!
    • 浏览器标准事件
  • 什么是事件绑定?

    • 给元素的事件行为绑定方法,当事件行为触发,会执行对应的方法!

    • DOM0事件绑定:

      box.onclick = function(){ ... }
      
      • 原理:给元素对象的 onxxx 事件私有属性赋值
        • 只能给元素的此事件行为绑定一个方法
        • 必须拥有此事件私有属性才可以,某些标准事件是没有对应的事件私有属性的「例如:DOMContentLoaded」
    • DOM2事件绑定:

      box.addEventListener('click',function(){ ... },布尔/对象)
      
      • 参数
        • 最后参数是布尔类型值,是控制触发的阶段 true:捕获 false:冒泡
        • 最后参数是对象,则是设置相关的配置项
          • capture 控制触发阶段
          • once 只能触发一次,触发完毕后,会自动基于 removeEventListener 移除事件绑定
          • passive 设置为true之后,则禁止阻止默认行为
      • 原理:利用浏览器内置的事件池机制,完成事件绑定及触发管理的
        • 只要是浏览器支持的标准事件,都可以用 addEventListener 做事件绑定「比DOM0的功能强大」
        • 可以给同一个元素的同一个事件类型,绑定多个不同方法,当事件触发的时候,所有绑定的方法会依次被触发执行
  • 什么是事件对象?

    • 所谓事件对象,就是一个对象,只不过记录了本次触发的相关信息;当事件触发,绑定的方法执行,会把分析好的事件对象,作为实参传递给每个函数!!

        box.addEventListener('click',function(ev){ 
          // ev:获取的事件对象信息
        })
      
    • 常用的事件对象:MouseEvent(PointerEvent/TouchEvent)、KeyboardEvent、Event…

    • 事件对象中常用的信息:

      • altKey: false
      • ctrlKey: false
      • shiftKey: false
      • clientX/clientY 操作点距离可视窗口的坐标
      • pageX/pageY 操作点距离BODY的坐标
      • srcElement/target 事件源
      • type 事件类型
      • which/keyCode 键盘的按键码
      • preventDefault 阻止默认行为
      • stopPropagation/stopImmediatePropagation 阻止事件传播
      • composedPath 获取传播的路径「事件源 -> … -> Window」
  • 事件的传播机制

    • 对于支持事件传播的事件行为来讲,当事件行为触发的时候,会经历三个阶段:
      • CAPTURING_PHASE: 1 捕获阶段「从window开始查找,一直找到事件源,其目的是为冒泡阶段分析好传播路径」
      • AT_TARGET: 2 目标阶段「把事件源的相关事件行为触发」
      • BUBBLING_PHASE: 3 冒泡阶段「并让其祖先元素的相关事件行为也被触发,而且是从事件源 -> Window」
    • 有一些事件行为是不支持“事件传播”的,例如:
      • mouseenter/mouseleave
      • load
  • 事件委托「事件代理」

    • 事件委托就是利用事件的传播机制,来实现的一种项目解决方案
    • 例如:一个容器中有很多后代元素,其中大部分后代元素,在点击的时候,都会处理一些事情「有些元素点击做相同的事情,有些点击做不同的事情」
      1. 传统方案:想操作哪些元素,就把这些元素全部都获取到,然后逐一做事件绑定
        • 这种方案不仅操作起来复杂,并且会开辟很多堆内存,性能也不是很好
      2. 新方案:利用事件的传播机制,不逐一获取元素和单独绑定事件了,而是只给外层容器做一个事件绑定,这样不管点击其后代中的哪一个元素,当前容器的点击事件也会触发,把绑定的方法执行;在方法中,我们只需要判断事件源是谁,从而处理不同的事情即可! ===> 事件委托
        • 这种新方案的性能比传统方案提高40%以上!
          • 例如:我们容器中的元素不是写死的,而是动态绑定(动态创建)的,而且会持续动态创建;我们需要在点击每个元素的时候,做一些事情!
            1. 传统方案:每一次动态创建完,都需要获取最新的元素,给这些元素单独做事件绑定!
            2. 事件委托:只需要给容器的点击事件绑定方法,不论其内部元素是写死的,还是动态绑定的,只要点击了,都说明元素已经存在了,基于事件传播机制,我们只需要在外层容器中判断事件源,做不同的事情即可!
              事件委托可以给动态绑定的元素做事件绑定!!
    • 真实项目中还有很多需求,必须基于事件委托来完成!所以:以后再遇到事件绑定的需求,首先想是否有必要基于事件委托处理,如果确定有必要,则直接基于事件委托处理!
  • mouseenter 与 mouseover的区别:是否会事件冒泡。

  • 拖拽操作:

    1. mousedown/mousemove/mouseup 「注意:鼠标焦点丢失问题」
    2. dragstart/drag/dragend 拖拽事件
      • dragenter/dragleave/drop 拖拽中让元素进入/离开目标位置&放在目标位置上
      • 使用这套事件模型,需要给元素设置draggable属性

事件对象的特殊性

  • 每一个事件操作,都有且只有一个事件对象。事件传播过程中的事件对象都是同一个。
    • 比如说点击一个元素,就会有一个事件对象产生。但在捕获阶段和冒泡阶段所触发的事件中,途中所经历的各个DOM元素的事件属性上绑定的各个事件中,所接收到的事件对象都是同一个内存地址的对象。在该对象上绑定的属性,也会在各个函数中共享。

base64

  • src为url的图片的渲染步骤。

    1. 经过对url解析之后发起网络请求,获取url对应的资源。
    2. 对资源进行base64编码。
    3. 把base64编码进行渲染成页面。
  • src为base64编码的图片的渲染步骤。

    1. 直接把base64编码渲染成页面。

http协议版本

  • HTTP1.0 VS HTTP1.1 VS HTTP2.0

  • HTTP1.0和HTTP1.1的一些区别

    1. 缓存处理,HTTP1.0中主要使用 Last-Modified,Expires 来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略:ETag,Cache-Control…
    2. 带宽优化及网络连接的使用,HTTP1.1支持断点续传,即返回码是206(Partial Content)
    3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除…
    4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
    5. 长连接,HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点
  • HTTP2.0和HTTP1.X相比的新特性

    1. 新的二进制格式Binary Format,HTTP1.x的解析是基于文本,基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合,基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便健壮
    2. header压缩HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header的fields表,既避免了重复header的传输,又减小了需要传输的大小
    3. 服务端推送server push,例如我的网页有一个style.css的请求,在客户端收到style.css数据的同时,服务端会将style.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了
    4. 多路复用MultiPlexing: 多个请求可同时在一个连接上并行执行,某个请求任务耗时严重,不会影响到其它连接的正常执行;

进阶参考

  1. 127.0.0.1和0.0.0.0地址的区别
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值