Web前端优化性能的方法

Html部分

1.语义化HTML:好处在于可以使代码简洁清晰,支持不同设备,利于搜索引擎,便于团队开发;
2.减少DOM节点:加速页面渲染;
3.给图片加上正确的宽高值:这可以减少页面重绘,同时防止图片缩放;
4.防止src属性和link的href属性为空:当值为空时,浏览器很可能会把当前页面当成其属性值加载;
5.正确的闭合标签:如避免使用<div/>,浏览器会多一个将它解析成<div></div>的过程;
6.链接为目录或首页的地址后面加”/”,如http://www.qq.com/
7.用LINK而不用@import方式导入样式;
8.样式放在页头,JS放在页尾;
9.缩小favicon.ico并缓存;

CSS部分

1.避免使用 CSS Expressions(CSS表达式):如
2.避免使用 CSS Filter(CSS滤镜);
3.使用CSS缩写,减少代码量;
4.通过CSSSprites把同类图片合成一张,减少图片请求;
5.减少查询层级:如.header .logo要好过.header .top .logo;
6.减少查询范围:如.header>li要好过.header li;
7.避免TAG标签与CLASS或ID并存:如a.top、button#submit;
8.删除重复的CSS;
栗子:

id选择器(#myid)
类选择器(.myclassname)
标签选择器(div,h1,p)
相邻选择器(h1+p)
子选择器(ul > li)
后代选择器(li a)
通配符选择器(*)
属性选择器(a[rel="external"])
伪类选择器(a:hover,li:nth-child)

Javscript部分

1.尽量少用全局变量;
2.使用事件代理绑定事件,如将事件绑定在body上进行代理;
3.避免频繁操作DOM节点;
4.不使用EVAL;
5.减少对象查找,如a.b.c.d这种查找方式非常耗性能,尽可能把它定义在变量里;
6.类型转换:把数字转换成字符串使用”” + 1,浮点数转换成整型使用Math.floor()或者Math.round();
7.对字符串进行循环操作,譬如替换、查找,应使用正则表达式;
8.删除重复的JS;

其中,值得一提的是:
1、使用requestAnimationFrame来替代setTimeout和setInterval
希望在每一帧刚开始的时候对页面进行更改,目前只有使用 requestAnimationFrame 能够保证这一点。使用 setTimeout 或者 setInterval 来触发更新页面的函数,该函数可能在一帧的中间或者结束的时间点上调用,进而导致该帧后面需要进行的事情没有完成,引发丢帧
2、使用IntersectionObserver来实现图片可视区域的懒加载
传统的做法中,需要使用scroll事件,并调用getBoundingClientRect方法,来实现可视区域的判断,即使使用了函数节流,也会造成页面回流。使用IntersectionObserver,则没有上述问题
3、使用web worker
客户端javascript一个基本的特性是单线程:比如,浏览器无法同时运行两个事件处理程序,它也无法在一个事件处理程序运行的时候触发一个计时器。Web Worker是HTML5提供的一个javascript多线程解决方案,可以将一些大计算量的代码交由web Worker运行,从而避免阻塞用户界面,在执行复杂计算和数据处理时,这个API非常有用

服务器部分

1.尽量合并CSS、JS文件,或将其直接写在页面上,减少HTTP请求;
2.压缩CSS、JS文件,缩短文件传输时间;
3.避免404错误:特别要避免给404指定一个停摆页面,否则所有404错误都将会加载一次页面;
4.一般要求减少DNS查询次数,如同一个页面的请求资源尽量少的使用不同的主机名,这可以减少网站并行下载的数量,但很多网站为了加速下载资源其实是特意用了多个主机名,这里要做一个权衡;
5.使用CDN加速,使用户从离自己最近的服务器下载文件;
6.减少Cookie的大小,使用无cookie的域,客户端请求静态文件的时候,减少 Cookie 的反复传输对主域名的影响;
7.为文件头指定Expires,使内容具有缓存性;
8.使用gzip压缩内容;

DOM优化

1、缓存DOM
const div = document.getElementById('div')
由于查询DOM比较耗时,在同一个节点无需多次查询的情况下,可以缓存DOM
2、减少DOM深度及DOM数量
HTML 中标签元素越多,标签的层级越深,浏览器解析DOM并绘制到浏览器中所花的时间就越长,所以应尽可能保持 DOM 元素简洁和层级较少。
3、批量操作DOM
由于DOM操作比较耗时,且可能会造成回流,因此要避免频繁操作DOM,可以批量操作DOM,先用字符串拼接完毕,再用innerHTML更新DOM
4、批量操作CSS样式
通过切换class或者使用元素的style.csstext属性去批量操作元素样式
5、在内存中操作DOM
使用DocumentFragment对象,让DOM操作发生在内存中,而不是页面上
6、DOM元素离线更新
对DOM进行相关操作时,例、appendChild等都可以使用Document Fragment对象进行离线操作,带元素“组装”完成后再一次插入页面,或者使用display:none 对元素隐藏,在元素“消失”后进行相关操作
7、DOM读写分离
浏览器具有惰性渲染机制,连接多次修改DOM可能只触发浏览器的一次渲染。而如果修改DOM后,立即读取DOM。为了保证读取到正确的DOM值,会触发浏览器的一次渲染。因此,修改DOM的操作要与访问DOM分开进行
8、事件代理
事件代理是指将事件监听器注册在父级元素上,由于子元素的事件会通过事件冒泡的方式向上传播到父节点,因此,可以由父节点的监听函数统一处理多个子元素的事件
利用事件代理,可以减少内存使用,提高性能及降低代码复杂度
9、防抖和节流
使用函数节流(throttle)或函数去抖(debounce),限制某一个方法的频繁触发
10、及时清理环境
及时消除对象引用,清除定时器,清除事件监听器,创建最小作用域变量,可以及时回收内存

webpack优化

1.打包公共代码

  • 使用CommonsChunkPlugin插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这会带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件
  • webpack 4 将移除 CommonsChunkPlugin, 取而代之的是两个新的配置项 optimization.splitChunks 和 optimization.runtimeChunk
  • 通过设置 optimization.splitChunks.chunks: "all" 来启动默认的代码分割配置项
    2.动态导入和按需加载
  • webpack提供了两种技术通过模块的内联函数调用来分离代码,优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure
    3.剔除无用代码
  • tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup
    JS的tree主要通过uglifyjs插件来完成,CSS的tree shaking主要通过purify CSS来实现的
    4.长缓存优化
  • 将hash替换为chunkhash,这样当chunk不变时,缓存依然有效
  • 使用Name而不是id
    每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变
    下面来使用两个插件解决这个问题。第一个插件是 NamedModulesPlugin,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。第二个选择是使用 HashedModuleIdsPlugin,推荐用于生产环境构建
    5.公用代码内联
  • 使用html-webpack-inline-chunk-plugin插件将mainfest.js内联到html文件中

针对性的处理

图片加载处理

  • 图片预加载

  • 图片懒加载

  • 图片压缩(UI方面,导出时压缩图片)

  • 雪碧图

  • Base64

  • 使用字体图标来代替图片

  • 预加载的意思就是提前加载内容。而图片的预加载往往会被用在图片资源比较大,加载时会导致很长的等待过程时,才会被使用的。常见场景:图片漫画展示时。往往会预加载一张到两张的图片。

  • 懒加载,这种方式在开发中会被经常使用。首先,我们需要明白一个道理:往往只有看到的资源是必须的,其他资源是可以随着用户的滚动,随即显示的。所以,特别是对于图片资源特别多的网站来说,做好图片的懒加载是可以大大提升网页的载入速度的。

  • 常见的图片懒加载的方式就是:在最初给图片的src设置一个比较简单的图片,然后将图片的真实地址设置给自定义的属性,做一个占位,然后给图片设置监听事件,一旦图片到达视口范围,从图片的自定义属性中获取出真是地址,然后赋值给src,让其进行加载。

  • 雪碧图:CSS雪碧图是以前非常流行的技术,把网站上的一些图片整合到一张单独的图片中,可以减少网站的HTTP请求数量,但是当整合图片比较大时,一次加载比较慢。随着字体图片、SVG图片的流行,该技术渐渐退出了历史舞台

  • Base64:将图片的内容以Base64格式内嵌到HTML中,可以减少HTTP请求数量。但是,由于Base64编码用8位字符表示信息中的6个位,所以编码后大小大约比原始值扩大了 33%

  • 使用字体图标来代替图片

文件合并

如果不进行文件合并,有如下3个隐患
1、文件与文件之间有插入的上行请求,增加了N-1个网络延迟
2、受丢包问题影响更严重
3、经过代理服务器时可能会被断开
但是,文件合并本身也有自己的问题
1、首屏渲染问题
2、缓存失效问题
所以,对于文件合并,有如下改进建议
1、公共库合并
2、不同页面单独合并

移动端优化

1.长列表滚动优化
2.函数防抖和函数节流
3.使用touchstart、touchend代替click
4.HTML的viewport设置
5.开启GPU渲染加速

  • 长列表滚动问题,是移动端需要面对的,IOS尽量使用局部滚动,android尽量使用全局滚动。同时,需要给body添加上-webkit-overflow-scrolling: touch来优化移动段的滚动。

  • 防抖和节流,设计到滚动等会被频繁触发的DOM事件,需要做好防抖和节流的工作。它们都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。

  • 函数防抖:当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间;函数节流:预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。

  • touchstart、touchend代替click,也是移动端比较常用的操作。click在移动端会有300ms延时,这种方法会影响用户的体验。所以做优化时,最简单的方法就是使用touchstart或者touchend代替click。因为它们事件执行顺序是touchstart->touchmove->touchend->click。或者,使用fastclick或者zepto的tap事件代替click事件。

  • HTML的viewport设置,可以防止页面的缩放,来优化性能。

  • 开启GPU渲染加速,小伙伴们一定听过CPU吧,但是这里的GPU不能和CPU混为一谈呦。GPU的全名是Graphics Processing Unit,是一种硬件加速方式。一般的css渲染,浏览器的渲染引擎都不会使用到它。但是,在3D渲染时,计算量较大,繁重,浏览器会开启显卡的硬件加速来帮助完成这些操作。所以,我们这里可以使用css中的translateZ设定,来欺骗浏览器,让其帮忙开启GPU加速,加快渲染进程。

减少重定向

  • 尽量避免使用重定向,当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验

  • 如果一定要使用重定向,如http重定向到https,要使用301永久重定向,而不是302临时重定向。因为,如果使用302,则每一次访问http,都会被重定向到https的页面。而永久重定向,在第一次从http重定向到https之后 ,每次访问http,会直接返回https的页面

使用缓存

使用cach-control或expires这类强缓存时,缓存不过期的情况下,不向服务器发送请求。强缓存过期时,会使用last-modified或etag这类协商缓存,向服务器发送请求,如果资源没有变化,则服务器返回304响应,浏览器继续从本地缓存加载资源;如果资源更新了,则服务器将更新后的资源发送到浏览器,并返回200响应

参考博客:
https://www.jianshu.com/p/fe32ef31deed
https://www.cnblogs.com/huangyin1213/p/5853529.html
https://www.cnblogs.com/xiaohuochai/p/9178390.html

推荐阅读更多精彩内容