1. 程式人生 > 實用技巧 >關於webpack模組打包工具

關於webpack模組打包工具

一、與webpack類似的工具有那些?為什麼最終選擇webpack?

 打包工具:

  • webpack
  • rollup
  • parcel

 理由:

  • webpack適用於大型複雜的前端站點構建
  • rollup適用於基礎庫的打包,如vue、react
  • parcel適用於簡單的實驗性專案,他可以滿足低門檻的快速看到效果。

    由於parcel在打包過程中給出的除錯資訊十分有限,所以一旦打包出錯難以除錯,所以不建議複雜的專案使用parcel

 外掛:

      • clean-webpack-plugin    打包前,清除檔案
      • copy-webpack-Plugin   把資料夾下的檔案拷貝到dist目錄下
      • BannerPlugin()   每個打包檔案的頭部新增版權
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')

  plugins: [
    new CleanWebpackPlugin({
      cleanAfterEveryBuildPatterns: ['dist']  //
打包前 清除dist檔案 }), new copyWebpackPlugin([ { from: 'gemfolder', to: './'} // 把gemfolder資料夾下的檔案拷貝到dist目錄下 ]), new webpack.BannerPlugin('make by gemma 2020/03/29') // 給每個打包檔案的頭部新增版權 ],

二、有哪些常見的Loader(載入)?他們是解決什麼問題的?

  • file-loader:把檔案輸出到一個資料夾中,在程式碼中通過相對 URL 去引用輸出的檔案
  • url-loader:和 file-loader 類似,但是能在檔案很小的情況下以 base64 的方式把檔案內容注入到程式碼中去
  • source-map-loader:載入額外的 Source Map 檔案,以方便斷點除錯
  • image-loader:載入並且壓縮圖片檔案
  • babel-loader:把 ES6 轉換成 ES5
  • css-loader:載入 CSS,支援模組化、壓縮、檔案匯入等特性
  • style-loader:把 CSS 程式碼注入到 JavaScript 中,通過 DOM 操作去載入 CSS。
  • eslint-loader:通過 ESLint 檢查 JavaScript 程式碼

三、有哪些常見的Plugin?他們是解決什麼問題的?

  • define-plugin:定義環境變數
  • commons-chunk-plugin:提取公共程式碼
  • uglifyjs-webpack-plugin:通過UglifyES壓縮ES6程式碼

四、Loader和Plugin的不同?

不同的作用

  • Loader直譯為"載入器"。Webpack將一切檔案視為模組,但是webpack原生是隻能解析js檔案,如果想將其他檔案也打包的話,就會用到loader。 所以Loader的作用是讓webpack擁有了載入和解析非JavaScript檔案的能力。
  • Plugin直譯為"外掛"。Plugin可以擴充套件webpack的功能,讓webpack具有更多的靈活性。 在 Webpack 執行的生命週期中會廣播出許多事件,Plugin 可以監聽這些事件,在合適的時機通過 Webpack 提供的 API 改變輸出結果。

不同的用法

  • Loader在module.rules中配置,也就是說他作為模組的解析規則而存在。 型別為陣列,每一項都是一個Object,裡面描述了對於什麼型別的檔案(test),使用什麼載入(loader)和使用的引數(options
  • Plugin在plugins中單獨配置。 型別為陣列,每一項是一個plugin的例項,引數都通過建構函式傳入。

五、webpack的構建流程是什麼?從讀取配置到輸出檔案這個過程儘量說全

Webpack 的執行流程是一個序列的過程,從啟動到結束會依次執行以下流程:

  1. 初始化引數:從配置檔案和 Shell 語句中讀取與合併引數,得出最終的引數;
  2. 開始編譯:用上一步得到的引數初始化 Compiler 物件,載入所有配置的外掛,執行物件的 run 方法開始執行編譯;
  3. 確定入口:根據配置中的 entry 找出所有的入口檔案;
  4. 編譯模組:從入口檔案出發,呼叫所有配置的 Loader 對模組進行翻譯,再找出該模組依賴的模組,再遞迴本步驟直到所有入口依賴的檔案都經過了本步驟的處理;
  5. 完成模組編譯:在經過第4步使用 Loader 翻譯完所有模組後,得到了每個模組被翻譯後的最終內容以及它們之間的依賴關係;
  6. 輸出資源:根據入口和模組之間的依賴關係,組裝成一個個包含多個模組的 Chunk,再把每個 Chunk 轉換成一個單獨的檔案加入到輸出列表,這步是可以修改輸出內容的最後機會;
  7. 輸出完成:在確定好輸出內容後,根據配置確定輸出的路徑和檔名,把檔案內容寫入到檔案系統。

在以上過程中,Webpack 會在特定的時間點廣播出特定的事件,外掛在監聽到感興趣的事件後會執行特定的邏輯,並且外掛可以呼叫 Webpack 提供的 API 改變 Webpack 的執行結果。


六、是否寫過Loader和Plugin?描述一下編寫loader或plugin的思路?

Loader像一個"翻譯官"把讀到的原始檔內容轉義成新的檔案內容,並且每個Loader通過鏈式操作,將原始檔一步步翻譯成想要的樣子。

編寫Loader時要遵循單一原則,每個Loader只做一種"轉義"工作。 每個Loader的拿到的是原始檔內容(source),可以通過返回值的方式將處理後的內容輸出,也可以呼叫this.callback()方法,將內容返回給webpack。 還可以通過this.async()生成一個callback函式,再用這個callback將處理後的內容輸出出去。 此外webpack還為開發者準備了開發loader的工具函式集——loader-utils

相對於Loader而言,Plugin的編寫就靈活了許多。 webpack在執行的生命週期中會廣播出許多事件,Plugin 可以監聽這些事件,在合適的時機通過 Webpack 提供的 API 改變輸出結果。

七、webpack的熱更新是如何做到的?說明其原理?

webpack的熱更新又稱熱替換(Hot Module Replacement),縮寫為HMR。 這個機制可以做到不用重新整理瀏覽器而將新變更的模組替換掉舊的模組。

首先要知道server端和client端都做了處理工作

    1. 第一步,在 webpack 的 watch 模式下,檔案系統中某一個檔案發生修改,webpack 監聽到檔案變化,根據配置檔案對模組重新編譯打包,並將打包後的程式碼通過簡單的 JavaScript 物件儲存在記憶體中。
    2. 第二步是 webpack-dev-server 和 webpack 之間的介面互動,而在這一步,主要是 dev-server 的中介軟體 webpack-dev-middleware 和 webpack 之間的互動,webpack-dev-middleware 呼叫 webpack 暴露的 API對程式碼變化進行監控,並且告訴 webpack,將程式碼打包到記憶體中。
    3. 第三步是 webpack-dev-server 對檔案變化的一個監控,這一步不同於第一步,並不是監控程式碼變化重新打包。當我們在配置檔案中配置了devServer.watchContentBase 為 true 的時候,Server 會監聽這些配置資料夾中靜態檔案的變化,變化後會通知瀏覽器端對應用進行 live reload。注意,這兒是瀏覽器重新整理,和 HMR 是兩個概念。
    4. 第四步也是 webpack-dev-server 程式碼的工作,該步驟主要是通過 sockjs(webpack-dev-server 的依賴)在瀏覽器端和服務端之間建立一個 websocket 長連線,將 webpack 編譯打包的各個階段的狀態資訊告知瀏覽器端,同時也包括第三步中 Server 監聽靜態檔案變化的資訊。瀏覽器端根據這些 socket 訊息進行不同的操作。當然服務端傳遞的最主要資訊還是新模組的 hash 值,後面的步驟根據這一 hash 值來進行模組熱替換。
    5. webpack-dev-server/client 端並不能夠請求更新的程式碼,也不會執行熱更模組操作,而把這些工作又交回給了 webpack,webpack/hot/dev-server 的工作就是根據 webpack-dev-server/client 傳給它的資訊以及 dev-server 的配置決定是重新整理瀏覽器呢還是進行模組熱更新。當然如果僅僅是重新整理瀏覽器,也就沒有後面那些步驟了。
    6. HotModuleReplacement.runtime 是客戶端 HMR 的中樞,它接收到上一步傳遞給他的新模組的 hash 值,它通過 JsonpMainTemplate.runtime 向 server 端傳送 Ajax 請求,服務端返回一個 json,該 json 包含了所有要更新的模組的 hash 值,獲取到更新列表後,該模組再次通過 jsonp 請求,獲取到最新的模組程式碼。這就是上圖中 7、8、9 步驟。
    7. 而第 10 步是決定 HMR 成功與否的關鍵步驟,在該步驟中,HotModulePlugin 將會對新舊模組進行對比,決定是否更新模組,在決定更新模組後,檢查模組之間的依賴關係,更新模組的同時更新模組間的依賴引用。
    8. 最後一步,當 HMR 失敗後,回退到 live reload 操作,也就是進行瀏覽器重新整理來獲取最新打包程式碼。

八、如何利用webpack來優化前端效能?(提高效能和體驗)

用webpack優化前端效能是指優化webpack的輸出結果,讓打包的最終結果在瀏覽器執行快速高效。

  • 壓縮程式碼。刪除多餘的程式碼、註釋、簡化程式碼的寫法等等方式。可以利用webpack的UglifyJsPluginParallelUglifyPlugin來壓縮JS檔案, 利用cssnano(css-loader?minimize)來壓縮css
  • 利用CDN加速。在構建過程中,將引用的靜態資源路徑修改為CDN上對應的路徑。可以利用webpack對於output引數和各loader的publicPath引數來修改資源路徑
  • 刪除死程式碼(Tree Shaking)。將程式碼中永遠不會走到的片段刪除掉。可以通過在啟動webpack時追加引數--optimize-minimize來實現
  • 提取公共程式碼。

九、如何提高webpack的構建速度?

    1. 多入口情況下,使用CommonsChunkPlugin來提取公共程式碼
    2. 通過externals配置來提取常用庫
    3. 利用DllPluginDllReferencePlugin預編譯資源模組 通過DllPlugin來對那些我們引用但是絕對不會修改的npm包來進行預編譯,再通過DllReferencePlugin將預編譯的模組載入進來。
    4. 使用Happypack實現多執行緒加速編譯
    5. 使用webpack-uglify-parallel來提升uglifyPlugin的壓縮速度。 原理上webpack-uglify-parallel採用了多核並行壓縮來提升壓縮速度
    6. 使用Tree-shakingScope Hoisting來剔除多餘程式碼

十、怎麼配置單頁應用?怎麼配置多頁應用?

單頁應用可以理解為webpack的標準模式,直接在entry中指定單頁應用的入口即可,這裡不再贅述

多頁應用的話,可以使用webpack的AutoWebPlugin來完成簡單自動化的構建,但是前提是專案的目錄結構必須遵守他預設的規範。 多頁應用中要注意的是:

  • 每個頁面都有公共的程式碼,可以將這些程式碼抽離出來,避免重複的載入。比如,每個頁面都引用了同一套css樣式表
  • 隨著業務的不斷擴充套件,頁面可能會不斷的追加,所以一定要讓入口的配置足夠靈活,避免每次新增新頁面還需要修改構建配置

十一、npm打包時需要注意哪些?

Npm是目前最大的 JavaScript 模組倉庫,裡面有來自全世界開發者上傳的可複用模組。你可能只是JS模組的使用者,但是有些情況你也會去選擇上傳自己開發的模組。 關於NPM模組上傳的方法可以去官網上進行學習,這裡只講解如何利用webpack來構建。

NPM模組需要注意以下問題:

  1. 要支援CommonJS模組化規範,所以要求打包後的最後結果也遵守該規則。
  2. Npm模組使用者的環境是不確定的,很有可能並不支援ES6,所以打包的最後結果應該是採用ES5編寫的。並且如果ES5是經過轉換的,請最好連同SourceMap一同上傳。
  3. Npm包大小應該是儘量小(有些倉庫會限制包大小)
  4. 釋出的模組不能將依賴的模組也一同打包,應該讓使用者選擇性的去自行安裝。這樣可以避免模組應用者再次打包時出現底層模組被重複打包的情況。
  5. UI元件類的模組應該將依賴的其它資原始檔,例如.css檔案也需要包含在釋出的模組裡。

十二、如何在vue專案中實現按需載入?

Vue UI元件庫的按需載入為了快速開發前端專案,經常會引入現成的UI元件庫如ElementUI、iView等,但是他們的體積和他們所提供的功能一樣,是很龐大的。 而通常情況下,我們僅僅需要少量的幾個元件就足夠了,但是我們卻將龐大的元件庫打包到我們的原始碼中,造成了不必要的開銷。

單頁應用的按需載入現在很多前端專案都是通過單頁應用的方式開發的,但是隨著業務的不斷擴充套件,會面臨一個嚴峻的問題——首次載入的程式碼量會越來越多,影響使用者的體驗。

通過import(*)語句來控制載入時機,webpack內建了對於import(*)的解析,會將import(*)中引入的模組作為一個新的入口在生成一個chunk。 當代碼執行到import(*)語句時,會去載入Chunk對應生成的檔案。import()會返回一個Promise物件,所以為了讓瀏覽器支援,需要事先注入Promise polyfill

十三、總結

  Webpack是很好的前端資源載入和打包工具,在webpack裡一切皆模組,很好地處理檔案之間的依賴關係,這裡我們介紹的是些理論性的知識,瞭解基本概念,知道整個流程是怎麼樣的,webpack是序列流水線執行的,工作期間會有很多廣播事件,來供外掛使用