聊一聊 webpack 中的 preloading 和 Prefetching
聊一聊 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 的方式去載入非同步檔案更合適一些,不過要注意瀏覽器的相容性問題。
完, 如有不恰當之處,歡迎指正哦