前端是龐大的,包括 HTML、 CSS、 Javascript、Image 、Flash等等各種各樣的資源。前端優化是複雜的,針對各個面向的資源都有不同的方式。那麼,前端優化的目的是什麼 ?
1. 從使用者角度而言,優化能讓頁面載入得更快、對使用者的操作回應得更及時,能夠提供使用者更友善的體驗。
2. 從服務商角度而言,最佳化能夠減少頁面請求數、或減少請求所佔頻寬,能夠節省可觀的資源。
總之,恰當的最佳化不僅能夠改善網站的使用者體驗並且能夠節省相當的資源利用。
前端優化的途徑有很多,按粒度大致可以分為兩類,第一類是頁面級別的優化,例如HTTP請求數、腳本的無阻塞加載、內聯腳本的位置優化等;第二類則是程式碼層級的最佳化,例如Javascript中的DOM 操作優化、CSS選擇符優化、圖片優化以及HTML結構優化等等。另外,本著提高投入產出比的目的,後文提到的各種最佳化策略大致依照投入產出比從大到小的順序排列。
一、頁面級優化
1. 減少 HTTP請求數
這條策略基本上所有前端人都知道,而且也是最重要最有效的。都說要減少 HTTP請求,那請求多了到底會怎麼樣呢 ?首先,每個請求都是有成本的,既包含時間成本也包含資源成本。一個完整的請求都需要經過 DNS尋址、與伺服器建立連線、發送資料、等待伺服器回應、接收資料這樣一個 「漫長」 而複雜的過程。時間成本就是使用者需要看到或 「感受」 到這個資源是必須要等待這個過程結束的,資源上由於每個請求都需要攜帶數據,因此每個請求都需要佔用頻寬。另外,由於瀏覽器進行並發請求的請求數是有上限的(具體參見此處),因此請求數多了以後,瀏覽器需要分批進行請求,因此會增加用戶的等待時間,會給用戶造成站點速度慢這樣一個印象,即使可能使用者能看到的第一螢幕的資源都已經被要求完了,但是瀏覽器的進度條會一直存在。
減少 HTTP請求數的主要途徑包括:
(1). 從設計實現層面簡化頁面
如果你的頁面像百度首頁一樣簡單,那麼接下來的規則基本上都用不著了。保持頁面簡潔、減少資源的使用時最直接的。如果不是這樣,你的頁面需要華麗的皮膚,則繼續閱讀下面的內容。
(2). 合理設定 HTTP快取
快取的力量是強大的,恰當的快取設定可以大大的減少 HTTP請求。以有啊首頁為例,當瀏覽器沒有快取的時候訪問一共會發出78個請求,共600多K數據(如圖1.1),而當第二次訪問即瀏覽器已緩存之後訪問則僅有10個請求,共20多K資料(如圖1.2)。 (這裡要說明的是,如果直接F5刷新頁面的話效果是不一樣的,這種情況下請求數還是一樣,不過被緩存資源的請求伺服器是304響應,只有Header沒有Body ,可以節省頻寬)
怎麼才算合理設定?原則很簡單,能緩存越多越好,能緩存越久越好。例如,很少變化的圖片資源可以直接透過 HTTP Header中的Expires設定一個很長的過期頭 ;變化不頻繁而又可能會變的資源可以使用 Last-Modifed來做請求驗證。盡可能的讓資源能夠在快取中待得更久。關於HTTP快取的具體設定和原理此處就不再詳述了,有興趣的可以參考下列文章:
HTTP1.1協定中關於快取策略的描述
Fiddler HTTP Performance中關於快取的介紹
(3). 資源合併與壓縮
如果可以的話,盡可能的將外部的腳本、樣式進行合併,多個合為一個。另外, CSS、 Javascript、Image 都可以用對應的工具來壓縮,壓縮後往往能省下不少空間。
(4). CSS Sprites
合併 CSS圖片,減少請求數的另一個好方法。
(5). Inline Images
使用 data:
URL scheme的方式將圖片嵌入到頁面或 CSS中,如果不考慮資源管理上的問題的話,不失為一個好方法。如果是嵌入頁面的話換來的是增加了頁面的體積,而且無法利用瀏覽器快取。使用在 CSS中的圖片則更為理想一些。
(6). Lazy Load Images(自己對這一塊的內容還是不了解)
這條策略其實不一定能減少 HTTP請求數,但是卻能在某些條件下或頁面剛載入時減少 HTTP請求數。對於圖片而言,在頁面剛加載的時候可以只加載第一屏,當用戶繼續往後滾屏的時候才加載後續的圖片。這樣一來,如果用戶只對第一屏的內容感興趣時,那剩餘的圖片請求就都節省了。
有啊首頁
曾經的做法是在載入的時候把第一屏之後的圖片地址緩存在 Textarea標籤中,待用戶往下滾屏的時候才 “惰性” 加載。
2. 將外部腳本置底(將腳本內容在頁面資訊內容載入後再載入)
前文有談到,瀏覽器是可以並發請求的,這一特點使得其能夠更快的載入資源,然而外鏈腳本在載入時卻會阻塞其他資源,例如在腳本載入完成之前,它後面的圖片、樣式以及其他腳本都處於阻塞狀態,直到腳本載入完成後才會開始載入。如果將腳本放在比較前面的位置,則會影響整個頁面的載入速度從而影響使用者體驗。解決這一問題的方法有很多,在
這裡有比較詳細的介紹
(這裡是譯文和
更詳細的例子
),而最簡單可依賴的方法就是將腳本盡可能的往後挪,減少對並發下載的影響。
3. 異步執行 inline腳本(其實原理和上面是一樣,保證腳本在頁面內容後面加載。)
inline腳本對性能的影響與外部腳本相比,是有過之而無不及。首頁,與外部腳本一樣, inline腳本在執行的時候一樣會阻塞並發請求,除此之外,由於瀏覽器在頁面處理方面是單線程的,當inline腳本在頁面渲染之前執行時,頁面的渲染工作則會被推遲。簡而言之, inline腳本在執行的時候,頁面處於空白狀態。鑑於以上兩點原因,建議將執行時間較長的inline腳本異步執行,異步的方式有很多種,例如使用script元素的defer 屬性(存在兼容性問題和其他一些問題,例如不能使用document.write)、使用setTimeout
,此外,在HTML5中引入了
Web Workers的機制,恰恰可以解決這類問題。
4. Lazy Load Javascript(只有在需要加載的時候加載,在一般情況下並不加載信息內容。)
隨著Javascript框架的流行,越來越多的站點也使用起了框架。不過,一個框架往往包括了很多的功能實現,這些功能並不是每一個頁面都需要的,如果下載了不需要的腳本則算得上是一種資源浪費-既浪費了頻寬又浪費了執行花費的時間。目前的做法大概有兩種,一種是為那些流量特別大的頁面專門訂製一個專用的 mini版框架,另一種則是 Lazy Load。 YUI 則使用了第二種方式,在 YUI的實作中,最初只載入核心模組,其他模組可以等到需要使用的時候才載入。
5. 將CSS放在HEAD中
如果將CSS放在其他地方例如BODY中,則瀏覽器有可能還未下載和解析到CSS就已經開始渲染頁面了,這就導致頁面由無CSS狀態跳到CSS狀態,使用者體驗比較糟糕。除此之外,有些瀏覽器會在 CSS下載完成後才開始渲染頁面,如果 CSS放在靠下的位置就會導致瀏覽器將渲染時間延後。
6. 非同步請求 Callback(就是將一些行為樣式提取出來,慢慢的載入資訊的內容)
在某些頁面中可能存在這樣一種需求,需要使用 script標籤來異步的請求資料。類似:
Javascript:
/*Callback 函數*/
function myCallback(info){
//do something here
}
HTML:
cb返回的內容:
myCallback('Hello world!');
像以上這種方式直接在頁面上寫<script>對頁面的性能也是有影響的,即增加了頁面首次載入的負擔,推遲了DOMLoaded和window.onload 事件的觸發時機。如果時效性允許的話,可以考慮在 DOMLoaded事件觸發的時候加載,或者使用 setTimeout方式來靈活的控制加載的時機。 <br/> 7. 減少不必要的HTTP跳轉<br/> 對於以目錄形式訪問的HTTP鏈接,很多人都會忽略鏈接最後是否帶'/',假如你的服務器對此是區別對待的話,那麼你也需要注意,這其中很可能隱藏了301跳轉,增加了多餘請求。具體參見下圖,其中第一個連結是以無 ’/'結尾的方式訪問的,於是伺服器有了一次跳轉。 <br/> 8. 避免重複的資源請求<br/> 這種情況主要是由於疏忽或頁面由多個模組拼接而成,然後每個模組中請求了同樣的資源時,會導致資源的重複請求</script>
二、程式碼級最佳化
1. Javascript
(1). DOM
DOM操作應該是腳本中最耗效能的一類操作,例如增加、修改、刪除DOM元素或對DOM集合進行操作。如果腳本中包含了大量的DOM操作則需要注意以下幾點:
a. HTML Collection(HTML收集器,傳回的是一個陣列內容資訊)
在腳本中document.images、document.forms 、 getElementsByTagName()傳回的都是HTMLCollection類型的集合,在平常使用的時候大多將它當作陣列來使用,因為它有length屬性,也可以使用索引來存取每個元素。不過在存取效能上則比數組差很多,原因是這個集合並不是一個靜態的結果,它表示的只是一個特定的查詢,每次訪問該集合時都會重新執行這個查詢從而更新查詢結果。所謂的 「訪問集合」 包括讀取集合的 length屬性、存取集合中的元素。
因此,當你需要遍歷 HTML Collection的時候,盡量將它轉為數組後再訪問,以提高性能。即使不轉換為數組,也請盡可能少的訪問它,例如在遍歷的時候可以將 length屬性、成員保存到局部變數後再使用局部變數。
b. Reflow & Repaint
除了上面一點之外, DOM操作還需要考慮瀏覽器的Reflow和Repaint ,因為這些都是需要消耗資源的,具體的可以參加以下文章:
如何減少瀏覽器的repaint和reflow?
Understanding Internet Explorer
Rendering Behaviour
Notes on HTML Reflow
(2). 慎用 with
with(obj){ p = 1}; 程式碼區塊的行為其實是修改了程式碼區塊中的
執行環境
,將obj放在了其作用域鏈的最前端,在with代碼塊中訪問非局部變量是都是先從obj上開始查找,如果沒有再依次按作用域鏈向上查找,因此使用with相當於增加了作用域鍊長度。而每次查找作用域鏈都是要消耗時間的,過長的作用域鏈會導致查找效能下降。
因此,除非你能肯定在 with程式碼中只存取 obj中的屬性,否則慎用 with,替代的可以使用局部變數快取需要存取的屬性。
(3). 避免使用 eval和 Function
每次 eval 或 Function 建構子作用於字串表示的原始程式碼時,腳本引擎都需要將原始程式碼轉換成可執行程式碼。這是很消耗資源的操作 —— 通常比簡單的函數呼叫慢 100倍以上。
eval 函數效率特別低,由於事先無法知曉傳給eval 的字串中的內容,eval在其上下文中解釋要處理的程式碼,也就是說編譯器無法最佳化上下文,因此只能有瀏覽器在運行時解釋程式碼。這對性能影響很大。
Function 建構子比 eval略好,因為使用此程式碼不會影響周圍程式碼 ;但其速度仍很慢。
此外,使用 eval和 Function也不利於Javascript 壓縮工具執行壓縮。
(4). 減少作用域鏈查找(這方面設計到一些內容的相關問題)
前文談到了作用域鏈查找問題,這一點在循環中是尤其需要注意的問題。如果在循環中需要存取非本作用域下的變數時請在遍歷之前用局部變數快取該變量,並在遍歷結束後重寫那個變量,這一點對全域變數尤其重要,因為全域變數處於作用域鏈的最頂端,訪問時的查找次數是最多的。
低效率的寫法:
// 全域變數
var globalVar = 1;
function myCallback(info){
for( var i = 100000; i--;){
//每次存取globalVar 都需要查找到作用域鏈最頂端,本例中需要存取100000 次
globalVar += i;
}
}
更有效率的寫法:
// 全域變數
var globalVar = 1;
function myCallback(info){
//局部變數快取全域變數
var localVar = globalVar;
for( var i = 100000; i--;){
//存取局部變數是最快的
localVar += i;
}
//本例只需要存取2次全域變數
在函數中只需要將globalVar中內容的值賦給localVar 中區
globalVar = localVar;
}
此外,要減少作用域鏈查找也應該減少閉包的使用。
(5). 資料存取
Javascript中的資料存取包含直接量(字串、正規表示式)、變數、物件屬性以及數組,其中對直接量和局部變數的存取是最快的,對物件屬性以及陣列的存取需要更大的開銷。當出現以下情況時,建議將資料放入局部變數:
a. 對任何物件屬性的存取超過1次
b. 對任何陣列成員的存取次數超過1次
另外,還應當盡可能的減少對物件以及數組深度查找。
(6). 字串拼接
在Javascript中使用"+" 號來拼接字串效率是比較低的,因為每次運行都會開闢新的記憶體並產生新的字串變量,然後將拼接結果賦值給新變數。與之相比更有效率的做法是使用陣列的 join方法,即將需要拼接的字串放在陣列中最後呼叫其 join方法得到結果。不過由於使用陣列也有一定的開銷,因此當需要拼接的字串較多的時候可以考慮用此方法。
關於Javascript優化的更詳細介紹請參考:
Write Efficient Javascript(PPT)
Efficient JavaScript
# 2. CSS選擇符
在大多數人的觀念中,都覺得瀏覽器對CSS選擇符的解析式從左往右進行的,例如
#toc A { color: #444; }
這樣一個選擇符,如果是從右往左解析則效率會很高,因為第一個ID選擇基本上就把查找的範圍限定了,但實際上瀏覽器對選擇符的解析是從右往左進行的。如同上面的選擇符,瀏覽器必須遍歷尋找每個 A標籤的祖先節點,效率並不像之前想像的那麼高。根據瀏覽器的這項行為特點,在寫選擇符的時候需要注意很多事項,有人已經一一列舉了,
詳情請參考此處。
3. HTML
對 HTML本身的優化現如今也越來越多的受人關注了,詳情可以參見這篇
總結性文章
。
4. Image壓縮
圖片壓縮是個技術活,不過現如今這方面的工具也非常多,壓縮之後往往能帶來不錯的效果,具體的壓縮原理以及方法在《 Even Faster Web Sites》第10 章有很詳細的介紹,有興趣的可以去看看。
總結
本文從頁面級以及代碼級兩個粒度對前端優化的各種方式做了一個總結,這些方法基本上都是前端開發人員在開發的過程中可以藉鑑和實踐的,除此之外,完整的前端優化還應該包括許多其他的途徑,例如CDN、 Gzip、多域名、無Cookie伺服器等等,由於對於開發人員的可操作性並不強大,在此也就不多敘述了,詳細的可以參考Yahoo和Google 的這些「金科玉律
以上是如何優化網頁效能的詳細內容。更多資訊請關注PHP中文網其他相關文章!

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

禪工作室 13.0.1
強大的PHP整合開發環境