0x01 程式碼最佳化與壓縮
(1)HTML
移除不必要的空白字元、註釋和冗餘標籤,以減少檔案大小
-
使用命令
npm install html-minifier -g
安裝 HTML Minifier -
使用命令
html-minifier -V
確認安裝成功 -
在 Node.js 環境中配置 index.js
// 引入 HTML Minifier const minify = require("html-minifier").minify; // 處理 HTML 文字 let result = minify('<p title="blah" id="moo">foo</p>', { removeAttributeQuotes: true, }); // 輸出處理結果 console.log(result);
-
使用命令
node .\index.js
執行,輸出結果為:<p title=blah id=moo>foo</p>
-
詳細參考:https://www.npmjs.com/package/html-minifier
線上使用:https://kangax.github.io/html-minifier/
(2)CSS
精簡樣式表,避免使用冗餘或過時的屬性,合理組織選擇器以減少計算複雜度
-
使用命令
npm install cssnano postcss postcss-cli --save-dev
安裝 PostCSS 與 CSSNaNo -
在 Node.js 環境中配置 postcss.config.js
module.exports = { plugins: [ require("cssnano")({ preset: "default", }), ], };
-
使用命令
npx postcss input.css > output.css
執行,生成最佳化後的結果 output.css -
詳細參考:https://www.cssnano.cn/docs/introduction/
-
其他工具:
- PostCSS:https://www.postcss.com.cn/
- PurgeCSS:https://www.purgecss.cn/
(3)JavaScript
使用工具(如 Webpack 等)或線上服務對程式碼進行壓縮,移除空格、註釋和不必要的字元
-
使用命令
npm install terser-webpack-plugin --save-dev
安裝 terser-webpack-plugin -
在 Node.js 環境中配置 webpack.config.js
const TerserPlugin = require("terser-webpack-plugin"); module.exports = { optimization: { minimize: true, minimizer: [new TerserPlugin()], }, };
-
使用命令
npx webpack
執行 Webpack 打包工具,並最佳化 JavaScript 程式碼
(4)合併檔案
將多個 CSS、JavaScript 檔案合併為一個,減少 HTTP 請求的數量
- 在 Webpack 中,可以透過配置
entry
和output
選項來自動合併多個模組到一個檔案中 - 在 Gulp 中,可以使用
gulp-concat
外掛來合併檔案
0x02 靜態資源最佳化
(1)壓縮圖片
- TinyPNG:API Reference
- ImageOptim:Optimize on the fly
- imagemin:https://www.npmjs.com/package/imagemin
(2)圖片懶載入
a. 原生 JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
img {
width: 1000px;
height: 700px;
background-color: wheat;
object-fit: cover;
object-position: center;
}
</style>
</head>
<body>
<img src="" data-src="./images/1.jpg" alt="" />
<img src="" data-src="./images/2.jpg" alt="" />
<img src="" data-src="./images/3.jpg" alt="" />
<img src="" data-src="./images/4.jpg" alt="" />
<img src="" data-src="./images/5.jpg" alt="" />
<script>
/**
* 初始化圖片懶載入功能。
* 該函式透過監聽視窗的滾動事件,來實現圖片的延遲載入。當圖片進入視口時,將其src屬性設定為真正的圖片源URL,從而實現懶載入的效果。
*/
const imageLazyLoad = () => {
// 獲取頁面中所有帶有data-src屬性的圖片元素
const imgs = document.querySelectorAll("img");
// 定義計算函式,用於檢查圖片是否進入視口
const calc = () => {
imgs.forEach((img) => {
// 檢查圖片是否進入視口:如果圖片的頂部位置小於等於視窗的底部位置,則圖片已進入視口,可以載入
if (img.offsetTop <= window.innerHeight + window.scrollY)
// 設定圖片的src屬性為data-src屬性的值,真正開始載入圖片
img.src = img.dataset.src;
else
// 如果圖片未進入視口,則返回,不進行載入
return;
});
};
// 監聽視窗的滾動事件,以便在滾動時觸發圖片的載入
window.addEventListener("scroll", () => calc());
// 初次載入頁面時,立即計算並載入可視區域內的圖片
calc();
};
// 呼叫函式,初始化圖片懶載入
imageLazyLoad();
</script>
</body>
</html>
b. Intersection Observer API
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
img {
width: 1000px;
height: 700px;
background-color: wheat;
object-fit: cover;
object-position: center;
}
</style>
</head>
<body>
<img src="" data-src="./images/1.jpg" alt="" />
<img src="" data-src="./images/2.jpg" alt="" />
<img src="" data-src="./images/3.jpg" alt="" />
<img src="" data-src="./images/4.jpg" alt="" />
<img src="" data-src="./images/5.jpg" alt="" />
<script>
/**
* 初始化圖片懶載入
* 該函式透過IntersectionObserver API來實現圖片的懶載入。只有當圖片進入視口時,才會真正載入圖片資源
*/
const imageLazyLoad = () => {
// 獲取所有需要懶載入的圖片元素
const imgs = document.querySelectorAll("img");
// 建立IntersectionObserver例項,用於觀察圖片是否進入視口
const observer = new IntersectionObserver((entries) => {
// 遍歷所有觀察到的條目
entries.forEach((entry) => {
// 如果圖片進入視口
if (entry.isIntersecting) {
let img = entry.target;
// 設定圖片的src屬性為data-src屬性的值,即真正的圖片源地址
img.src = img.dataset.src;
// 停止觀察該圖片,因為它已經載入
observer.unobserve(img);
}
});
});
// 對所有需要懶載入的圖片元素啟用IntersectionObserver觀察
imgs.forEach((img) => {
observer.observe(img);
});
};
imageLazyLoad();
</script>
</body>
</html>
c. 第三方庫
- 如 vue-lazyload(Vue 框架中使用)
(3)CDN
- 部署內容分發網路(CDN),將靜態資源託管在地理位置接近使用者的邊緣節點上,減少延遲
- 步驟:
- 選擇 CDN 服務提供商,如 Amazon、Cloudflare 等
- 上傳靜態資源
- 配置 DNS
- 測試並最佳化
- 優點:減少訪問延遲、提高可用性、減輕源伺服器負載
(4)快取機制
- 步驟:
- 設定 Cache-Control 頭部:響應頭欄位,表示在快取有效期內直接從本地快取中載入這些資源,而不是向伺服器傳送請求
- 使用 ETag:響應頭欄位,表示資源的特定版本
- 當瀏覽器再次請求資源時,會將 ETag 值傳送給伺服器,如果資源未更改(即 ETag 值相同),伺服器將返回 304 Not Modified 響應,告訴瀏覽器使用本地快取的版本。
- 配置 Expires 頭部:相容舊版瀏覽器
- 優點:減少伺服器負載、加快載入速度、改善使用者體驗
0x03 渲染效能最佳化
(1)減少 DOM 元素數量
避免不必要的 DOM 元素,以減少渲染和重繪的時間
-
使用 CSS 替代 DOM 元素
舉例:
<ul> <li><img src="icon1.png" alt="Icon 1"><span>Item 1</span></li> <li><img src="icon2.png" alt="Icon 2"><span>Item 2</span></li> </ul>
最佳化為
<ul> <li class="item">Item 1</li> <li class="item">Item 2</li> </ul> <style> .item::before { content: ""; display: inline-block; width: 20px; height: 20px; background-image: url(icon-based-on-class.png); background-size: cover; margin-right: 5px; } </style>
-
引入 Flex 佈局與 Grid 佈局
舉例:
<div class="container"> <div class="row"> <div class="col">Item 1</div> <div class="col">Item 2</div> </div> </div>
最佳化為
<div style="display: flex;"> <div>Item 1</div> <div>Item 2</div> </div>
-
多個動態內容採用 DocumentFragment 新增
舉例:
for (let i = 0; i < 100; i++) { let li = document.createElement("li"); li.textContent = `Item ${i}`; document.querySelector("ul").appendChild(li); }
最佳化為
let fragment = document.createDocumentFragment(); for (let i = 0; i < 100; i++) { let li = document.createElement("li"); li.textContent = `Item ${i}`; fragment.appendChild(li); } document.querySelector("ul").appendChild(fragment);
DocumentFragment 是一個輕量級的文件物件,可以包含節點和子節點,但不會成為文件樹的一部分
(2)事件委託
透過事件委託來減少與 DOM 的互動次數,提高效能
-
事件委託:一種事件處理的技術,它利用事件冒泡的原理,只在父元素上設定一個事件監聽器,而不是在每個子元素上分別設定。當子元素上發生事件時,該事件會冒泡到父元素,父元素上的事件監聽器會檢查事件源(即觸發事件的子元素),並據此執行相應的操作
-
舉例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <ul> <li>Item 1</li> <li>Item 2</li> </ul> <button>Add item</button> <script> const ul = document.querySelector("ul"); ul.addEventListener("click", (e) => { if (e.target.tagName === "LI") { console.log("e.target.textContent", e.target.textContent); e.target.remove(); } }); const button = document.querySelector("button"); button.addEventListener("click", () => { const newItem = document.createElement("li"); newItem.textContent = `Item ${ul.children.length + 1}`; ul.appendChild(newItem); }); </script> </body> </html>
(3)最佳化 DOM 操作
使用批次更新、虛擬 DOM 等技術減少重繪和迴流
- 批次更新:將多個 DOM 操作合併成一個批次,然後一次性執行(參考 DocumentFragment)
- 虛擬 DOM:一種程式設計概念,用 JavaScript 物件來表示 DOM 樹
- 當應用程式的狀態發生變化時,虛擬 DOM 樹會首先進行更新,然後使用高效的演算法(如 diff 演算法)來比較新舊虛擬 DOM 樹之間的差異,並只將這些差異應用到真實的 DOM 上
- 即檢測發生變化的 DOM 元素,並僅對變化的 DOM 元素操作,減少不必要的 DOM 操作
- 一般在 React、Vue 等前端框架中廣泛應用
(4)CSS 放在頂部
-
將CSS放在
<head>
標籤內,以確保頁面在載入時能夠優先渲染樣式<head> <link rel="stylesheet" href="style.css" /> </head>
(5)非同步載入 JavaScript
-
將非首屏必需的 JS 指令碼放在文件末尾或使用
async
、defer
屬性,避免阻塞渲染<!DOCTYPE html> <html lang="en"> <head> <!-- 同步載入首屏必需指令碼 --> <script src="critical.js"></script> </head> <body> <!-- 頁面內容 --> <!-- 非同步載入非首屏且無依賴的指令碼 --> <script async src="non-critical-async.js"></script> <!-- 非同步載入但按順序執行的非首屏指令碼 --> <script defer src="non-critical-defer.js"></script> <!-- 將非首屏指令碼放在底部(適用於不支援 async/defer 的老舊瀏覽器) --> <!-- <script src="non-critical-old-browser.js"></script> --> </body> </html>
-
async
與defer
- 兩者都用於非同步載入指令碼
async
:完全非同步,指令碼的載入和執行不會阻塞文件的解析,但多個非同步指令碼之間執行順序不一定,不建議用於具有依賴關係的指令碼defer
:等到整個文件都被解析和顯示之後,再按照指令碼在文件中出現的順序來執行
0x04 網路效能最佳化
(1)啟用 HTTP/2 或 HTTP/3
- 啟用 HTTP/2 或 HTTP/3,利用多路複用、頭部壓縮等特性提升請求效率
- 一般在 Web 伺服器中,透過 Nginx、Apache 等配置並啟用
(2)TLS/SSL
- 確保網站使用 HTTPS 加密傳輸,提高安全性並可能獲得 SEO 優勢
- HTTPS、TLS/SSL 在網路傳輸的應用參考:瀏覽器工作過程及相關名詞 | 部落格園-SRIGT
(3)預載入和預讀取
a. 預載入
-
預載入:一種資源提示,它告訴瀏覽器這個資源對於當前導航立即需要,並且應該被優先下載和解析
- 如字型、CSS 和關鍵 JavaScript 指令碼
-
使用
<link rel="preload">
預載入關鍵資源<head> <!-- 預載入字型 --> <link rel="preload" href="fonts/myfont.woff2" as="font" type="font/woff2" crossorigin="anonymous"> <!-- 預載入CSS --> <link rel="preload" href="styles/main.css" as="style"> <!-- 其他頭部標籤... --> </head>
b. 預讀取
-
預讀取:一種資源提示,它告訴瀏覽器這個資源可能會在將來的導航中被用到,但不像預載入那樣具有緊迫性
- 瀏覽器可以選擇在空閒時間下載這些資源,以便在使用者實際需要它們時能夠更快地載入
-
使用
<link rel="prefetch">
預讀取可能需要的未來資源<link rel="prefetch" href="details/product-images.jpg"> <link rel="prefetch" href="details/product-details.js">
(4)本地快取
a. Cookie
- 儲存限制:儲存大小限制在 4KB 左右,且儲存數量有限制
- 缺點:
- 每次都會將 Cookie 資料攜帶在 HTTP 請求中,可能帶來效能問題並佔用頻寬
- 安全性較低
- 由於瀏覽器的跨域限制,客戶端和服務端必須保證同源原則
- 場景:跟蹤使用者會話資訊,如使用者登入狀態、購物車資訊等
- Session:以鍵值對的方式將快取資料儲存在伺服器中,並把鍵值(Session ID)作為 Cookie 返給瀏覽器
- Token:應對移動網際網路不提供 Cookie 的解決方案,將資料雜湊加密並儲存在移動端的儲存系統中
- JWT 是一種廣泛應用的 Token 標準
b. LocalStorage
- HTML5 提供的一種新的本地快取方案,用於儲存資料在使用者的瀏覽器中
- 儲存限制:
- 長久儲存,無有效期,直到手動刪除或瀏覽器清理快取為止
- 儲存空間一般可以達到 5MB 及以上(不同的瀏覽器有所區別)
- 場景:儲存需要在多個頁面或會話中持久儲存的資料,如使用者偏好設定、遊戲進度等
c. SessionStorage
- 大體與 LocalStorage 類似,在儲存限制上,資料僅在當前會話期間有效,瀏覽器關閉或標籤頁關閉後資料即被清除
- 場景:儲存僅在當前會話中需要的資料,如臨時狀態資訊、表單資料等
d. IndexedDB
- 一個低階的 API,允許進行復雜的查詢、事務處理和資料庫管理操作,提供索引功能
- 儲存限制:儲存空間相對較大,可以儲存大量資料
- 場景:儲存大量結構化資料並需要進行復雜查詢,如離線應用、遊戲資料儲存等
e. Cache API
-
一種用於儲存和檢索網路請求的響應的介面
-
可以與 Service Workers 結合使用,實現離線應用和效能最佳化
Service Workers:在 Web 瀏覽器中執行的指令碼,具備在後臺獨立於網頁執行的能力
- 提供很多高階功能,如離線內容快取、推送通知、背景資料同步等
-
場景:精確控制快取策略和資源快取,如構建 PWA(Progressive Web Apps)時
0x05 框架與庫的選擇與最佳化
(1)框架
-
輕量級
- Preact:3kb 大小的 React 輕量、快速替代方案,擁有相同的現代 API
- Vue3:在 Vue2 基礎上,應用 Tree-shaking,允許在構建過程中自動移除未使用的程式碼
-
按需載入
-
採用程式碼分割和懶載入技術,將應用拆分成多個小塊,並在需要時才載入它們
-
以 Vue Router 為例,Vue Router 支援動態匯入
const routes = [ { path: "/products", name: "Products", // 使用動態匯入來懶載入元件 component: () => import(/* webpackChunkName: "products" */ "./views/Products.vue"), }, // 其他路由... ];
-
(2)第三方庫
- 僅引入必要的庫,避免過時或冗餘的庫,定期檢查更新以利用效能最佳化
0x06 效能監控與最佳化工具
(1)效能分析工具
- Lighthouse:Google 開發的一款開源自動化工具,整合在 Chrome DevTools 中
- PageSpeed Insights:Google 提供的一款免費工具,官網連結
- Chrome DevTools Performance 皮膚:整合在 Chrome DevTools 中
(2)使用者效能監控
採用整合 RUM(Real User Monitoring)工具收集實際使用者的載入效能資料
- Google Analytics:使用外掛 Google Tag Manager 來部署 RUM 指令碼,該指令碼將收集使用者互動資料併傳送到 Google Analytics 進行處理和分析
- SpeedCurve:https://www.speedcurve.com/
0x07 其他最佳化策略
(1)首屏內容最佳化
- 確保首屏載入時立即展示關鍵內容,避免使用者看到空白或載入指示器過久
- 舉例:電商網站首屏最佳化操作
- 精簡首屏內容:保留最關鍵的內容,如網站Logo、歡迎語、輪播圖、商品推薦
- 最佳化圖片和指令碼:壓縮和最佳化所有首屏載入的圖片,合併和壓縮 CSS 和 JavaScript 指令碼進行,減少HTTP請求次數
- 非同步載入非關鍵內容:非首屏關鍵內容設定為非同步載入,即使用者滾動到相應位置時再載入這些內容
- 使用CDN加速:將網站內容分發到全球多個 CDN 節點,根據使用者地理位置選擇最近的節點進行載入
- 預載入和快取:利用瀏覽器的預載入和快取機制,提前載入和儲存一些常用的資原始檔
(2)語義化 HTML
-
使用合理 HTML 標記以及其特有的屬性去格式化文件內容,提高內容可理解性
-
詳細方法參考:HTML語義化 | CSDN-北航程式設計師小陳
-
舉例:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>新聞文章標題</title> </head> <body> <header> <h1>新聞文章標題</h1> <p>由 <a href="/author-profile">作者姓名</a> 發表於 <time datetime="2023-04-01">2023年4月1日</time></p> </header> <main> <article> <h2>引言</h2> <p>這裡是引言部分的內容,簡要介紹文章的主題和背景。</p> <h2>正文標題</h2> <p>這裡是正文的第一段,詳細闡述文章的主要觀點或故事。</p> <h3>小節標題</h3> <p>這裡是文章中的一個小節,進一步細化或支援主要觀點。</p> <!-- 可以繼續新增更多的h2、h3、p等元素來構建文章內容 --> <footer> <p>文章結束。</p> </footer> </article> </main> <aside> <h2>相關文章</h2> <ul> <li><a href="/article1">相關文章1</a></li> <li><a href="/article2">相關文章2</a></li> <!-- 更多相關文章連結 --> </ul> </aside> <footer> <p>版權所有 © 2023 網站名稱</p> </footer> </body> </html>
(3)後設資料
-
設定
<title>
、<meta>
、<link rel="canonical">
等 SEO 相關標籤 -
舉例:
<!DOCTYPE html> <html lang="zh-CN"> <head> <!-- 頁面編碼,用於處理不同語言的字串 --> <meta charset="UTF-8" /> <!-- 設定視口,確保網頁在不同裝置上正確顯示 --> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!-- 頁面標題,顯示在瀏覽器標籤和搜尋結果中 --> <title>頁面標題 - 網站名稱</title> <!-- 頁面描述,顯示在搜尋結果中,用於概括頁面內容 --> <meta name="description" content="這裡是頁面的簡短描述,應包含關鍵詞並吸引使用者點選。" /> <!-- 頁面關鍵詞,雖然現代搜尋引擎對keywords標籤的重視程度降低,但仍可作為參考 --> <meta name="keywords" content="關鍵詞1, 關鍵詞2, 關鍵詞3" /> <!-- 指定頁面的規範URL,有助於防止內容重複被搜尋引擎索引 --> <link rel="canonical" href="https://www.example.com/your-page-url" /> <!-- 其他可能的後設資料 --> <meta name="author" content="作者姓名或組織" /> <!-- 新增作者資訊 --> <meta name="robots" content="index, follow" /> <!-- 指示搜尋引擎索引並跟蹤頁面上的連結 --> <!-- 對於響應式網站,可以使用meta標籤來適應不同裝置 --> <meta name="HandheldFriendly" content="true" /> <!-- 告訴移動裝置,頁面適合於手機瀏覽 --> <meta name="MobileOptimized" content="320" /> <!-- 指定移動裝置的螢幕寬度,以適應響應式設計 --> <!-- 引入CSS樣式 --> <link rel="stylesheet" href="style.css" /> </head> <body> <!-- 頁面內容 --> <!-- 引入JavaScript指令碼 --> <script src="script.js"></script> </body> </html>
(4)結構化資料
-
結構化資料:一種使用特定格式(如 JSON-LD、Microdata 或 RDFa)來標記網頁內容的方式,以便搜尋引擎和其他機器能夠更容易地理解和處理這些資訊
-
舉例:以下是採用 Schema.org 和JSON-LD 格式的結構化資料
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <title>電影《星際穿越》</title> <script type="application/ld+json"> { "@context": "https://schema.org/", "@type": "Movie", "name": "星際穿越", "image": "https://example.com/movie-poster.jpg", "director": { "@type": "Person", "name": "克里斯托弗·諾蘭" }, "genre": ["科幻", "劇情", "冒險"], "actor": [ { "@type": "Person", "name": "馬修·麥康納希" }, { "@type": "Person", "name": "安妮·海瑟薇" } ], "datePublished": "2014-11-07", "description": "一隊探險家利用他們針對蟲洞的新發現,超越人類對於太空旅行的極限,從而開始在廣袤的宇宙中進行星際航行的故事。", "aggregateRating": { "@type": "AggregateRating", "ratingValue": "8.7", "reviewCount": "123456" } } </script> </head> <body> <!-- 網頁內容 --> <h1>電影《星際穿越》</h1> <p>導演:克里斯托弗·諾蘭</p> <p>主演:馬修·麥康納希, 安妮·海瑟薇</p> <p>型別:科幻, 劇情, 冒險</p> <p>上映日期: 2014年11月7日</p> <p>劇情簡介:...(詳細描述)</p> </body> </html>
(5)無障礙性(a11y)
- 無障礙性:確保網站對所有使用者,包括殘障使用者,都能夠友好地訪問和使用
- WCAG 標準:Web 內容無障礙指南
- 舉例:
- 非文字內容的文字替代
- 如:
<img alt="這是一張圖片" />
- 如:
- 鍵盤可訪問
- 清晰和一致的導航
- 足夠的顏色對比度
- 字幕和音訊描述
- 非文字內容的文字替代
-End-