前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端性能优化

前端性能优化

作者头像
娜姐
发布2020-09-22 10:19:33
9010
发布2020-09-22 10:19:33
举报
文章被收录于专栏:娜姐聊前端

之前写过一篇文章前端网络高级篇(六)网站性能优化,里面提到过13个性能优化的点:

  • 减少HTTP请求
  • 使用CDN
  • 利用HTTP缓存
  • Gzip压缩
  • 将样式表放在顶部
  • 将JS脚本放在底部
  • 避免CSS表达式
  • 使用外部JS和CSS
  • 减少DNS查找
  • 压缩JavaScript和CSS
  • 少用iframe
  • JS文件异步/按需加载
  • 图片懒加载

在具体编程方面,再补充几个点。

1. DOM编程优化

用JS操作DOM,是比较慢的。为什么呢?首先,补充一下浏览器相关的知识。下图为浏览器结构:

image

我们只要关心渲染引擎(Rendering engine)和JS引擎(JavaScript Interpreter)也可称为JS解析器。如图所示,当用JS引擎和渲染引擎是独立实现的,两者通过桥接接口通信。而DOM由渲染引擎绘制,所以,当JS改变DOM结构时,必须通过Bridge通知给渲染引擎,然后进行重排或者重绘。这个通信是有开销的。

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

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式。

重排的开销要远大于重绘

所以,我们的优化点就是:

  1. 尽可能减少DOM操作
  2. 尽可能减少重排

看看下面的例子吧。

例子1: 在container元素里面添加10000个“hello”。

不好的行为(JS多次读取DOM元素):

代码语言:javascript
复制
for(var count=0;count<10000;count++){ 
  document.getElementById('container').innerHTML+='<span>hello</span>';
}

改造(JS只读取一次DOM元素,但是,依旧多次更改DOM元素):

代码语言:javascript
复制
// 只获取一次container
let container = document.getElementById('container');
for(let count=0;count<10000;count++){ 
  container.innerHTML += '<span>hello</span>';
}

再改造(JS只读取一次DOM元素,只操作DOM元素):

代码语言:javascript
复制
let container = document.getElementById('container');
let content = '';
for(let count=0;count<10000;count++){ 
  // 拼接内容
  content += '<span>hello</span>';
} 
// 最后更改DOM
container.innerHTML = content;

其实,JS里面用+号拼接String开销也略大,一般会建议创建数组,然后通过array.join('')将数组转为String,如:

代码语言:javascript
复制
let array = [];
for(let count=0;count<10000;count++){ 
  // 拼接内容
  array.push('<span>hello</span>');
} 
container.innerHTML = array.join('');

不过,DOM提供了更好的内置容器来帮助做内容拼接 - DOM Fragment。最后,用 DOM Fragment 改写总结版:

代码语言:javascript
复制
let container = document.getElementById('container');
// 创建一个DOM Fragment对象作为容器
let content = document.createDocumentFragment();
for(let count=0;count<10000;count++){
  // 通过DOM API创建span
  let spanElt = document.createElement("span");
  spanElt.innerHTML = 'hello';
  // 像操作真实DOM一样操作DOM Fragment对象
  content.appendChild(spanElt);
}
// 最后更改DOM
container.appendChild(content)
例子2:更改DOM元素样式

不好的行为(逐条更改样式):

代码语言:javascript
复制
const container = document.getElementById('container')
container.style.width = '100px';
container.style.height = '200px';
container.style.color = 'red';

改造(利用class,只改动一次样式):

代码语言:javascript
复制
//style.css
.basic_style {
   width: 100px;
   height: 200px;
   color: red;
}
//app.js
const container = document.getElementById('container');
container.classList.add('basic_style');

当DOM离线时(display: none),无论怎么操作,浏览器都不会绘制它,也就不会引发重排或者重绘。所以,利用这个特性再改造一版:

代码语言:javascript
复制
let container = document.getElementById('container');
container.style.display = 'none';
container.style.width = '100px';
container.style.height = '200px';
container.style.color = 'red';

container.style.display = 'block'

最后提醒一下,下面的属性慎用。因为这些属性都需要实时计算得到,所以,浏览器为了取得正确的值,会进行重排

offsetTop、offsetLeft、 offsetWidth、offsetHeight、 scrollTop、scrollLeft、scrollWidth、scrollHeight、 clientTop、clientLeft、clientWidth、clientHeight

3. 事件节流(throttle)和防抖(debounce)

比如窗口的scroll和resize事件,一旦激活,会频繁触发相应的事件函数。频繁触发回掉函数导致的大量计算有可能引发页面抖动甚至卡顿。为了规避这些风险,我们会采用事件节流或者防抖,来降低函数的触发频率。

节流:当事件第一次被触发时,在指定时间内,无论再次触发多少次,都会被忽略。也就是说,以第一次事件为准。 防抖:事件触发后,会延迟执行,在延迟时间内,如果事件再次被触发,上一次的事件被取消,以当次为准,重新延迟执行。也就是说,以最后一次事件为准。

示例代码如下:

代码语言:javascript
复制
// 节流 1
let canRun = true;
$(window).scroll(() => {
   if(!canRun){
       // 判断是否已空闲,如果在执行中,则直接return
        return;
   } 
   canRun = false;
    setTimeout(() => {
        canRun = true;
    }, 300);
}); 

// 节流 2
let interval = 300;
let last = 0;
$(window).scroll(() => {
   let now = +new Date()
   if (now - last >= interval) {
      // 如果时间间隔大于设定的时间间隔阈值,则执行回调
      last = now;
      ....
    }
}); 

// 防抖
let timer;
$(window).scroll(() => {
  if(timer){
    clearTimeout(timer)
  }
  timer = setTimeout(() => {
    // 延时 200ms,处理滚动逻辑
  }, 200)
})

一般在浏览器scroll和resize事件应用节流,在远程搜索场景下,应用防抖。

4. CSS优化

CSS选择器是从右向左解析的,所以,尽可能直接用class作为选择器,减少查询时间。

代码语言:javascript
复制
// 推荐
.top {...}
// 不推荐
// 浏览器会先查找所有的a标签,然后再找这些a标签中哪些有span父标签...
div span a {...}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. DOM编程优化
    • 例子1: 在container元素里面添加10000个“hello”。
      • 例子2:更改DOM元素样式
      • 3. 事件节流(throttle)和防抖(debounce)
        • 4. CSS优化
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档