欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前端性能优化-笔记

程序员文章站 2022-05-14 15:55:16
...

浏览器从输入url到页面呈现的过程做了哪些事情?

  1. 域名解析:解析输入的域名查找对应的IP地址
    1.1查找浏览器自身的DNS缓存
    1.2没有找到条目,浏览器会搜索操作系统自身的DNS缓存
    1.3如果在DNS缓存也没有找到,那么尝试读取hosts文件
       (位于C:\Windows\System32\drivers\etc)
    1.4如果hosts文件也没有找到,浏览器就会发起DNS解析请求,直到找到对应的IP地址
复制代码
  1. TCP请求:
    浏览器向web服务器发起TCP连接请求: 发起TCP的3次握手
复制代码
  1. HTTP请求:
    浏览器会通过TCP连接向远程服务器发送HTTP请求
复制代码
  1. 服务器端响应HTTP请求:
    服务器端处理请求,返回html文件
复制代码
  1. 浏览器解析html文件:
    解析html文件,加载js,css,image等静态资源
    向服务器端请求下载,会使用多线程去下载,不同浏览器之间线程数不一样
    可以用上keep-alive特性,建立一次HTTP连接,连续请求多个静态资源
    比如: link标签,发送请求获取css
           script标签,发送请求获取js
           img标签,发送请求获取image图片
复制代码
  1. 浏览器根据html和css形成DOM树和渲染树,绘制到浏览器。
  2. 数据加载完成,断开连接

资源合并与压缩

  1. 合并:减少HTTP请求的数量

  2. 压缩:减少请求资源的大小

    html压缩:压缩在文本文件中有意义,但是在html中不显示的字符,包括空格,制表符,换行符等, 还有压缩一些其他意义的字符,如HTML注释

     方式: 
         1. 在线网站压缩
         2. nodejs提供了html-minifier工具
    复制代码

    css压缩: 无效代码删除 css语义合并

     方式: 
         1. 使用clean-css对cssj进行压缩
         2. 使用html-minifier对html中的css进行压缩
    复制代码

    js压缩与混乱: 无效字符的剔除: 比如注释回车空格,比如无效的变量 剔除注释 代码语义的缩减和优化: 比如变量名称特别长sommmmesuisan, 压缩为s变量 代码保护: 代码的混乱对前端代码的保护,将代码的风格变的混乱降低可读性

     方式: 
         1. 使用uglifyjs2对jsj进行压缩
         2. 使用html-minifier对html中的js代码进行压缩
    复制代码

    文件合并:

     文件合并的带来的问题:
         1. 首屏渲染问题: 首屏js文件加载过长,比如vue就会出现这样的情况
         2. 缓存失效问题: a17abdh12.js 和b128hsg12h.js两个文件合并后,如果
             a17abdh12.js发生了变化,那么这两个文件合并后的js文件肯定也会变化
             。导致用户在访问网站的时候之前缓存的js文件失效。
     
     真实场景:
         1. 一般公共库会合并成一个文件,业务代码会合并为一个文件。公共库修改
             非常少,而业务代码变动的可能性非常高,所以业务代码改动不会影响
             到公共库代码的缓存的失效。
     
     方式: 
         1. 利用nodejs实现文件合并,比如webpack来进行合并
    复制代码

    开启gzip:

     服务端开启gzip压缩,缩小文件大小,浏览器获取到gzip文件后在解压缩
    复制代码

图片相关

    png8/png24/png32之间的区别
    
    1. png8 --- 256色 + 支持透明  + 文件小很多
    2. png24 --- 2^24色 + 不支持透明 + 文件大很多
    3. png32 --- 2^24色 + 支持透明 + 文件最大
    
    备注:每种图片格式都有自己的特点,针对不同的业务场景选择不同的图片格式非常重要
    
    不同格式图片常用的业务场景: 
    
    1. jpg有损压缩,文件体积小,不支持透明   ---大部分不需要使用透明图片的场景
    2. png支持透明,浏览器兼容性好     --- 大部分需要使用透明图片的场景
    3. webp压缩体积最小,但是兼容性存在问题   --- ios支持存在问题,安卓尝试使用
    4. svg矢量图,代码内嵌,相对较小,图片样式相对简单的场景  --- 图片样式相对简单的场景
    
