• JavaScript 性能优化


    加载和执行

    1、 </body>闭合标签之前,将所有的<script> 标签放在页面底部,确保在脚步执行之前页面已经完成渲染。

    2、 合并脚本。下载单个 100KB 的文件将比下载 4 个 25KB 的文件更快,因此页面标签的<script>标签越少,加载也就越快,响应也越迅速。无论外链文件或者内嵌脚本。

    3、 使用无阻塞下载 javascript 的方法,即在页面加载完成后才加载 Javascript 代码,以下有几种无阻塞下载方法:

    • 使用<script>的 defer 属性(只有 IE4+和 FireFox3.5+支持)
    <script src="file.js" defer></script>
    • 使用动态创建的<script>元素来下载并执行代码

       1 // 通过监听script元素接收完成事件来获得脚本加载完成时的状态
       2 // 封装标准和IE特有的实现方法函数
       3 function loadscript(url, callback) {
       4   let script = document.createElement('script');
       5   if (script.readyState) {
       6     //IE
       7     script.onreadystatechange = function() {
       8       if (
       9         script.readyState === 'loaded' ||
      10         script.readyState === 'complete'
      11       ) {
      12         script.onreadystatechange = null;
      13         callback();
      14       }
      15     };
      16   } else {
      17     // 其他浏览器
      18     scripy.onload = function() {
      19       callback();
      20     };
      21   }
      22   script.src = url;
      23   document.getElementsByTagName('head')[0].appendchild(script);
      24 }
      25 // 使用上述封装函数加载文件
      26 loadscript('file.js', function() {
      27   console.log('file is loaded ~');
      28 });
    • 使用 XHR 对象下载 javascript代码并注入页面中

      let xhr = new XMLHttpRequest();
      xhr.open('get', 'file.js', true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
            let script = document.createElement('script');
            script.text = xhr.responseText;
            document.body.appendChild(script);
          }
        }
      };

    总结:

    向页面中添加大量 JavaScript 的推荐做法:先添加动态加载所需代码,然后加载初始化页面所需剩下代码

    数据存储

    1、访问字面量和局部变量的速度比访问数组元素和对象成员快,因此尽量使用字面量和局部变量,减少数组项和对象成员的使用。

    2、尽可能使用局部变量。如果某个跨作用域的值在函数中被引用一次以上,把它存储到局部变量中。

     1 // 不推荐
     2 function initFn() {
     3   // document.getElementById('myImg')多次被引用
     4   document.getElementById('myImg').src = 'photo.png';
     5   document.getElementById('myImg').alt = 'photo';
     6 }
     7 
     8 // 推荐
     9 function initFn() {
    10   // 改写
    11   let myImg = document.getElementById('myImg');
    12   myImg.src = 'photo.png';
    13   myImg.alt = 'photo';
    14 }

    3、避免使用 with 语句,它会改变执行环境作用域链。try-catch 中的 catch 也有同样的影响,看实际情况中进行运用。

    4、属性和方法在原型链中的位置越深,访问的速度越慢

    总结:

    把常用的对象成员、数组元素、跨域变量保存在局部变量中

    DOM

    1、最小化 DOM 访问次数,如果需要多次访问某个 DOM 节点,使用局部变量存储它的引用。

     1 // 不推荐
     2 function innerHTMLLoop(){
     3   for(let i = 0; i < 5; i++){
     4     document.getElementById('myDiv').innerHTML += 'a';
     5   }
     6 }
     7 
     8 // 推荐
     9 function innerHTMLLoop(){
    10   let content = '';
    11   for(let i = 0; i < 5; i++){
    12     content += 'a';
    13   }
    14   document.getElementById('myDiv').innerHTML = content;
    15 }

    2、如果需要在迭代中使用 HTML 集合,把它的长度缓存到一个变量中;如果需要多次操作集合,把它拷贝到一个数组中。

    • HTML 集合有document.getElementsByName(),document.getElementsClassName(),document.getElementsByTagName(),document.images,document.links,document.forms等等,返回的是类似数组的列表,但是它以一种“假定实时态”实时存在,即当底层文档对象更新时,它也会自动更新。

      // 死循环
      let divs = document.getElementsByTagName('div');
      for (let i = 0; i < divs.length; i++) {
        document.body.appendChild(document.createElement('div'));
      }
    • 当遍历一个集合时,把集合存储在局部变量中,并把 length 缓存在循环外部

       1 // 推荐
       2 function collectNodes() {
       3   let coll = document.getElementsByTagName('div');
       4   let len = coll.length;
       5   let nodeName = '';
       6   let tagName = '';
       7   let el = null;
       8   for (let i = 0; i < len; i++) {
       9     el = coll[i];
      10     nodeName = el.nodeName;
      11     tagName = el.tagName;
      12   }
      13   return { nodeName, tagName };
      14 }

    3、减少重绘和重排,批量修改样式时,“离线”操作 DOM 树,使用缓存,并减少访问布局信息的次数。

    • 当页面布局和几何属性改变时需要“重排”。下列情况均会发生“重排”:

      1、添加或删除可见的 DOM 元素

      2、元素位置发生改变

      3、元素尺寸改变(包括:外边距、内边距、边框厚度、宽度、高度等属性改变)

      4、内容改变,如文本改变或图片被另一个不同尺寸的图片代替

      5、页面渲染器初始化

      6、浏览器窗口改变

    • 完成“重排”后,浏览器会重新绘制受影响的部分到屏幕中,过程为“重绘”。

    • 减少“重绘”和“重排”

      1、使元素脱离文档流

      2、对其应用多次改变

      3、把元素带回文档中

       1 // 第一种方式:隐藏元素,应用修改,重新显示
       2 let ul = document.getElementById('nyList');
       3 ul.style.display = 'none';
       4 // 向ul中添加附加数据
       5 appendDataToElement(ul, data);
       6 ul.style.display = 'block';
       7 
       8 // 第二种方式:使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝到文档(推荐)
       9 let fragment = document.createDocumentFragment();
      10 // 向fragment中添加附加数据
      11 appendDataToElement(ul, data);
      12 document.getElementById('myList').appendChild(fragment);
      13 
      14 // 第三种方式:将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素
      15 let old = document.getElementById('myList');
      16 let clone = old.cloneNode(true);
      17 // 向clone中添加附加数据
      18 appendDataToElement(ul, data);
      19 old.parentNode.replaceChild(clone, old);

    4、使用事件委托减少事件处理器的数量

    1 // Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement。
    2 myLi.onclick = function(e) {
    3   let e = e || window.event;
    4   let target = ev.target || ev.srcElement;
    5   if (target.nodeName.toLowerCase() == 'li') {
    6     alert(target.innerHTML);
    7   }
    8 };

    算法和流程控制

    • 选择时间复杂度低的算法
    • 避免使用 for-in 循环,除非需要遍历一个属性数量未知的对象
    • 使用 if-else 要确保最可能出现的条件放在首位,条件数量大时,优先使用 switch
    • 使用查找表。查找表相对于 if-else 和 switch,不近速度快,而且可读性好
      1 // 将返回值集合存入数组
      2 let results = [result0,result1,result2,result3,result4,result5,result6,result7,result8,result9,result10,];
      3 // 返回当前结果
      4 return results[value];

    构建和部署

    • 合并 JavaScript 文件以减少 HTTP 请求数

    • 使用 webpack 压缩 JavaScript 文件

    • 使用 CDN 提供 JavaScript 文件;CDN 不仅可以提升性能,也会为你管理文件的压缩和缓存

    参考:图灵图书《高性能JavaScript》

  • 相关阅读:
    Mat
    分治法-最近点对问题
    动态规划作业-最长公共子序列问题
    动态规划作业-多段图的最短路径问题
    OpenCV+VisualStudion2017配置
    R入门(二)-对象以及它们的模式和属性
    Big number
    R入门(一)
    Spring-aop
    Spring-ioc
  • 原文地址:https://www.cnblogs.com/wxw1314/p/8637022.html
Copyright © 2020-2023  润新知