本文主要是在我读《高性能Javascript》以后,想要记录下一些有用的优化方案,而且就我自己的一些经验,来你们一块儿分享下,javascript
你们都知道,浏览器在解析DOM树的时候,当解析到script标签的时候,会阻塞其余的全部任务,直到该js文件下载、解析执行完成后,才会继续往下执行。所以,这个时候浏览器就会被阻塞在这里,若是将script标签放在head里的话,那么在该js文件加载执行前,用户只能看到空白的页面,这样的用户体验确定是特别烂。对此,经常使用的方法有如下:css
<script src="test.js" type="text/javascript" defer></script>
1.动态的插入script标签来加载脚本,好比经过如下代码vue
function loadScript(url, callback) { const script = document.createElement('script'); script.type = 'text/javascript'; // 处理IE if (script.readyState) { script.onreadystatechange = function () { if (script.readyState === 'loaded' || script.readyState === 'complete') { script.onreadystatechange = null; callback(); } } } else { // 处理其余浏览器的状况 script.onload = function () { callback(); } } script.src = url; document.body.append(script); } // 动态加载js loadScript('file.js', function () { console.log('加载完成'); })
2.经过xhr方式加载js文件,不过经过这种方式的话,就可能会面临着跨域的问题。例子以下:java
const xhr = new XMLHttpRequest(); xhr.open('get', 'file.js'); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304>) { const script = document.createElement('script'); script.type = 'text/javascript'; script.text = xhr.responseText; document.body.append(script); } } }
3.将多个js文件合并为同一个,而且进行压缩。 缘由:目前浏览器大多已经支持并行下载js文件了,可是并发下载仍是有必定的数量限制了(基于浏览器,一部分浏览器只能下载4个),而且,每个js文件都须要创建一次额外的http链接,加载4个25KB的文件比起加载一个100KB的文件消耗的时间要大。所以,咱们最好就是将多个js文件合并为同一个,而且进行代码压缩。node
当一个函数执行的时候,会生成一个执行上下文,这个执行上下文定义了函数执行时的环境。当函数执行完毕后,这个执行上下文就会被销毁。所以,屡次调用同一个函数会致使建立多个执行上下文。每隔执行上下文都有本身的做用域链。相信你们应该早就知道了做用域这个东西,对于一个函数而言,其第一个做用域就是它函数内部的变量。在函数执行过程当中,每遇到一个变量,都会搜索函数的做用域链找到第一个匹配的变量,首先查找函数内部的变量,以后再沿着做用域链逐层寻找。所以,若咱们要访问最外层的变量(全局变量),则相比直接访问内部的变量而言,会带来比较大的性能损耗。所以,咱们能够将常用的全局变量引用储存在一个局部变量里。react
const a = 5; function outter () { const a = 2; function inner () { const b = 2; console.log(b); // 2 console.log(a); // 2 } inner(); }
javascript中,主要分为字面量、局部变量、数组元素和对象这四种。访问字面量和局部变量的速度最快,而访问数组元素和对象成员相对较慢。而访问对象成员的时候,就和做用域链同样,是在原型链(prototype)上进行查找。所以,若查找的成员在原型链位置太深,则访问速度越慢。所以,咱们应该尽量的减小对象成员的查找次数和嵌套深度。好比如下代码webpack
// 进行两次对象成员查找 function hasEitherClass(element, className1, className2) { return element.className === className1 || element.className === className2; } // 优化,若是该变量不会改变,则可使用局部变量保存查找的内容 function hasEitherClass(element, className1, className2) { const currentClassName = element.className; return currentClassName === className1 || currentClassName === className2; }
// 优化前,在每次循环的时候,都要获取id为t的节点,而且设置它的innerHTML function innerHTMLLoop () { for (let count = 0; count < 15000; count++) { document.getElementById('t').innerHTML += 'a'; } } // 优化后, function innerHTMLLoop () { const tNode = document.getElemenById('t'); const insertHtml = ''; for (let count = 0; count < 15000; count++) { insertHtml += 'a'; } tNode.innerHtml += insertHtml; }
1.当咱们要对Dom的样式进行修改的时候,咱们应该尽量的合并全部的修改而且一次处理,减小重排和重汇的次数。git
// 优化前 const el = document.getElementById('test'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px'; // 优化后,一次性修改样式,这样能够将三次重排减小到一次重排 const el = document.getElementById('test'); el.style.cssText += '; border-left: 1px ;border-right: 2px; padding: 5px;'
2.当咱们要批量修改DOM节点的时候,咱们能够将DOM节点隐藏掉,而后进行一系列的修改操做,以后再将其设置为可见,这样就能够最多只进行两次重排。具体的方法以下:github
// 未优化前 const ele = document.getElementById('test'); // 一系列dom修改操做 // 优化方案一,将要修改的节点设置为不显示,以后对它进行修改,修改完成后再显示该节点,从而只须要两次重排 const ele = document.getElementById('test'); ele.style.display = 'none'; // 一系列dom修改操做 ele.style.display = 'block'; // 优化方案二,首先建立一个文档片断(documentFragment),而后对该片断进行修改,以后将文档片断插入到文档中,只有最后将文档片断插入文档的时候会引发重排,所以只会触发一次重排。。 const fragment = document.createDocumentFragment(); const ele = document.getElementById('test'); // 一系列dom修改操做 ele.appendChild(fragment);
3.使用事件委托:事件委托就是将目标节点的事件移到父节点来处理,因为浏览器冒泡的特色,当目标节点触发了该事件的时候,父节点也会触发该事件。所以,由父节点来负责监听和处理该事件。那么,它的优势在哪里呢?假设你有一个列表,里面每个列表项都须要绑定相同的事件,而这个列表可能会频繁的插入和删除。若是按照日常的方法,你只能给每个列表项都绑定一个事件处理器,而且,每当插入新的列表项的时候,你也须要为新的列表项注册新的事件处理器。这样的话,若是列表项很大的话,就会致使有特别多的事件处理器,形成极大的性能问题。而经过事件委托,咱们只须要在列表项的父节点监听这个事件,由它来统一处理就能够了。这样,对于新增的列表项也不须要作额外的处理。并且事件委托的用法其实也很简单:web
function handleClick(target) { // 点击列表项的处理事件 } function delegate (e) { // 判断目标对象是否为列表项 if (e.target.nodeName === 'LI') { handleClick(e.target); } } const parent = document.getElementById('parent'); parent.addEventListener('click', delegate);
本文地址在->本人博客地址, 欢迎给个 start 或 follow