复制代码

图片压缩

  1. css雪碧图,整合到一张图片上,减少网站的HTTP请求数量。带来的缺点: 整合图片比较大的时候,一次加载就比较慢了。所以雪碧图也要控制大小

  2. Image inline,将图片的内容内嵌到html当中,减少网站HTTP请求数量, 也就是base64格式的图片,直接写在html中,就不会发送HTTP请求了。

  3. 使用矢量图, 使用SVG进行矢量图的绘制,使用iconfont解决icon问题。

  4. 在安卓下使用webp,图片体积非常非常小,同时具有无损和有损压缩。 在使用webp应该采用降级方式,如果支持使用webp,否则默认还是使用jpg。

html页面加载渲染的过程

浏览器解析html标签渲染成DOM树,当加载到link标签,会并发的向服务器发送css的资源请求。 当加载到script标签,会向服务器请求js资源,对css进行解析后结合DOM树生成渲染树,接着进行布局,然后绘制。

html渲染过程的特点

  1. 顺序执行, 并发加载

    • 词法分析: 浏览器对html文档解析的方式,从最开始的部分对标签进行从上到下的解析,解析过程从上到下,顺序执行!
    • 并发加载: html引入的外部资源是并发加载的
    • 并发上限: 某个域名下的请求数量是存在上限的,所以对某个域名下所请求资源的数量从而避免达到并发上限
  2. 是否阻塞

    • css阻塞
        1. head标签中阻塞页面渲染
            推荐使用link标签的方式在head中在html中引入
        2. css不阻塞外部脚本js的并发加载,但是会阻塞外部脚本js的执行
            因为js在操作DOM的时候,有可能涉及到css样式的修改,而这个css样式的修改
            是基于之前css样式的渲染。所以在css代码渲染完毕,才会进而执行js代码,
            也就是css代码阻塞了外部js的执行
    复制代码
    • js阻塞
        1. 直接引入的js阻塞页面的渲染,比如<script src='a.js'></script>
            js代码中很有可能去调用document.write方法来修改DOM文档的结构,
            所以需要等待js代码执行完毕,进而去继续执行html标签的分析,
            所以js脚本代码中不要出现document.write这样的代码
        2. js不阻塞外部资源的加载
        3. js顺序执行,会阻塞后续js逻辑的执行
            举个例子:<script src='a.js'></script>
                      <script src='b.js'></script>
            按照这样的顺序引入,会在a.js里面的代码执行完毕接着执行b.js里面的代码
            因为js的执行是单线程的,也就是b.js里面执行的代码可以依赖a.js中的代码
        
    复制代码
  3. 引入方式

  • js引入方式
    1. 直接引入
    2. defer: 确保所有的DOM树都生成
    3. async:不能确保脚本js文件之间的依赖关系,所以async引入的js脚本不能存在依赖关系
    4. 异步动态引入js: 在需要某个js文件的时候,通过生成script标签来引入

复制代码

css和js文件加载过程的优化点

  1. css样式表存放在head中,确保css和html一起生成渲染树,而不至于从没有样式,跳转到有样式的情况,出现页面闪动。
  2. 用link标签代替@import这样引入css的方式,@import的方式无法并发加载,另外@import这样的方式会在DOM加载结束后才会加载
  3. js脚本文件放在body底部
  4. 合理使用js的异步加载能力,就是采用动态生成script标签,动态加载所需要的js文件

