zhaobinglong / myBlog

https://zhaobinglong.github.io/myBlog/
MIT License
7 stars 0 forks source link

JavaScript性能优化 #108

Open zhaobinglong opened 3 years ago

zhaobinglong commented 3 years ago

前端新能优化总纲领:#63

最小化语句数相关

JS代码中的语句数量也会影响所执行的操作的速度。完成多个操作的单个语句要比完成单个操作的多个语句块快。这一条依赖参与人员的自觉性,实施难度大。

// bad
test = () => {
    var a = 1
    var b = 2
    var c = 3
    return a + b + c
}

// good
test = () => {
    var a = 1,
    b = 2,
    c = 3
    return a + b + c
}

用key-value代理循环

zhaobinglong commented 3 years ago

事件委托减少循环绑定的事件

什么是事件委托:通俗的讲,事件就是onclick,onmouseover,onmouseout,等就是事件,委托呢,就是让别人来做,这个事件本来是加在某些元素上的,然而你却加到别人身上来做,完成这个事件。

原理:利用冒泡的原理,把事件加到父级上,触发执行效果。

优点

1.提高性能。 2.新添加的元素还会有之前的事件。 试想一下,一个页面上ul的每一个li标签添加一个事件,我们会不会给每一个标签都添加一个onclick呢。 当页面中存在大量元素都需要绑定同一个事件处理的时候,这种情况可能会影响性能,不仅消耗了内存,还多循环时间。每绑定一个事件都加重了页面或者是运行期间的负担。对于一个富前端的应用,交互重的页面上,过多的绑定会占用过多内存。 一个简单优雅的方式就是事件委托。它是基于事件的工作流:逐层捕获,到达目标,逐层冒泡。既然事件存在冒泡机制,那么我们可以通过给外层绑定事件,来处理所有的子元素出发的事件。

demo


<ul id="ul">
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>

// 一个事件委托的简单实现:
document.getElementById('ulId').onclick = function(e) {
    var e = e || window.event;
    var target = e.target || e.srcElement; //兼容旧版本IE和现代浏览器
    if (target.nodeName.toLowerCase() !== 'ul') {
        return;
    }
    console.log(target.innerHTML);
}
zhaobinglong commented 3 years ago

work子线程处理任务

浏览器是单线程运行环境,渲染的时候不能运行js,运行js的时候就不能渲染,所以我们把js耗时操作放入work中,让渲染和业务逻辑分开

常见场景

主线程和work通信

// 主线程新建一个work子线程,work.js必须是一个网络文件
var worker = new Worker('work.js');

worker.postMessage('Hello World');  // 主线程给子线程发送消息
worker.postMessage({method: 'echo', args: ['Work']}); // // 主线程调用子线程的方法,同时传参

// 主线程监听子线程发送过来的消息
worker.onmessage = function (event) {
  console.log('Received message ' + event.data);
  doSomething();
}

// 主线程关闭子线程
worker.terminate();

work和主线程通信

// 子线程监听主线程发送过来的数据,同时给主线程发送信息
addEventListener('message', function (e) {
  postMessage('You said: ' + e.data);
}, false);

参考

http://www.ruanyifeng.com/blog/2018/07/web-worker.html

zhaobinglong commented 3 years ago

长列表优化(createDocementFragment)

js新建节点的时候,每次JavaScript对DOM的操作都会改变当前页面的呈现,并重新刷新整个页面,从而消耗了大量的时间。而createDocumentFragment()的作用,就是可以创建一个文档碎片,把所有的新节点附加其上,然后把文档碎片的内容一次性添加到document中。

demo对比

// 向页面中添加1000个节点
 for (var i = 0; i < 1000; i++)
  {
    var el = document.createElement('p');
    el.innerHTML = i;
    document.body.appendChild(el); //直接用appendChild向文档中插入节点
  }

var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++)
{
  var el = document.createElement('p');
  el.innerHTML = i; 
  frag.appendChild(el); //首先将新节点先添加到DocumentFragment 节点
}
document.body.appendChild(frag);//然后用appendChild插入文档中
zhaobinglong commented 3 years ago

渲染优化(requestAnimationFrame)

如果我们一次性渲染刷新几万条数据,页面会卡顿,因此只能分批渲染,既然知道原理我们就可以使用setInterval和setTimeout、requestAnimationFrame来实现定时分批渲染,实现每16 ms 刷新一次

requestAnimationFrame跟setTimeout/setInterval差不多,通过递归调用同一方法来不断更新画面以达到动起来的效果,但它优于setTimeout/setInterval的地方在于它是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销。

可以将它看做一个钩子,刚好卡在浏览器重绘前向我们的操作伸出橄榄枝。实际上它更像定时器,每秒60次执行回调——符合屏幕的刷新频率,遇到耗时长的操作,这个数字会降到30来保证稳定的帧数。语法也很简单:window.requestAnimationFrame(callback)

demo

function refresh(total, onceCount) {
  //total -> 渲染数据总数 onceCount -> 一次渲染条数
  let count = 0, //初始渲染次数值
    loopCount = total / onceCount //渲染次数
  function refreshAnimation() {
    /*
    * 在此处渲染数据
    */
    if (count < loopCount) {
      count++
      requestAnimationFrame(refreshAnimation)
    }
  }
  requestAnimationFrame(refreshAnimation)
}
zhaobinglong commented 3 years ago

原生js懒加载

var io = new IntersectionObserver(callback, option);

// 开始观察
io.observe(document.getElementById('example'));

// 停止观察
io.unobserve(element);

// 关闭观察器
io.disconnect();

实现长列表封面图览加载

参考

http://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

zhaobinglong commented 3 years ago

防抖和截流

https://github.com/zhaobinglong/myBlog/issues/23#issuecomment-690026419

zhaobinglong commented 3 years ago

网站应用的细节优化

https://joelcalifa.com/blog/tiny-wins/

zhaobinglong commented 3 years ago

模块延迟加载

https://humanwhocodes.com/blog/2021/04/lazy-loading-property-pattern-javascript/