摘要:當函數執行完畢后,這個執行上下文就會被銷毀。訪問字面量和局部變量的速度最快,而訪問數組元素和對象成員相對較慢。因此,由父節點來負責監聽和處理該事件。
本文主要是在我讀《高性能Javascript》之后,想要記錄下一些有用的優化方案,并且就我本身的一些經驗,來大家一起分享下,
Javascript的加載與執行大家都知道,瀏覽器在解析DOM樹的時候,當解析到script標簽的時候,會阻塞其他的所有任務,直到該js文件下載、解析執行完成后,才會繼續往下執行。因此,這個時候瀏覽器就會被阻塞在這里,如果將script標簽放在head里的話,那么在該js文件加載執行前,用戶只能看到空白的頁面,這樣的用戶體驗肯定是特別爛。對此,常用的方法有以下:
將所有的script標簽都放到body最底部,這樣可以保證js文件是最后加載并執行的,可以先將頁面展現給用戶。但是,你首先得清楚,頁面的首屏渲染是否依賴于你的部分js文件,如果是的話,則需要將這一部分js文件放到head上。
使用defer,比如下面這種寫法。使用defer這種寫法時,雖然瀏覽器解析到該標簽的時候,也會下載對應的js文件,不過它并不會馬上執行,而是會等到DOM解析完后(DomContentLoader之前)才會執行這些js文件。因此,就不會阻塞到瀏覽器。
動態加載js文件,通過這種方式,可以在頁面加載完成后,再去加載所需要的代碼,也可以通過這種方式實現js文件懶加載/按需加載,比如現在比較常見的,就是webpack結合vue-router/react-router實現按需加載,只有訪問到具體路由的時候,才加載相應的代碼。具體的方法如下:
1.動態的插入script標簽來加載腳本,比如通過以下代碼
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文件,不過通過這種方式的話,就可能會面臨著跨域的問題。例子如下:
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文件合并為同一個,并且進行代碼壓縮。
javascript作用域當一個函數執行的時候,會生成一個執行上下文,這個執行上下文定義了函數執行時的環境。當函數執行完畢后,這個執行上下文就會被銷毀。因此,多次調用同一個函數會導致創建多個執行上下文。每隔執行上下文都有自己的作用域鏈。相信大家應該早就知道了作用域這個東西,對于一個函數而言,其第一個作用域就是它函數內部的變量。在函數執行過程中,每遇到一個變量,都會搜索函數的作用域鏈找到第一個匹配的變量,首先查找函數內部的變量,之后再沿著作用域鏈逐層尋找。因此,若我們要訪問最外層的變量(全局變量),則相比直接訪問內部的變量而言,會帶來比較大的性能損耗。因此,我們可以將經常使用的全局變量引用儲存在一個局部變量里。
const a = 5; function outter () { const a = 2; function inner () { const b = 2; console.log(b); // 2 console.log(a); // 2 } inner(); }對象的讀取
javascript中,主要分為字面量、局部變量、數組元素和對象這四種。訪問字面量和局部變量的速度最快,而訪問數組元素和對象成員相對較慢。而訪問對象成員的時候,就和作用域鏈一樣,是在原型鏈(prototype)上進行查找。因此,若查找的成員在原型鏈位置太深,則訪問速度越慢。因此,我們應該盡可能的減少對象成員的查找次數和嵌套深度。比如以下代碼
// 進行兩次對象成員查找 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; }DOM操作優化
最小化DOM的操作次數,盡可能的用javascript來處理,并且盡可能的使用局部變量儲存DOM節點。比如以下的代碼:
// 優化前,在每次循環的時候,都要獲取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的樣式進行修改的時候,我們應該盡可能的合并所有的修改并且一次處理,減少重排和重匯的次數。
// 優化前 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節點隱藏掉,然后進行一系列的修改操作,之后再將其設置為可見,這樣就可以最多只進行兩次重排。具體的方法如下:
// 未優化前 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.使用事件委托:事件委托就是將目標節點的事件移到父節點來處理,由于瀏覽器冒泡的特點,當目標節點觸發了該事件的時候,父節點也會觸發該事件。因此,由父節點來負責監聽和處理該事件。
那么,它的優點在哪里呢?假設你有一個列表,里面每一個列表項都需要綁定相同的事件,而這個列表可能會頻繁的插入和刪除。如果按照平常的方法,你只能給每一個列表項都綁定一個事件處理器,并且,每當插入新的列表項的時候,你也需要為新的列表項注冊新的事件處理器。這樣的話,如果列表項很大的話,就會導致有特別多的事件處理器,造成極大的性能問題。而通過事件委托,我們只需要在列表項的父節點監聽這個事件,由它來統一處理就可以了。這樣,對于新增的列表項也不需要做額外的處理。而且事件委托的用法其實也很簡單:
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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108646.html
摘要:本文是圖說系列文章的第五篇。這樣的話,使用的開發者也不需要做任何適配,但是它們卻能獲得更高性能。該圖并不是用來準確的衡量其性能的。運行編寫出高性能的代碼是可能的。這種清理工作由引擎自動進行,稱為垃圾回收。 本文是圖說 WebAssembly 系列文章的第五篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 在上一篇文章中,我們說到了使用 WebAssembly 和 JavaScript...
摘要:下面我們撇開網絡方面的優化,只分析靜態資源方面的優化。不過,也會阻止的構建和延緩網頁渲染。未優化正常加載優化后異步加載根據上面的分析,我們可以清楚的認識到,非必要優先加載的,選擇異步加載是最優選擇。 為什么做優化 經典問題:白屏時間過長,用戶體驗差產生的原因:網絡問題、關鍵渲染路徑(CRP)問題 怎么做優化 如何做好優化呢,網上隨便一搜,就有很多優化總結,無非就是網絡優化、靜態資源(h...
摘要:下面我們撇開網絡方面的優化,只分析靜態資源方面的優化。不過,也會阻止的構建和延緩網頁渲染。未優化正常加載優化后異步加載根據上面的分析,我們可以清楚的認識到,非必要優先加載的,選擇異步加載是最優選擇。 為什么做優化 經典問題:白屏時間過長,用戶體驗差產生的原因:網絡問題、關鍵渲染路徑(CRP)問題 怎么做優化 如何做好優化呢,網上隨便一搜,就有很多優化總結,無非就是網絡優化、靜態資源(h...
摘要:下面我們撇開網絡方面的優化,只分析靜態資源方面的優化。不過,也會阻止的構建和延緩網頁渲染。未優化正常加載優化后異步加載根據上面的分析,我們可以清楚的認識到,非必要優先加載的,選擇異步加載是最優選擇。 為什么做優化 經典問題:白屏時間過長,用戶體驗差產生的原因:網絡問題、關鍵渲染路徑(CRP)問題 怎么做優化 如何做好優化呢,網上隨便一搜,就有很多優化總結,無非就是網絡優化、靜態資源(h...
摘要:插件性能優化及個人常用優化方法經常會觸發視覺變化。作用域鏈指的是當前作用于下可用變量的集合,它在各種主流瀏覽器中至少包含兩個部分局部變量的集合和全局變量的集合。在考慮優化時,數值和變量的性能差不多,并且速度顯著優于對象屬性和數組元素。 JavaScript 插件性能優化及個人react常用優化方法 JavaScript 經常會觸發視覺變化。有時是直接通過樣式操作,有時是會產生視覺變化...
閱讀 3189·2021-11-24 10:30
閱讀 1312·2021-09-30 09:56
閱讀 2384·2021-09-07 10:20
閱讀 2596·2021-08-27 13:10
閱讀 698·2019-08-30 11:11
閱讀 2050·2019-08-29 12:13
閱讀 758·2019-08-26 12:24
閱讀 2897·2019-08-26 12:20