webpack 是一個現代 JavaScript
應用程序的靜態模塊打包器 (module bundler)
。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖 (dependency graph)
,其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle
。html
前端工程化演進到今天,webpack 作了很大的貢獻。項目工程化帶來了不少便捷,咱們再也不須要手動處理依賴之間的關係,也能夠更方便的使用更多好用的框架,咱們能夠更關注業務自己,集中精力打造咱們的產品。前端
在 webpack 中,使用懶加載或者按需加載,是一種很好的優化網頁或應用的方式。這種方式其實是先把你的代碼在一些邏輯斷點處分離開,而後在一些代碼塊中完成某些操做後,當即引用或即將引用另一些新的代碼塊。這樣加快了應用的初始加載速度,減輕了它的整體體積,由於某些代碼塊可能永遠不會被加載。webpack
那麼,接下來讓咱們來探究一下 webpack 對懶加載的模塊作了哪些工做吧~git
咱們先假設咱們在完成一個真實的項目,這個項目中有上傳下載功能。github
下載功能通常是打開一個連接,因此咱們直接實如今主包中。而上傳功能可能會使用到第三方 sdk,咱們使用懶加載進行加載。只有在用戶點擊上傳時,咱們纔會加載這個具有上傳功能的包,來進行上傳。web
上傳下載功能可能會使用到一些第三方 sdk,而這些第三方 sdk 的體積每每很是大,而且這個功能因此這個功能作成懶加載實現是合理的。爲了演示差異,咱們這裏將「下載」和「上傳」兩個功能作區分。npm
咱們先搭建一個基礎的 webpack 配置,讓其支持懶加載配置,而後咱們直接經過打包後的代碼來看看懶加載實現的效果。咱們須要有個基礎目錄配置,項目 Demo 目錄結構以下:json
文件/目錄 | 說明 |
---|---|
src |
入口文件、下載模塊、上傳模塊 |
index.html |
html 模板文件 |
webpack.config.js |
webpack 配置文件 |
package.json |
項目說明文件 |
咱們先來看看咱們的功能代碼實現吧,分別是 download.js
、upload.js
、index.js
。前端工程化
// ./src/download.js const download = () => { console.log("download start"); console.log("schedule download sdk"); console.log("download"); } export default download;
// ./src/upload.js const upload = () => { console.log("upload start"); console.log("schedule upload sdk"); console.log("upload"); } export default upload;
// ./src/index.js import download from "./download"; console.log("initial page"); async function handlerUploadClick() { // 動態加載 upload 模塊,該模塊的 default 屬性就是 upload 方法 const { default: upload } = await import("./upload"); // 調用 upload 方法 upload(); } async function handlerDownloadClick() { download(); } // 點擊 upload 按鈕時,調用上傳方法 document.querySelector("#upload").addEventListener("click", handlerUploadClick, false) // 點擊 download 按鈕時,調用下載方法 document.querySelector("#download").addEventListener("click", handlerDownloadClick, false)
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Webpack LazyLoad</title> </head> <body> <section> <h1>Home</h1> <button id="upload">Upload</button> <button id="download">Download</button> </section> </body> </html>
在咱們的功能代碼實現中,咱們實現了一個 html
網頁,其中有兩個按鈕,一個是上傳按鈕,一個是下載按鈕。數組
功能實現後,咱們須要配置 webpack,而後打包生成可以直接運行的項目。
咱們新建文件 webpack.config.js
進行配置,代碼實現以下:
// webpack.config.js const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const WebpackChain = require("webpack-chain"); // 使用 webpack chain 組裝配置 const chain = new WebpackChain(); // 設置入口文件爲 src/index.js chain.entry("main").add("./src/index.js").end(); // 將構建生成的文件輸出到 dist 目錄 chain.output.path(path.resolve(__dirname, "./dist")).end(); // 添加 html-webpack-plugin 插件,設置 HTML 模板文件 chain.plugin("html-webpack-plugin").use(HtmlWebpackPlugin, [{ template: path.resolve(__dirname, "./index.html") }]); // 設置 source map 生成規則 chain.devtool("cheap-source-map").end(); // 將配置轉成 webpack 可識別的配置對象 const config = chain.toConfig(); module.exports = config;
從代碼實現中能夠看出,咱們的 webpack
配置只是簡單配置了入口出口和 html
模板文件。
那麼接下來,咱們須要配置 package.json
,在 scripts
中添加啓動命令,代碼實現以下:
"scripts": { "build": "NODE_ENV=development webpack --config webpack.config.js && cd dist && anywhere" }
anywhere 是一個快速啓動http
服務的插件,可使用npm i anywhere -g
進行全局安裝。
在配置完了之後,咱們須要運行下面的命令安裝一些依賴
npm i webpack webpack-cli webpack-chain html-webpack-plugin anywhere -D
安裝依賴後,咱們就能夠準備啓動項目啦!
咱們運行 npm build
啓動項目編譯,命令行輸出將會是下面這樣(以下圖)
咱們的項目在被 webpack
打包後,輸出到了 dist
目錄,而且由 anywhere
運行了一個服務,在 8000
端口。
咱們打開瀏覽器,能夠看到下面這個頁面。(以下圖)
咱們打開控制檯,會看到咱們設置的對應輸出(以下圖)
咱們來進行一些頁面操做,咱們先點擊 Download
按鈕,會發現控制檯的輸出變成了下面這樣(以下圖)
從上圖能夠看出 download
方法被調用了,此時執行了一個下載操做(mock 操做)。
那咱們此時再點擊一下 Upload
按鈕,再觀察控制檯輸出(以下圖)
從控制檯能夠看到咱們的上傳操做被調用了,可是彷佛和下載操做沒什麼區別。
此時,咱們須要切換到 network
控制面板,查看網絡請求。(以下圖)
咱們從上圖能夠發現,在調用 upload
方法時,纔會加載 upload
方法對應的文件,從而實現懶加載。
這樣作的好處在於,能夠根據需求有效減少主包的體積,加快首屏渲染速度,減小服務器帶寬壓力。同時也減小了 JS 解析時間,提高頁面渲染速度。
懶加載的實現對前端來講,是性能優化專項必修課。能夠說,項目越複雜,那麼懶加載帶來的好處就越大。
下面咱們能夠來看看 webpack
編譯後的代碼,看看 webpack
是如何實現懶加載的。
首先,咱們查看主包文件,也就是 dist/main.js
。在這個文件裏找到咱們在 src/index.js
中實現的初始化頁面操做。(以下圖)
從上圖能夠看出,該操做直接被打包進了構建生成的 bundle
文件中。
那咱們再來看看 src/download.js
中實現的下載方法調用(以下圖)
從上圖能夠看出,handlerDownloadClick
最終調用了 _download__WEBPACK_IMPORTED_MODULE_0__.default
方法。
而這個 _download__WEBPACK_IMPORTED_MODULE_0__.default
是什麼呢?
在構建後的代碼中,找到了對這個對象的賦值操做(以下圖)
而 __webpack_require__
函數,其實就是加載 __webpack_modules__
中的對應模塊,這裏加載的對應模塊就是 "./src/download.js"
模塊。
最終,咱們在 dist/main.js
中,找到了對該模塊的定義。(以下圖)
從上圖能夠看出,對該模塊的定義,其實就是 src/download.js
的實現,被打包進了 dist/main.js
中,成爲了 __webpack_modules__
對象的一部分。
看完了 download
方法的打包實現,咱們接下來看看懶加載的 upload
是如何實現的?
咱們先找到 upload
對應的函數調用(以下圖)
從上圖能夠看出,當點擊上傳按鈕時,先使用 __webpack_require__.e
方法進行了一個加載操做,咱們來看看這個方法所作的事情。
該方法先拼接了這個模塊對應的絕對路徑(以下圖)
這個路徑的文件其實就是咱們打包後生成在 dist
目錄的文件(以下圖)
而後使用動態插入 script
標籤的方式,將對應的腳本文件插入到文檔中。(以下圖)
當 upload
對應的文件被插入後,將會自動執行。src_upload_js.js
腳本文件中將會執行 webpackJsonpCallback
方法,執行後將會在 window
的 webpackChunklazyload
數組插入剛纔懶加載的模塊。(以下圖)
在執行該函數後,還會把 upload
模塊註冊在 __webpack_modules__
中,後面的調用流程就和調用 download
同樣啦~(以下圖)
從一個簡單的案例,咱們瞭解到了 webpack
的懶加載實現。
webpack
的懶加載實如今打包時會將懶加載的代碼切割出去單獨打包,而後在主包中進行按需加載,最後執行調用。
咱們最後用一張圖來梳理一下懶加載的加載執行過程。(以下圖)
若是您已經看到這裏了,但願您仍是點個贊再走吧~
您的點贊是對做者的最大鼓勵,也可讓更多人看到本篇文章!
若是以爲本文對您有幫助,請幫忙在 github 上點亮 star
鼓勵一下吧!