async 和 defer

  1. async 确保在所有html页面dom渲染完之后才会加载
  2. async 加载的js脚本文件,不是按照顺序加载的,所以只有几个js脚本没有依赖关系,才可以通过async的方式来加载脚本文件
  3. defer 不阻塞页面渲染,是按照顺序执行的,会按照script的顺序依次执行。在DOM渲染完成之后才会执行。

动态引入js脚本

    function loadScript(src) {
        let ele = document.createElement('script');
        ele.type = 'text/javascript';
        ele.src = src
        document.body.appendChild(ele)
    }
    loadScript('demo.js')

复制代码

懒加载和预加载

懒加载

  1. 图片进入可视区域之后请求图片资源
  2. 对于电商等图片很多,页面很长的业务场景非常适用
  3. 减少无效资源的加载,因为可能用户只访问100张图片,剩下的100张是没有必要加载出来,这些资源就是属于无效资源。

预加载

  1. 图片等静态资源在使用之前提前请求图片资源
  2. 资源在使用到的时候从缓存中加载,提升用户体验

重绘和回流

回流(reflow): 当render tree中的一部分因为元素尺寸,布局,隐藏等改变而需要重新构建,称为回流。 当页面布局和几何属性改变时就会回流。

触发回流的属性

  1. 盒子模型相关属性
    width, height, padding, margin,display,border-width, border,min-height
复制代码
  1. 定位属性
    top, bottom, left, right, position, float, clear
复制代码
  1. 改变节点内部文字结构像相关属性
    text-align, overflow-y, font-weight, overflow, font-family, line-height
    vertical-align, white-space, font-size
复制代码

重绘:当render tree中一些元素需要更新属性,而这些属性只是影响元素颜色风格,而不影响布局的,比如background-color,color等 称为重绘。

只会触发重绘的属性

    color, border-style, border-radius, visibility, text-decoration, background,
    background-size, background-image,background-position, background-repeat,
    outline-color, outline, outline-style, box-shadow
复制代码

重绘不一定会触发回流,但是回流一定会绘制重绘

重绘回流优化方式:

  1. 避免使用触发重绘,回流的CSS属性
  2. 将重绘和回流的范围限制在单独的图层之内(暂时不理解.....)

实际工作中CSS关于回流和重绘的总结

  1. translate 替换top的变化
    translate不会触发回流,top会触发回流

  2. opacity 替换visiblity
    visiblity只会触发重绘的过程,opacity不会触发重绘的过程

        // bad
        setTimeout(() => {
            document.getElementById('box').style.visiblity = 'hidden'
        }, 2000)
        
        // good
        #box {
            width: 100px;
            height: 100px;
            background: red;
            transform: translateZ(0);  // 在浏览器的层面建立一个新图层
            opacity: 1;
        }
        setTimeout(() => {
            document.getElementById('box').style.opacity = 0
        })
    复制代码
  3. 不要一条一条的地修改DOM的样式,预先定义好class,然后修改DOM的ClassName

    // bad
    var box = document.getElementId('box')
    setTimeout(() => {
        box.style.width = '200px';  
        box.style.height = '200px';
        box.style.background = 'red'
    })
    
    // good
    .box {
        width: 200px;
        height: 200px;
        background: red;
    }
    setTimeout(() => {
        box.classList.add('box');
    })
复制代码
  1. 把DOM离线后修改(比如,需要频繁操作DOM并将DOM的样式进行修改,那么可以先将DOM的display:none(有一次回流),然后你修改DOM样式100次,在display:block显示出来)
  2. 不要使用table布局,可能很小的小改动会造成整个table的重新布局
  3. 合理选择动画的速度
  4. 当使用实现动画的时候,启用GPU硬件渲染加速,当然如果数据量很大,需要从cpu到gpu的过程也是非常耗费时间的,这个也是需要考量的
    .box {
        transform: translateZ(0);
        transform: translate3d(0,0,0);  // 启用GPU硬件加速
    }
```

复制代码

转载于:https://juejin.im/post/5cad8f105188251af07a46fa