聊一聊 webpack 中的 preloading 和 Prefetching 提到 Preloading 和 Prefetching 就不得不先說一下代碼分割,通過下麵的例子我們來說明為什麼需要代碼分割? 在首次訪問時, index.js 文件的大小為 2 MB,需要載入的大小是 2 MB 業務代 ...
聊一聊 webpack 中的 preloading 和 Prefetching
提到 Preloading 和 Prefetching 就不得不先說一下代碼分割,通過下麵的例子我們來說明為什麼需要代碼分割?
// index.js
import _ from 'lodash'; // 假設大小為 1 MB
業務代碼 // 假設大小為 1 MB
- 在首次訪問時, index.js 文件的大小為 2 MB,需要載入的大小是 2 MB
- 業務代碼改變用戶再次訪問時,index.js 的大小為 2 MB,需要載入的大小還是 2 MB
現在進行代碼分割:
// src/index.js
業務代碼 // 假設大小為 1 MB
// src/lodash.js
import _ from 'lodash';
window._ = _; // 以後在其它文件中使用 _ 就可以使用 lodash 庫了。
首次訪問時,index.js 1 MB,lodash.js 1 MB , 需要載入的大小是 2 MB,而且此時可以進行並行載入,速度一般會比上面的快。
業務代碼改變用戶再次訪問時,index.js 1 MB,由於 lodash.js 文件並沒有發生變化,所以無需再次載入,因為瀏覽器的緩存中有,所以此次只需載入 1 MB。
從上面的例子可以看出,代碼分割提高了性能,但是第一次訪問的時間並沒有減少多少,webpack 想讓第一次訪問的時候也得到很大的優化。
我們先從 webpack 中的 SplitChunkPlugin 的預設配置中找到答案,
optimazition: {
splitChunks: {
chunks: 'async', // 非同步代碼才會進行代碼分割
...
}
}
我們可以看到,chunks
的配置是 async
,只有當非同步時才會進行代碼分割。
webpack 為什麼要這樣預設設置呢?
還是從下麵的例子來說明:
創建一個div元素,併在頁面上顯示出來。
// index.js
document.addEventListener('click', () => {
const div = document.createElement('div');
div.innerHTML = 'hello webpack';
document.body.appendChild(div);
});
思考上面的代碼寫的有問題嗎?還有優化的空間嗎?
現在我們將上面的代碼打包在瀏覽器中運行,在瀏覽器中 按 Ctrl + Shift + P
,然後在彈出的對話框中輸入 coverage
,點擊回車,然後再點擊下麵的小黑原點,小黑圓點變成紅圓點之後,刷新頁面,會出現下圖所示的頁面:
從紅色的方框中可以看出當前載入的文件中在當前頁面中的利用率為 74.6%
。
仔細分析一下上面的代碼,在回調函數中的下麵三行代碼只有在點擊頁面之後才會有用,因此載入頁面時沒必要載入它們。
const div = document.createElement('div');
div.innerHTML = 'hello webpack';
document.body.appendChild(div);
現在我們換一種寫法,將它們非同步載入進來,現在新建一個 click 文件:
// click.js
function handleClick() {
const div = document.createElement('div');
div.innerHTML = 'hello webpack';
document.body.appendChild(div);
}
export default handleClick;
然後改寫 index.js 文件:
document.addEventListener('click', () => {
import('./click.js').then(({default: func}) => {
func();
})
});
這時候將非同步代碼寫在一個單獨的文件中,只有當點擊頁面時才會去載入 click.js 這個文件。
現在再看此時的代碼利用率為 75%
有了一點提升,設想如果非同步載入在的代碼比較大的話,提升的會比較多。
現在我們就看出來 webopack 為什麼要使用 chunks: 'async'
這樣的預設配置了。
webpack 優化的側重點是代碼的使用率而不是緩存,只是使用緩存的方式來優化意義是不大的,通過非同步的方式提高代碼的利用率才能比較大程度地提高網站的性能。
這也是為什麼老提倡寫非同步代碼的原因。
現在又有一個問題,只有當用戶點擊頁面時才會載入 click.js
這個文件,那麼如果這個文件很大,那載入的時間也會很長呀,用戶體驗也不高呀。
那這個問題應該如何解決呢?
有些小伙伴可能會想,能不能在載入完頁面網路空閑的時候先把這些文件載入進來呀,真聰明,這就是接下來要講的 Preloading 和 Prefetching。
Prefetching
使用方法也比較簡單,就是在要非同步載入的文件前面加上
/* webpackPrefetch: true */
這個 magic comment 即可。document.addEventListener('click', () => { import(/* webpackPrefetch: true */ './click.js').then(({default: func}) => { func(); }) });
上圖中的 0.js 是 click.js 打包之後,可以看出在頁面載入完之後的空閑時間還沒有點擊頁面時已經載入了 0.js ,當點擊頁面時,0.js 直接從緩存中讀取,因此耗時非常短。
Preloading 和 Prefetching 有什麼區別?
兩者的最大區別在於,Prefetching 是在核心代碼載入完成之後帶寬空閑的時候再去載入,而 Preloading 是和核心代碼文件一起去載入的。
因此,使用 Prefetching 的方式去載入非同步文件更合適一些,不過要註意瀏覽器的相容性問題。
完, 如有不恰當之處,歡迎指正哦。