среда, 23 июня 2010 г.

Загрузка .js-файлов в html коде – best practicies от Nokia

  • Использование lazy-loading для .js-файлов

При генерации страницы среда последовательно считывает содержимое html-кода. Если в процессе генерации попадается тег <script> с указанием на загрузку скрипта из внешнего источника – парсинг страницы прекращается до момента загрузки и выполнения попавшего скрипта. Тем самым, если скрипты объемные либо соединение нестабильное и медленное, построение страницы может затянутся на длительное время, даже если скрипт используется совсем для других целей. Для избежания проблем с залипанием процесса рендеринга страницы используется механизм, так называемого, динамической загрузки скриптов или иньекции скриптов. Данный механизм позволяет последовательно генерировать страницу без ожидания загрузки скриптов, в том время как сами скрипты автоматически будут выполнены после загрузки, паралельно построению layout-а.

   1: //bad
   2: <div id="splash"/>
   3: <script src="my-script-file.js" type="text/javascript"></script>
   4:  
   5: //good
   6: <div id="splash"/>
   7: // JavaScript
   8: function loadScript(src, callback) {
   9:     var head = document.getElementsByTagName('head')[0],
  10:         script = document.createElement('script');
  11:     done = false;
  12:     script.setAttribute('src', src);
  13:     script.setAttribute('type', 'text/javascript');
  14:     script.setAttribute('charset', 'utf-8');
  15:     script.onload = script.onreadstatechange = function() {
  16:         if (!done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
  17:             done = true;
  18:             script.onload = script.onreadystatechange = null;
  19:                 if (callback) {
  20:                     callback();
  21:                 }
  22:             }
  23:     }
  24:     head.insertBefore(script, head.firstChild);
  25: }
  26:  
  27: // load the my-script-file.js and display an alert dialog once the script has been loaded
  28: loadScript('my-script-file.js', function() { alert('my-script-file.js loaded.'); });


  • Использование http-заголовков Expires для загружаемых скриптов

Данный механизм позволяет не загружать скрипты, если локальная копия еще имеет не просроченное время срока жизни. Тем самым, после первого посещения сайта загруженные и сохраненные в локальном кеше скрипты можно будет повторно использовать без перезагрузки.



  • Сжатие передаваемых скриптов посредстов Gzip-компрессии

  • Объединение нескольких скриптов в один – это позволит уменьшить количество http-запросов к серверу, а, соответственно, и время ожидания загрузки всех скриптов

  • Использовать при возможности несколько мест хранения скриптов с разными адрессами, что позволит распаралелить загрузку

  • Не использовать inline-скрипты – встроенные в страницу, выносите их в отдельных файл. При последующей загрузке страницы будет использована локальная копия скриптового файла, а не загружать регулярно при загрузке страницы

  • Для уменьшения скорости построения страницы старайтесь использовать минимально возможное количество DOM-элементов


   1: //получить количество DOM-элементов на странице
   2: var n = document.getElementsByTagName('*').length;



  • Если есть возможность – изменяйте свойства DOM-объекта в состоянии – не видим.


   1: var subElem = document.createElement('div'),
   2:     elem = document.getElementById('animated');
   3: elem.style.display = 'none';
   4: elem.appendChild(subElem);
   5: elem.style.width = '320px';
   6: elem.style.display = 'block';



  • Не считывайте следующие свойства без необходимости – это приводит к изменению положения элемента и перепроверке положения последующих: getComputedStyle, offsetWidth, scrollWidth и clientWidth


   1: //slow
   2: var elem = document.getElementById('animated');
   3: elem.style.fontSize = (elem.offsetWidth / 10) + 'px';
   4: elem.firstChild.style.marginleft = (elem.offsetWidth / 20) + 'px';
   5:  
   6: //faster
   7: var elem = document.getElementById('animated'),
   8:     elemWidth = elem.offsetWidth;
   9: elem.style.fontSize = (elemWidth / 10) + 'px';
  10: elem.firstChild.style.marginleft = (elemWidth / 20) + 'px';



  • Вместо измнения нескольких стилей элемента последовально, лучше использовать назвачение отдельного css-класса


   1: //slow
   2: var elem = document.getElementById('styled');
   3: elem.style.background = 'blue';
   4: elem.style.color = 'white';
   5:  
   6: //Faster 
   7: <style type="text/css">
   8: div { background: white; color: black; }
   9: div.active { background: blue; color: white; }
  10: </style>
  11: var elem = document.getElementById('styled').className = 'active';



  • Чтобы не создавать отдельный css-класс – можно использовать метод setAttribute для назначения нескольких свойст за раз


   1: var elem = document.getElementById('styled');
   2: elemStyle = 'background: blue; color: white;';
   3: elem.setAttribute('style', elemStyle);



  • сохранять найденные элементы в переменные при необходимости повторного их использования


   1: //slow
   2: document.getElementById('elem').propertyOne = 'value of first property';
   3: document.getElementById('elem').propertyTwo = 'value of second property';
   4: document.getElementById('elem').propertyThree = 'value of third property';
   5:  
   6: //faster
   7: var elem = document.getElementById('elem');
   8: elem.propertyOne = 'value of first property';
   9: elem.propertyTwo = 'value of second property';
  10: elem.propertyThree = 'value of third property'

