webpack是開發工具,面試考點重點在配置和使用,原理理解不需要太深。
一、基本配置
1、拆分配置和merge
將公共配置跟dev和prod的配置拆分,然後通過webpack-merge對配置進行整合。
2、啟動本地服務
dev環境啟動devserver配置。
3、處理ES6
使用babel-loader,針對對應目錄的js進行程式碼轉換。
4、處理樣式
使用postcss-loader、css-loader、style-loader等,這裡有個考點是loader的執行順序是從後往前執行。
5、處理圖片
在dev環境直接使用file-loader進行圖片的直接引用,prod環境會使用url-loader對小圖片進行base64編碼。
6、模組化
webpack天生支援模組化。
二、高階配置
1、配置多入口
entry設定多入口檔案,在output輸出檔案使用[name]根據entry的key動態生成輸出檔名
plugins要設定多個HtmlWebpackPlugin,根據多入口生成多個html檔案,同時要設定chunks,來引入相應入口檔案,如果不設定則會把全部入口檔案都引入 。
2、每次打包清除dist原有檔案
在plugins使用new CleanWebpackPlugin(),會預設清空 output.path 指定的資料夾內容。
3、抽離css檔案
mini-css-extract-plugin使用這個外掛的loder來替換style-loader。
在plugins裡面配置mini-css-extract-plugin的filename,用於設定存放抽離的css具體目錄和名字。
使用webpack的optimization的minimizer加入 terser-webpack-plugin 和 optimize-css-assets-webpack 外掛來壓縮抽離的css。
4、抽離公共程式碼
使用webpack的optimization的splitChunks
optimization:{ splitChunks:{ chunks: 'all', // 快取分組 cacheGroups:{ //第三方模組 vendor:{ name:'vendor', // chunk 名稱 priority:1, // 許可權最高,優先抽離,重要!! test:/node_modules/, // 匹配目錄規則 minSize:0, // 大小限制 minChunks:1, // 最少複用過幾次 } //公共模組 common:{ name:'common', // chunk 名稱 priority:0, // 優先順序 minSize:0, // 公共模組的大小限制 minChunks:2, // 公共模組最少複用過幾次 } } } }
5、非同步載入js
import語法webpack天生支援非同步載入。
非同步載入會產生單獨的chunk。
6、處理JSX
安裝配置 @babel/preset-react 即可。
7、處理VUE檔案
安裝配置 vue-loader 即可
三、優化構建速度
1、優化 babel-loader(可用於生產)
{ test: /\.js$/, use: ['babel-loader?cacheDirectory'], // 開啟快取 include: path.resolve(__dirname, 'src'), // 明確範圍 // 排除範圍,include 和 exclude 兩者選一個即可 // exclude: path.resolve(__dirname, 'node_modules') }
開啟快取後,es6沒有改的會使用快取不會重新編譯
設定使用範圍可以縮小編譯量
2、IgnorePlugin(可用於生產)
忽略正則匹配的 js
3、noParse(可用於生產)
不去解析正則匹配的庫的依賴
4、happyPack(可用於生產)
JS單執行緒,使用happyPack開啟多程式打包,提高構建速度,特別是多核CPU。
5、ParallelUglifyPlugin(只用在生產)
webpack內建Uglify工具壓縮JS,開啟多程式壓縮JS,原理和happyPack相同。
new ParallelUglifyPlugin({ // 傳遞給UglifyJS的引數,值不過開啟了多程式壓縮 uglifyJS:{ output:{ beautify:false, // 最緊湊的輸出 comments:false, // 刪除所有註釋 }, compress:{ // 刪除所有的 console 語句,可以相容ie drop_console: true, // 內嵌定義了但是隻用到一次的變數 collapse_vars: true, // 提取出出現多次但是沒有定義成變數去引用的靜態值 reduce_vars: true, } } })
關於開啟多程式要按需使用,
專案大,打包速度慢,開啟多程式提高速度
專案小,打包速度快,開啟多程式反而降低速度
6、自動重新整理(不可用於生產)
{ watch:true, // 開啟監聽,預設 false // 注意,開啟監聽之後,webpack-dev-server 會自動開啟重新整理瀏覽器 // 監聽配置 watchOptions:{ ignored: /node_modules/, // 忽略內容 // 監聽到變化發生後等300ms再去執行動作,防止檔案更新太快導致重新編譯頻率太高 aggregateTimeout:300, // 預設為300ms // 判斷檔案是否發生變化時通過不停的去詢問系統指定檔案有沒變化實現的 poll:1000, // 預設每隔1000ms詢問一次 } }
以上配置一般開發不會配置到,因為開啟webpack-dev-server會自動帶上自動重新整理的配置。以上配置是為了理解自動重新整理有哪些內容。
7、熱更新(不可用於生產)
自動重新整理:整個網頁全部重新整理,速度較慢,狀態會丟失
熱更新:新程式碼生效,網頁不重新整理,狀態不丟失
熱更新使用外掛:HotModuleReplacementPlugin
在entry入口中要新增兩行配置:
entry:{ index:[ 'webpack-dev-server/client?http://localhost:8080', 'webpack/hot/dev-server', path.join(srcPath,'index.js') ] }
然後在plugin中使用HotModuleReplacementPlugin外掛。
最後在devServer中增加一個屬性:hot:true,開啟熱更新。
配置熱更新後,需要增加需要熱更新模組的邏輯程式碼,如下
if(module.hot){ moudule.hot.accept(['./math'],()=>{ // 熱更新模組裡面的回撥函式 }) }
8、DllPlugin 動態連結庫(不可用於生產)
使用前提:
前端框架如vue、react體積大,構建慢,比較穩定,不常升級。
同一個版本只構建一次即可,不用每次都重新構建。
webpack已內建DllPlugin支援。
使用過程:
DllPlugin先打包出dll檔案
DllReferencePlugin再使用dll檔案
四、優化產出程式碼
優化方向:體積更小,合理分包,不重複載入,速度更快,記憶體使用更少
1、小圖片Base64編碼
2、bundle加hash
3、懶載入
4、提取公共程式碼
5、IngorePlugin
6、使用CDN加速:
在output設定publicPath,會修改所有js跟css檔案新增cdn字首
在url-loader設定publicPath,可以修改圖片的cdn字首
7、使用production
開啟production模式跟development相比的差異點:
(1)自動開啟程式碼壓縮
(2)Vue React等會自動刪掉除錯程式碼(如開發環境的warning)
(3)自動啟動Tree-Shaking,必須用ES6 Module 才能讓tree-shaking生效,commonjs就不行
ES6 Module 靜態引入,編譯時引入
Commonjs動態引入,執行時引入
只有靜態引入才能進行靜態分析,實現Tree-Shaking
8、Scope Hosting
開啟之後,會將多個引用檔案合併成一個函式,減少函式作用域,程式碼體積更小,程式碼可讀性更好
如何配置:
引用ModuleConcatenationPlugin外掛,使用外掛即可開啟。
針對npm中第三方模組優先採用 jsnext:main 中指向的 ES6 模組化語法的檔案
resolve:{ mainFields: ['jsnext:main','brower','main'] }
五、babel
基本配置:.babelrc檔案
plugins是寫如何翻譯語法的外掛,presets是集合多個外掛整合的一個包。
{ "presets":[ ["@babel/preset-env"] ], "plugins":[] }
babel-polyfill:相容低版本瀏覽器,重寫低版本沒有的新語法。
core-js和regenerator兩個庫可以滿足最大多數新語法的polyfill,babel-polyfill就是前兩個庫的集合。
babel 7.4之後已經棄用了babel-polyfill,而推薦直接使用core-js和regenerator。
直接使用的弊端是由於重寫全域性方法會汙染全域性環境。
babel-runtime:
使用runtime配置,可以使polyfill重寫的方法不汙染全域性環境,相當於重新命名方法,程式碼也是自動替換成新名稱的方法。
面試真題:
1、前端程式碼為什麼要進行構建和打包?
程式碼層面:
(1)體積更小(Tree-Shaking、壓縮、合併),載入更快
(2)編輯高階語言或語法(TS、ES6+、模組化,scss)
(3)相容性和錯誤檢查(Polyfill、postcss、eslint)
研發流程:
(1)統一、高效的開發環境
(2)統一的構建流程和產出標準
(3)整合公司構建規範(提測、上線等)
2、module chunk bundle 分別什麼意思,有何區別?
module - 各個原始碼檔案,webpack 中一切皆模組
chunk - 多模組合併成的,如 entry import() splitChunk
bundle - 最終的輸出檔案
3、loader和plugin的區別?
loader模組轉換器,如 less 轉 css。說出一些常用的loader
plugin擴充套件外掛,如 HtmlWebpackPlugin。說出一些常用的plugin
4、babel和webpack的區別?
babel是JS新語法編譯工具,不關係模組化
webpack是打包構建工具,是多個 loader plugin 的集合
5、如何產出一個 lib
output:{ // lib的檔名 filename: 'lodash.js', // 輸出 lib 到dist 目錄下 path: distPath, // lib 的全域性變數名 library: 'lodash' }
6、webpack如何實現懶載入?
import()語法實現
結合 Vue React 非同步元件,結合 Vue-router React-router 非同步載入路由來簡要說下。
7、webpack常見效能優化?
將上面的三、四的優化內容根據實際效果講下
8、babel-runtime和babel-polyfill的區別?
babel-polyfill會汙染全域性
babel-runtime不會汙染全域性
產出第三方 lib 要用 babel-runtime
9、為何Proxy 不能被 polyfill?
Class 可以用 function 模擬, Promise 可以用 callback 來模擬
但是Proxy的功能用Object.defineProperty 無法模擬