本篇的做者是來自淘系技術部用戶增加前端團隊的會考古懂金融的資深前端 「空堂」。前端
最近咱們在校招前端面試衝刺互助羣裏輔導簡歷的時候,常常有同窗感嘆不知道怎麼優化項目,你們不妨嘗試下在項目中引入代碼分割的方式提高性能。react
關於 Web 應用性能優化,有一點是毫無疑問的:「頁面加載越久,用戶體驗就越差」。咱們幾乎能夠說 Web 應用性能優化的關鍵之處就在於:減小頁面初載時,所需加載資源的「數量」和「體積」。webpack
那麼當所需加載的資源數量到達多少或資源大小小於多少,咱們才能夠自信地宣稱咱們的 Web 應用擁有出色的性能呢?git
下面是我給出的一個參考值,該參考值考慮到了移動端與國外等多種訪問環境:github
或許你會以爲這個標準有點過於苛刻了,是有一點點,但爲了建立出高性能的 Web 應用,你的實際資源加載狀況應該儘量靠近這個目標。web
也許你注意到了,咱們上一節最後提到的一個指標是「代碼利用率」,你多是第一次據說這個概念,這裏我解釋一下它的計算方式:面試
代碼利用率 = 你頁面中實際被執行的代碼 / 你頁面中引入的代碼 * 100%瀏覽器
你可能會困惑在實際開發中如何獲得這個值,別擔憂,經過使用 Chrome 開發者工具(很遺憾,目前只有 Chrome 支持這一功能),你就能夠迅速對你的 Web 應用進行分析,獲得當前頁面下的代碼利用率狀態,步驟以下:性能優化
代碼分割是指,將腳本中無需當即調用的代碼在代碼構建時轉變爲異步加載的過程。微信
在 Webpack 構建時,會避免加載已聲明要異步加載的代碼,異步代碼會被單獨分離出一個文件,當代碼實際調用時被加載至頁面。
代碼分割技術的核心是「異步加載資源」,可喜的是,瀏覽器容許咱們這麼作,W3C stage 3 規範: whatwg/loader 對其進行了定義:你能夠經過 import() 關鍵字讓瀏覽器在程序執行時異步加載相關資源。
沒錯,正如你所看到的, IE 瀏覽器目前並不支持這一特性,但這並不意味着你的異步加載功能在 IE 瀏覽會失效(那太可怕了 🤦♂️),實際上,Webpack 底層幫你將異步加載的代碼抽離成一份新的文件,並在你須要時經過 JSONP 的方式去獲取文件資源,所以,你能夠在任何瀏覽器上實現代碼的異步加載,而且在未來全部瀏覽器都實現 import() 方法時平滑過渡,cool!👍
代碼分割能夠分爲「靜態分割」和「「動態」分割」兩種方式,注意這裏打了引號的 「動態」,由於實際上它並不意味着異步調用的代碼是 「動態」 生成的,咱們以後會看到 Webpack 是如何作到這一點的,在那以前,讓咱們先看看「靜態代碼分割」。
靜態代碼分割是指:在代碼中明確聲明須要異步加載的代碼。
下面 👇 的代碼說明了咱們應該如何使用這一技術:
import Listener from './listeners.js'
const getModal = () => import('./src/modal.js') Listener.on('didSomethingToWarrentModalBeingLoaded', () => { // Async fetching modal code from a separate chunk getModal().then((module) => { const modalTarget = document.getElementById('Modal') module.initModal(modalTarget) })})
const getModal = () => import('./src/modal.js')
Listener.on(
'didSomethingToWarrentModalBeingLoaded',
() => {
// Async fetching modal code from a separate chunk
getModal().then(
(module) => {
const modalTarget = document.getElementById('Modal')
module.initModal(modalTarget)
})
}
)
複製代碼
正如你所看到的:每當你調用一個聲明瞭異步加載代碼的變量時,它老是返回一個 Promise 對象。
⚠️ 注意:在 Vue 中,能夠直接使用 import() 關鍵字作到這一點,而在 React 中,你須要使用 react-loadable 去完成一樣的事。
最後,讓咱們談談什麼時候使用靜態代碼分割技術,這一技術適合如下的場景:
1. 你正在使用一個很是大的庫或框架:若是在頁面初始化時你不須要使用它,就不要在頁面初載時加載它; 2. 任何臨時的資源:指不在頁面初始化時被使用,被使用後又會當即被銷燬的資源,例如模態框,對話框,tooltip 等(任何一開始不顯示在頁面上的東西均可以有條件的加載); 3. 路由:既然用戶不會一會兒看到全部頁面,那麼只把當前頁面相關資源給用戶就是個明智的作法; 好了,如今你掌握了靜態代碼分割技術,如今讓咱們看看什麼是「「動態代」代碼分割」技術。
動態代碼分割是指:
在代碼調用時根據當前的狀態,「動態地」異步加載對應的代碼塊。
下面 👇 的代碼說明了它具體是如何被實現的:
const getTheme = (themeName) => import(`./src/themes/${themeName}`)
// using `import()` 'dynamically'
if (window.feeling.stylish) {
getTheme('stylish').then((module) => {
module.applyTheme()
})
} else if (window.feeling.trendy) {
getTheme('trendy').then((module) => {
module.applyTheme()
})
}
複製代碼
看到了嗎,咱們 「動態」 的聲明瞭咱們要異步加載的代碼塊,這是怎麼作到的?!答案出乎意料的簡單,Webpack 會在構建時將你聲明的目錄下的全部可能分離的代碼都抽象爲一個文件(這被稱爲 contextModule 模塊),所以不管你最終聲明瞭調用哪一個文件,本質上就和靜態代碼分割同樣,在請求一個早已準備好的,靜態的文件。
下面是一些使用 「動態」 代碼分割技術的場景:
1. A/B Test:你不須要在代碼中引入不須要的 UI 代碼; 2. 加載主題:根據用戶的設置,動態加載相應的主題; 3. 爲了方便 :本質上,你能夠用靜態代碼分割代替「動態」代碼分割,可是後者比前者擁有更少的代碼量;
魔術註釋是由 Webpack 提供的,能夠爲代碼分割服務的一種技術。經過在 import 關鍵字後的括號中使用指定註釋,咱們能夠對代碼分割後的 chunk 有更多的控制權,讓咱們看一個例子:
// index.js
import (
/* webpackChunkName: 「my-chunk-name」 */
'./footer'
)
複製代碼
同時,也要在 webpack.config.js 中作一些改動:
// webpack.config.js
{
output: {
filename: 「bundle.js」,
chunkFilename: 「[name].lazy-chunk.js」
}
}
複製代碼
經過這樣的配置,咱們能夠對分離出的 chunk 進行命名,這對於咱們 debug 而言很是方便。
除了上面提到過得 webpackChunkName 註釋外,Webpack 還提供了一些其餘註釋讓咱們可以對異步加載模塊擁有更多控制權,例以下方這個例子:
import (
/* webpackChunkName: 「my-chunk-name」 */
/* webpackMode: lazy */
'./someModule'
)
複製代碼
webpackMode 的默認值爲 lazy 它會使全部異步模塊都會被單獨抽離成單一的 chunk,若設置該值爲 lazy-once,Webpack 就會將全部帶有標記的異步加載模塊放在同一個 chunk 中。
經過添加 webpackPrefetch 魔術註釋,Webpack 令咱們可使用與 <link rel=「prefetch」>
相同的特性。讓瀏覽器會在 Idle 狀態時預先幫咱們加載所需的資源,善用這個技術可使咱們的應用交互變得更加流暢。
import(
/* webpackPrefetch: true */
'./someModule'
)
複製代碼
⚠️ 注意:你確保你的代碼在將來必定會用到時,再開啓該功能。
至此,咱們講解了全部有關 Code Splitting 的知識,並告訴你了一些神奇的「魔法註釋」讓你對分割後的代碼有更多的掌控,但願你能將上面的技術靈活運用在你的項目中,開發出更加激動人心,如絲般順滑的應用! Good Luck!🙌
3月25日晚 7:00 - 8:00 ,咱們有淘系前端技術部直播活動,內容包含淘系前端團隊技術體系介紹,師兄們(各類大佬)的成長之路分享,校招相關答疑等諸多幹貨,直播信息將會在下面的羣裏放出,歡迎入羣觀看。
如今是校招季,淘系前端正在幫助21屆同窗們收割大廠offer,專門建立了交流羣,能夠在這裏提問題,交流面試經驗,這裏也提供了簡歷輔導和答疑解惑等服務,掃碼或者加淘小招微信邀你入羣,微信號 taoxiaozhao233