
一、CDN
1. CDN的概念
CDN(Content Delivery Network,內容分發網絡)是指一種通過互聯網互相連接的電腦網絡系統,利用最靠近每位用戶的服務器,更快、更可靠地將音樂、圖片、視頻、應用程序及其他文件發送給用戶,來提供高性能、可擴展性及低成本的網絡內容傳遞給用戶。
典型的CDN系統由下面三個部分組成:
- 分發服務系統: 最基本的工作單元就是Cache設備,cache(邊緣cache)負責直接響應最終用戶的訪問請求,把緩存在本地的內容快速地提供給用戶。同時cache還負責與源站點進行內容同步,把更新的內容以及本地沒有的內容從源站點獲取並保存在本地。Cache設備的數量、規模、總服務能力是衡量一個CDN系統服務能力的最基本的指標。
- 負載均衡系統: 主要功能是負責對所有發起服務請求的用戶進行訪問調度,確定提供給用戶的最終實際訪問地址。兩級調度體系分為全局負載均衡(GSLB)和本地負載均衡(SLB)。全局負載均衡主要根據用戶就近性原則,通過對每個服務節點進行“最優”判斷,確定向用戶提供服務的cache的物理位置。本地負載均衡主要負責節點內部的設備負載均衡
- 運營管理系統: 運營管理系統分為運營管理和網絡管理子系統,負責處理業務層面的與外界系統交互所必須的收集、整理、交付工作,包含客戶管理、產品管理、計費管理、統計分析等功能。
2. CDN的作用
CDN一般會用來托管Web資源(包括文本、圖片和腳本等),可供下載的資源(媒體文件、軟件、文檔等),應用程序(門戶網站等)。使用CDN來加速這些資源的訪問。
(1)在性能方面,引入CDN的作用在於:
- 用戶收到的內容來自最近的數據中心,延遲更低,內容加載更快
- 部分資源請求分配給了CDN,減少了服務器的負載
(2)在安全方面,CDN有助於防御DDoS、MITM等網絡攻擊:
- 針對DDoS:通過監控分析異常流量,限制其請求頻率
- 針對MITM:從源服務器到 CDN 節點到 ISP(Internet Service Provider),全鏈路 HTTPS 通信
除此之外,CDN作為一種基礎的雲服務,同樣具有資源托管、按需擴展(能夠應對流量高峰)等方面的優勢。
3. CDN的原理
CDN和DNS有着密不可分的聯系,先來看一下DNS的解析域名過程,在瀏覽器輸入 www.test.com 的解析過程如下: (1) 檢查瀏覽器緩存 (2)檢查操作系統緩存,常見的如hosts文件 (3)檢查路由器緩存 (4)如果前幾步都沒沒找到,會向ISP(網絡服務提供商)的LDNS服務器查詢 (5)如果LDNS服務器沒找到,會向根域名服務器(Root Server)請求解析,分為以下幾步:
- 根服務器返回頂級域名(TLD)服務器如
.com,.cn,.org等的地址,該例子中會返回.com的地址 - 接着向頂級域名服務器發送請求,然后會返回次級域名(SLD)服務器的地址,本例子會返回
.test的地址 - 接着向次級域名服務器發送請求,然后會返回通過域名查詢到的目標IP,本例子會返回
www.test.com的地址 - Local DNS Server會緩存結果,並返回給用戶,緩存在系統中
CDN的工作原理: (1)用戶未使用CDN緩存資源的過程:
- 瀏覽器通過DNS對域名進行解析(就是上面的DNS解析過程),依次得到此域名對應的IP地址
- 瀏覽器根據得到的IP地址,向域名的服務主機發送數據請求
- 服務器向瀏覽器返回響應數據
(2)用戶使用CDN緩存資源的過程:
- 對於點擊的數據的URL,經過本地DNS系統的解析,發現該URL對應的是一個CDN專用的DNS服務器,DNS系統就會將域名解析權交給CNAME指向的CDN專用的DNS服務器。
- CND專用DNS服務器將CND的全局負載均衡設備IP地址返回給用戶
- 用戶向CDN的全局負載均衡設備發起數據請求
- CDN的全局負載均衡設備根據用戶的IP地址,以及用戶請求的內容URL,選擇一台用戶所屬區域的區域負載均衡設備,告訴用戶向這台設備發起請求
- 區域負載均衡設備選擇一台合適的緩存服務器來提供服務,將該緩存服務器的IP地址返回給全局負載均衡設備
- 全局負載均衡設備把服務器的IP地址返回給用戶
- 用戶向該緩存服務器發起請求,緩存服務器響應用戶的請求,將用戶所需內容發送至用戶終端。
如果緩存服務器沒有用戶想要的內容,那么緩存服務器就會向它的上一級緩存服務器請求內容,以此類推,直到獲取到需要的資源。最后如果還是沒有,就會回到自己的服務器去獲取資源。
CNAME(意為:別名):在域名解析中,實際上解析出來的指定域名對應的IP地址,或者該域名的一個CNAME,然后再根據這個CNAME來查找對應的IP地址。
4. CDN的使用場景
- **使用第三方的CDN服務:**如果想要開源一些項目,可以使用第三方的CDN服務
- **使用CDN進行靜態資源的緩存:**將自己網站的靜態資源放在CDN上,比如js、css、圖片等。可以將整個項目放在CDN上,完成一鍵部署。
- **直播傳送:**直播本質上是使用流媒體進行傳送,CDN也是支持流媒體傳送的,所以直播完全可以使用CDN來提高訪問速度。CDN在處理流媒體的時候與處理普通靜態文件有所不同,普通文件如果在邊緣節點沒有找到的話,就會去上一層接着尋找,但是流媒體本身數據量就非常大,如果使用回源的方式,必然會帶來性能問題,所以流媒體一般采用的都是主動推送的方式來進行。
二、懶加載
1. 懶加載的概念
懶加載也叫做延遲加載、按需加載,指的是在長網頁中延遲加載圖片數據,是一種較好的網頁性能優化的方式。在比較長的網頁或應用中,如果圖片很多,所有的圖片都被加載出來,而用戶只能看到可視窗口的那一部分圖片數據,這樣就浪費了性能。
如果使用圖片的懶加載就可以解決以上問題。在滾動屏幕之前,可視化區域之外的圖片不會進行加載,在滾動屏幕時才加載。這樣使得網頁的加載速度更快,減少了服務器的負載。懶加載適用於圖片較多,頁面列表較長(長列表)的場景中。
2. 懶加載的特點
- 減少無用資源的加載:使用懶加載明顯減少了服務器的壓力和流量,同時也減小了瀏覽器的負擔。
- 提升用戶體驗: 如果同時加載較多圖片,可能需要等待的時間較長,這樣影響了用戶體驗,而使用懶加載就能大大的提高用戶體驗。
- 防止加載過多圖片而影響其他資源文件的加載 :會影響網站應用的正常使用。
3. 懶加載的實現原理
圖片的加載是由src引起的,當對src賦值時,瀏覽器就會請求圖片資源。根據這個原理,我們使用HTML5 的data-xxx屬性來儲存圖片的路徑,在需要加載圖片的時候,將data-xxx中圖片的路徑賦值給src,這樣就實現了圖片的按需加載,即懶加載。
注意:data-xxx 中的xxx可以自定義,這里我們使用data-src來定義。
懶加載的實現重點在於確定用戶需要加載哪張圖片,在瀏覽器中,可視區域內的資源就是用戶需要的資源。所以當圖片出現在可視區域時,獲取圖片的真實地址並賦值給圖片即可。
使用原生JavaScript實現懶加載:
知識點:
(1)window.innerHeight 是瀏覽器可視區的高度
(2)document.body.scrollTop || document.documentElement.scrollTop 是瀏覽器滾動的過的距離
(3)imgs.offsetTop 是元素頂部距離文檔頂部的高度(包括滾動條的距離)
(4)圖片加載條件:img.offsetTop < window.innerHeight + document.body.scrollTop;
圖示:
代碼實現:
<div class="container">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
</div>
<script>
var imgs = document.querySelectorAll('img');
function lozyLoad(){
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
var winHeight= window.innerHeight;
for(var i=0;i < imgs.length;i++){
if(imgs[i].offsetTop < scrollTop + winHeight ){
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
window.onscroll = lozyLoad();
</script>
4. 懶加載與預加載的區別
這兩種方式都是提高網頁性能的方式,兩者主要區別是一個是提前加載,一個是遲緩甚至不加載。懶加載對服務器前端有一定的緩解壓力作用,預加載則會增加服務器前端壓力。
- 懶加載也叫延遲加載,指的是在長網頁中延遲加載圖片的時機,當用戶需要訪問時,再去加載,這樣可以提高網站的首屏加載速度,提升用戶的體驗,並且可以減少服務器的壓力。它適用於圖片很多,頁面很長的電商網站的場景。懶加載的實現原理是,將頁面上的圖片的 src 屬性設置為空字符串,將圖片的真實路徑保存在一個自定義屬性中,當頁面滾動的時候,進行判斷,如果圖片進入頁面可視區域內,則從自定義屬性中取出真實路徑賦值給圖片的 src 屬性,以此來實現圖片的延遲加載。
- 預加載指的是將所需的資源提前請求加載到本地,這樣后面在需要用到時就直接從緩存取資源。 通過預加載能夠減少用戶的等待時間,提高用戶的體驗。我了解的預加載的最常用的方式是使用 js 中的 image 對象,通過為 image 對象來設置 scr 屬性,來實現圖片的預加載。
三、回流與重繪
1. 回流與重繪的概念及觸發條件
(1)回流
當渲染樹中部分或者全部元素的尺寸、結構或者屬性發生變化時,瀏覽器會重新渲染部分或者全部文檔的過程就稱為回流。
下面這些操作會導致回流:
- 頁面的首次渲染
- 瀏覽器的窗口大小發生變化
- 元素的內容發生變化
- 元素的尺寸或者位置發生變化
- 元素的字體大小發生變化
- 激活CSS偽類
- 查詢某些屬性或者調用某些方法
- 添加或者刪除可見的DOM元素
在觸發回流(重排)的時候,由於瀏覽器渲染頁面是基於流式布局的,所以當觸發回流時,會導致周圍的DOM元素重新排列,它的影響范圍有兩種:
- 全局范圍:從根節點開始,對整個渲染樹進行重新布局
- 局部范圍:對渲染樹的某部分或者一個渲染對象進行重新布局
(2)重繪
當頁面中某些元素的樣式發生變化,但是不會影響其在文檔流中的位置時,瀏覽器就會對元素進行重新繪制,這個過程就是重繪。
下面這些操作會導致回流:
- color、background 相關屬性:background-color、background-image 等
- outline 相關屬性:outline-color、outline-width 、text-decoration
- border-radius、visibility、box-shadow
注意: 當觸發回流時,一定會觸發重繪,但是重繪不一定會引發回流。
2. 如何避免回流與重繪?
減少回流與重繪的措施:
- 操作DOM時,盡量在低層級的DOM節點進行操作
- 不要使用
table布局, 一個小的改動可能會使整個table進行重新布局 - 使用CSS的表達式
- 不要頻繁操作元素的樣式,對於靜態頁面,可以修改類名,而不是樣式。
- 使用absolute或者fixed,使元素脫離文檔流,這樣他們發生變化就不會影響其他元素
- 避免頻繁操作DOM,可以創建一個文檔片段
documentFragment,在它上面應用所有DOM操作,最后再把它添加到文檔中 - 將元素先設置
display: none,操作結束后再把它顯示出來。因為在display屬性為none的元素上進行的DOM操作不會引發回流和重繪。 - 將DOM的多個讀操作(或者寫操作)放在一起,而不是讀寫操作穿插着寫。這得益於瀏覽器的渲染隊列機制。
瀏覽器針對頁面的回流與重繪,進行了自身的優化——渲染隊列
瀏覽器會將所有的回流、重繪的操作放在一個隊列中,當隊列中的操作到了一定的數量或者到了一定的時間間隔,瀏覽器就會對隊列進行批處理。這樣就會讓多次的回流、重繪變成一次回流重繪。
上面,將多個讀操作(或者寫操作)放在一起,就會等所有的讀操作進入隊列之后執行,這樣,原本應該是觸發多次回流,變成了只觸發一次回流。
3. 如何優化動畫?
對於如何優化動畫,我們知道,一般情況下,動畫需要頻繁的操作DOM,就就會導致頁面的性能問題,我們可以將動畫的position屬性設置為absolute或者fixed,將動畫脫離文檔流,這樣他的回流就不會影響到頁面了。
4. documentFragment 是什么?用它跟直接操作 DOM 的區別是什么?
MDN中對documentFragment的解釋:
DocumentFragment,文檔片段接口,一個沒有父對象的最小文檔對象。它被作為一個輕量版的 Document使用,就像標准的document一樣,存儲由節點(nodes)組成的文檔結構。與document相比,最大的區別是DocumentFragment不是真實 DOM 樹的一部分,它的變化不會觸發 DOM 樹的重新渲染,且不會導致性能等問題。
當我們把一個 DocumentFragment 節點插入文檔樹時,插入的不是 DocumentFragment 自身,而是它的所有子孫節點。在頻繁的DOM操作時,我們就可以將DOM元素插入DocumentFragment,之后一次性的將所有的子孫節點插入文檔中。和直接操作DOM相比,將DocumentFragment 節點插入DOM樹時,不會觸發頁面的重繪,這樣就大大提高了頁面的性能。
四、節流與防抖
1. 對節流與防抖的理解
- 函數防抖是指在事件被觸發 n 秒后再執行回調,如果在這 n 秒內事件又被觸發,則重新計時。這可以使用在一些點擊請求的事件上,避免因為用戶的多次點擊向后端發送多次請求。
- 函數節流是指規定一個單位時間,在這個單位時間內,只能有一次觸發事件的回調函數執行,如果在同一個單位時間內某事件被觸發多次,只有一次能生效。節流可以使用在 scroll 函數的事件監聽上,通過事件節流來降低事件調用的頻率。
防抖函數的應用場景:
- 按鈕提交場景:防⽌多次提交按鈕,只執⾏最后提交的⼀次
- 服務端驗證場景:表單驗證需要服務端配合,只執⾏⼀段連續的輸⼊事件的最后⼀次,還有搜索聯想詞功能類似⽣存環境請⽤lodash.debounce
節流函數的適⽤場景:
- 拖拽場景:固定時間內只執⾏⼀次,防⽌超⾼頻次觸發位置變動
- 縮放場景:監控瀏覽器resize
- 動畫場景:避免短時間內多次觸發動畫引起性能問題
2. 實現節流函數和防抖函數
函數防抖的實現:
function debounce(fn, wait) {
var timer = null;
return function() {
var context = this,
args = [...arguments];
// 如果此時存在定時器的話,則取消之前的定時器重新記時
if (timer) {
clearTimeout(timer);
timer = null;
}
// 設置定時器,使事件間隔指定事件后執行
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
函數節流的實現:
// 時間戳版
function throttle(fn, delay) {
var preTime = Date.now();
return function() {
var context = this,
args = [...arguments],
nowTime = Date.now();
// 如果兩次時間間隔超過了指定時間,則執行函數。
if (nowTime - preTime >= delay) {
preTime = Date.now();
return fn.apply(context, args);
}
};
}
// 定時器版
function throttle (fun, wait){
let timeout = null
return function(){
let context = this
let args = [...arguments]
if(!timeout){
timeout = setTimeout(() => {
fun.apply(context, args)
timeout = null
}, wait)
}
}
}
五、圖片優化
1. 如何對項目中的圖片進行優化?
- 不用圖片。很多時候會使用到很多修飾類圖片,其實這類修飾圖片完全可以用 CSS 去代替。
- 對於移動端來說,屏幕寬度就那么點,完全沒有必要去加載原圖浪費帶寬。一般圖片都用 CDN 加載,可以計算出適配屏幕的寬度,然后去請求相應裁剪好的圖片。
- 小圖使用 base64 格式
- 將多個圖標文件整合到一張圖片中(雪碧圖)
- 選擇正確的圖片格式:
- 對於能夠顯示 WebP 格式的瀏覽器盡量使用 WebP 格式。因為 WebP 格式具有更好的圖像數據壓縮算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的圖像質量,缺點就是兼容性並不好
- 小圖使用 PNG,其實對於大部分圖標這類圖片,完全可以使用 SVG 代替
- 照片使用 JPEG
2. 常見的圖片格式及使用場景
(1)BMP,是無損的、既支持索引色也支持直接色的點陣圖。這種圖片格式幾乎沒有對數據進行壓縮,所以BMP格式的圖片通常是較大的文件。
(2)GIF是無損的、采用索引色的點陣圖。采用LZW壓縮算法進行編碼。文件小,是GIF格式的優點,同時,GIF格式還具有支持動畫以及透明的優點。但是GIF格式僅支持8bit的索引色,所以GIF格式適用於對色彩要求不高同時需要文件體積較小的場景。
(3)JPEG是有損的、采用直接色的點陣圖。JPEG的圖片的優點是采用了直接色,得益於更豐富的色彩,JPEG非常適合用來存儲照片,與GIF相比,JPEG不適合用來存儲企業Logo、線框類的圖。因為有損壓縮會導致圖片模糊,而直接色的選用,又會導致圖片文件較GIF更大。
(4)PNG-8是無損的、使用索引色的點陣圖。PNG是一種比較新的圖片格式,PNG-8是非常好的GIF格式替代者,在可能的情況下,應該盡可能的使用PNG-8而不是GIF,因為在相同的圖片效果下,PNG-8具有更小的文件體積。除此之外,PNG-8還支持透明度的調節,而GIF並不支持。除非需要動畫的支持,否則沒有理由使用GIF而不是PNG-8。
(5)PNG-24是無損的、使用直接色的點陣圖。PNG-24的優點在於它壓縮了圖片的數據,使得同樣效果的圖片,PNG-24格式的文件大小要比BMP小得多。當然,PNG24的圖片還是要比JPEG、GIF、PNG-8大得多。
(6)SVG是無損的矢量圖。SVG是矢量圖意味着SVG圖片由直線和曲線以及繪制它們的方法組成。當放大SVG圖片時,看到的還是線和曲線,而不會出現像素點。這意味着SVG圖片在放大時,不會失真,所以它非常適合用來繪制Logo、Icon等。
(7)WebP是谷歌開發的一種新圖片格式,WebP是同時支持有損和無損壓縮的、使用直接色的點陣圖。從名字就可以看出來它是為Web而生的,什么叫為Web而生呢?就是說相同質量的圖片,WebP具有更小的文件體積。現在網站上充滿了大量的圖片,如果能夠降低每一個圖片的文件大小,那么將大大減少瀏覽器和服務器之間的數據傳輸量,進而降低訪問延遲,提升訪問體驗。目前只有Chrome瀏覽器和Opera瀏覽器支持WebP格式,兼容性不太好。
- 在無損壓縮的情況下,相同質量的WebP圖片,文件大小要比PNG小26%;
- 在有損壓縮的情況下,具有相同圖片精度的WebP圖片,文件大小要比JPEG小25%~34%;
- WebP圖片格式支持圖片透明度,一個無損壓縮的WebP圖片,如果要支持透明度只需要22%的格外文件大小。
六、Webpack優化
1. 如何提⾼webpack的打包速度?
(1)優化 Loader
對於 Loader 來說,影響打包效率首當其沖必屬 Babel 了。因為 Babel 會將代碼轉為字符串生成 AST,然后對 AST 繼續進行轉變最后再生成新的代碼,項目越大,轉換代碼越多,效率就越低。當然了,這是可以優化的。
首先我們優化 Loader 的文件搜索范圍
module.exports = {
module: {
rules: [
{
// js 文件才使用 babel
test: /\.js$/,
loader: 'babel-loader',
// 只在 src 文件夾下查找
include: [resolve('src')],
// 不會去查找的路徑
exclude: /node_modules/
}
]
}
}
對於 Babel 來說,希望只作用在 JS 代碼上的,然后 node_modules 中使用的代碼都是編譯過的,所以完全沒有必要再去處理一遍。
當然這樣做還不夠,還可以將 Babel 編譯過的文件緩存起來,下次只需要編譯更改過的代碼文件即可,這樣可以大幅度加快打包時間
loader: 'babel-loader?cacheDirectory=true'
(2)HappyPack
受限於 Node 是單線程運行的,所以 Webpack 在打包的過程中也是單線程的,特別是在執行 Loader 的時候,長時間編譯的任務很多,這樣就會導致等待的情況。
HappyPack 可以將 Loader 的同步執行轉換為並行的,這樣就能充分利用系統資源來加快打包效率了
module: {
loaders: [
{
test: /\.js$/,
include: [resolve('src')],
exclude: /node_modules/,
// id 后面的內容對應下面
loader: 'happypack/loader?id=happybabel'
}
]
},
plugins: [
new HappyPack({
id: 'happybabel',
loaders: ['babel-loader?cacheDirectory'],
// 開啟 4 個線程
threads: 4
})
]
(3)DllPlugin
DllPlugin 可以將特定的類庫提前打包然后引入。這種方式可以極大的減少打包類庫的次數,只有當類庫更新版本才有需要重新打包,並且也實現了將公共代碼抽離成單獨文件的優化方案。DllPlugin的使用方法如下:
// 單獨配置在一個文件中
// webpack.dll.conf.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
// 想統一打包的類庫
vendor: ['react']
},
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].dll.js',
library: '[name]-[hash]'
},
plugins: [
new webpack.DllPlugin({
// name 必須和 output.library 一致
name: '[name]-[hash]',
// 該屬性需要與 DllReferencePlugin 中一致
context: __dirname,
path: path.join(__dirname, 'dist', '[name]-manifest.json')
})
]
}
然后需要執行這個配置文件生成依賴文件,接下來需要使用 DllReferencePlugin 將依賴文件引入項目中
// webpack.conf.js
module.exports = {
// ...省略其他配置
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
// manifest 就是之前打包出來的 json 文件
manifest: require('./dist/vendor-manifest.json'),
})
]
}
(4)代碼壓縮
在 Webpack3 中,一般使用 UglifyJS 來壓縮代碼,但是這個是單線程運行的,為了加快效率,可以使用 webpack-parallel-uglify-plugin 來並行運行 UglifyJS,從而提高效率。
在 Webpack4 中,不需要以上這些操作了,只需要將 mode 設置為 production 就可以默認開啟以上功能。代碼壓縮也是我們必做的性能優化方案,當然我們不止可以壓縮 JS 代碼,還可以壓縮 HTML、CSS 代碼,並且在壓縮 JS 代碼的過程中,我們還可以通過配置實現比如刪除 console.log 這類代碼的功能。
(5)其他
可以通過一些小的優化點來加快打包速度
resolve.extensions:用來表明文件后綴列表,默認查找順序是['.js', '.json'],如果你的導入文件沒有添加后綴就會按照這個順序查找文件。我們應該盡可能減少后綴列表長度,然后將出現頻率高的后綴排在前面resolve.alias:可以通過別名的方式來映射一個路徑,能讓 Webpack 更快找到路徑module.noParse:如果你確定一個文件下沒有其他依賴,就可以使用該屬性讓 Webpack 不掃描該文件,這種方式對於大型的類庫很有幫助
2. 如何減少 Webpack 打包體積
(1)按需加載
在開發 SPA 項目的時候,項目中都會存在很多路由頁面。如果將這些頁面全部打包進一個 JS 文件的話,雖然將多個請求合並了,但是同樣也加載了很多並不需要的代碼,耗費了更長的時間。那么為了首頁能更快地呈現給用戶,希望首頁能加載的文件體積越小越好,這時候就可以使用按需加載,將每個路由頁面單獨打包為一個文件。當然不僅僅路由可以按需加載,對於 loadash 這種大型類庫同樣可以使用這個功能。
按需加載的代碼實現這里就不詳細展開了,因為鑒於用的框架不同,實現起來都是不一樣的。當然了,雖然他們的用法可能不同,但是底層的機制都是一樣的。都是當使用的時候再去下載對應文件,返回一個 Promise,當 Promise 成功以后去執行回調。
(2)Scope Hoisting
Scope Hoisting 會分析出模塊之間的依賴關系,盡可能的把打包出來的模塊合並到一個函數中去。
比如希望打包兩個文件:
// test.js
export const a = 1
// index.js
import { a } from './test.js'
對於這種情況,打包出來的代碼會類似這樣:
[
/* 0 */
function (module, exports, require) {
//...
},
/* 1 */
function (module, exports, require) {
//...
}
]
但是如果使用 Scope Hoisting ,代碼就會盡可能的合並到一個函數中去,也就變成了這樣的類似代碼:
[
/* 0 */
function (module, exports, require) {
//...
}
]
這樣的打包方式生成的代碼明顯比之前的少多了。如果在 Webpack4 中你希望開啟這個功能,只需要啟用 optimization.concatenateModules 就可以了:
module.exports = {
optimization: {
concatenateModules: true
}
}
(3)Tree Shaking
Tree Shaking 可以實現刪除項目中未被引用的代碼,比如:
// test.js
export const a = 1
export const b = 2
// index.js
import { a } from './test.js'
對於以上情況,test 文件中的變量 b 如果沒有在項目中使用到的話,就不會被打包到文件中。
如果使用 Webpack 4 的話,開啟生產環境就會自動啟動這個優化功能。
3. 如何⽤webpack來優化前端性能?
⽤webpack優化前端性能是指優化webpack的輸出結果,讓打包的最終結果在瀏覽器運⾏快速⾼效。
- 壓縮代碼:刪除多余的代碼、注釋、簡化代碼的寫法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUglifyPlugin 來壓縮JS⽂件, 利⽤ cssnano (css-loader?minimize)來壓縮css
- 利⽤CDN加速: 在構建過程中,將引⽤的靜態資源路徑修改為CDN上對應的路徑。可以利⽤webpack對於 output 參數和各loader的 publicPath 參數來修改資源路徑
- Tree Shaking: 將代碼中永遠不會⾛到的⽚段刪除掉。可以通過在啟動webpack時追加參數 --optimize-minimize 來實現
- Code Splitting: 將代碼按路由維度或者組件分塊(chunk),這樣做到按需加載,同時可以充分利⽤瀏覽器緩存
- 提取公共第三⽅庫: SplitChunksPlugin插件來進⾏公共模塊抽取,利⽤瀏覽器緩存可以⻓期緩存這些⽆需頻繁變動的公共代碼
4. 如何提⾼webpack的構建速度?
- 多⼊⼝情況下,使⽤ CommonsChunkPlugin 來提取公共代碼
- 通過 externals 配置來提取常⽤庫
- 利⽤ DllPlugin 和 DllReferencePlugin 預編譯資源模塊 通過 DllPlugin 來對那些我們引⽤但是絕對不會修改的npm包來進⾏預編譯,再通過 DllReferencePlugin 將預編譯的模塊加載進來。
- 使⽤ Happypack 實現多線程加速編譯
- 使⽤ webpack-uglify-parallel 來提升 uglifyPlugin 的壓縮速度。 原理上 webpack-uglify-parallel 采⽤了多核並⾏壓縮來提升壓縮速度
- 使⽤ Tree-shaking 和 Scope Hoisting 來剔除多余代碼