JavaScript Performance Best Practices от Nokia

Избегать использования функции eval()

   1: //slow
   2: setInterval(1000, "alert('hello')");
   3:  
   4: //faster
   5: setInterval(1000, function() { alert('hello'); });

Избегать использования выражения with



   1: //slow
   2: with (test.object) {    
   3:     foo = 'Value of foo property of object';    
   4:     bar = 'Value of bar property of object';
   5: }
   6:  
   7: //faster
   8: var myObj = test.object;
   9: myObj.foo = 'Value of foo property of object';
  10: myObj.bar = 'Value of bar property of object';


Не использовать try-catch-finnaly в требующих времени функциях



   1: //slow
   2: var object = ['foo', 'bar'], i;
   3: for (i = 0; i < object.length; i++) {   
   4:     try {      
   5:         // do something that throws an exception   
   6:     } 
   7:     catch (e) {      
   8:         // handle exception   
   9:     }
  10: }
  11:  
  12: //faster
  13: var object = ['foo', 'bar'], i;
  14: try {    
  15:     for (i = 0; i < object.length; i++) {        
  16:         // do something    
  17:     }
  18: } 
  19: catch (e) {    
  20:     // handle exception
  21: }


Использовать минимум глобальных переменных



   1: //slow
   2: var i,    str = '';
   3: function globalScope() {    
   4:     for (i=0; i < 100; i++) {        
   5:         str += i; 
   6:     }
   7: }
   8: globalScope();
   9:  
  10: //faster
  11: function localScope() {    
  12:     var i, str = '';    
  13:     for (i=0; i < 100; i++) {        
  14:         str += i; 
  15:     }
  16: }
  17: localScope();


В сложных и долговременных циклах стараться не использовать выражение for-in


Не делать многоразовых арифметических операций над строками одним выражением



   1: //Slow
   2: a += 'x' + 'y'; 
   3:  
   4: //Faster
   5: a += 'x'; a += 'y'; 
   6:  


Бинарные, тернарные, простые операции будут выполняться быстрее чем вызов аналогичных функций



   1: //slow
   2: var min = Math.min(a, b);
   3: arr.push(val);
   4:  
   5: //faster
   6: var min = a < b ? a : b;
   7: arr[arr.length] = val;


Не создавать без надобности переменные, имеющие привязки к DOM-модели



   1: //bad
   2: var car = new Object();
   3: car.color = "red";
   4: car.type = "sedan";
   5:  
   6: //good
   7: var car = { color : "red"; type : "sedan"}


Стараться не вызывать элементы из глобального скопа



   1: //bad
   2: var url = location.href;
   3:  
   4: //good
   5: var url = window.location.href;

Не использовать длинных комментариев и длинных названий переменных



   1: //bad
   2: function someFunction() {
   3:     var user_first_last_name = "first last"; /* stores the full name of user*/ 
   4: }
   5:  
   6: //good
   7: function someFunction() {
   8:     var fullName = "first last";
   9: }

Кешировать переменные, значения которых не меняются



   1: //slow
   2: var m = 10;
   3: for(var i=0; i<250; i++)
   4:     n += Math.tan(m);
   5:  
   6: //falster
   7: var m = 10;
   8: var exp = Math.tan(m);
   9: for(var i=0; i<250; i++)
  10:     n += exp;