本文已收錄在
Github
github.com/Geekhyt,感謝Star。
從頭髮的濃密程度和幹練的走路姿式我察覺到,面前坐着的這位面試官也是一把好手。javascript
我像以往同樣,準備花3分鐘的時間進行自我介紹。在此期間,個人目光被16寸的MacBook Pro所吸引,此次的自我介紹我作足了準備,頗有信心征服面試官。不出我所料,面試官被我引入了我擅長的領域。css
看來你對Webpack很熟悉,那我來考考你
(我開始熟悉的報起了菜名)html
raw-loader
:加載文件原始內容(utf-8)file-loader
:把文件輸出到一個文件夾中,在代碼中經過相對 URL 去引用輸出的文件 (處理圖片和字體)url-loader
:與 file-loader 相似,區別是用戶能夠設置一個閾值,大於閾值會交給file-loader處理,小於閾值時返回文件 base64 形式編碼 (處理圖片和字體)source-map-loader
:加載額外的 Source Map 文件,以方便斷點調試svg-inline-loader
:將壓縮後的 SVG 內容注入代碼中image-loader
:加載而且壓縮圖片文件json-loader
加載 JSON 文件(默認包含)handlebars-loader
: 將 Handlebars 模版編譯成函數並返回babel-loader
:把 ES6 轉換成 ES5ts-loader
: 將 TypeScript 轉換成 JavaScriptawesome-typescript-loader
:將 TypeScript 轉換成 JavaScript,性能優於 ts-loadersass-loader
:將SCSS/SASS代碼轉換成CSScss-loader
:加載 CSS,支持模塊化、壓縮、文件導入等特性style-loader
:把 CSS 代碼注入到 JavaScript 中,經過 DOM 操做去加載 CSSpostcss-loader
:擴展 CSS 語法,使用下一代 CSS,能夠配合 autoprefixer 插件自動補齊 CSS3 前綴eslint-loader
:經過 ESLint 檢查 JavaScript 代碼tslint-loader
:經過 TSLint檢查 TypeScript 代碼mocha-loader
:加載 Mocha 測試用例的代碼coverjs-loader
:計算測試的覆蓋率vue-loader
:加載 Vue.js 單文件組件i18n-loader
: 國際化cache-loader
: 能夠在一些性能開銷較大的 Loader 以前添加,目的是將結果緩存到磁盤裏更多 Loader 請參考官網前端
(面試官:挺好,知道的還挺多)vue
(這大兄弟好像聽上癮了,繼續開啓常規操做)java
define-plugin
:定義環境變量 (Webpack4 以後指定 mode 會自動配置)ignore-plugin
:忽略部分文件html-webpack-plugin
:簡化 HTML 文件建立 (依賴於 html-loader)web-webpack-plugin
:可方便地爲單頁應用輸出 HTML,比 html-webpack-plugin 好用uglifyjs-webpack-plugin
:不支持 ES6 壓縮 (Webpack4 之前)terser-webpack-plugin
: 支持壓縮 ES6 (Webpack4)webpack-parallel-uglify-plugin
: 多進程執行代碼壓縮,提高構建速度mini-css-extract-plugin
: 分離樣式文件,CSS 提取爲獨立文件,支持按需加載 (替代extract-text-webpack-plugin)serviceworker-webpack-plugin
:爲網頁應用增長離線緩存功能clean-webpack-plugin
: 目錄清理ModuleConcatenationPlugin
: 開啓 Scope Hoistingspeed-measure-webpack-plugin
: 能夠看到每一個 Loader 和 Plugin 執行耗時 (整個打包耗時、每一個 Plugin 和 Loader 耗時)webpack-bundle-analyzer
: 可視化 Webpack 輸出文件的體積 (業務組件、依賴第三方模塊)更多 Plugin 請參考官網node
(Double Kill)react
(就知道你會問這個,我用手掩蓋着嘴角的微笑)webpack
Loader
本質就是一個函數,在該函數中對接收到的內容進行轉換,返回轉換後的結果。
由於 Webpack 只認識 JavaScript,因此 Loader 就成了翻譯官,對其餘類型的資源進行轉譯的預處理工做。nginx
Plugin
就是插件,基於事件流框架 Tapable
,插件能夠擴展 Webpack 的功能,在 Webpack 運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在合適的時機經過 Webpack 提供的 API 改變輸出結果。
Loader
在 module.rules 中配置,做爲模塊的解析規則,類型爲數組。每一項都是一個 Object,內部包含了 test(類型文件)、loader、options (參數)等屬性。
Plugin
在 plugins 中單獨配置,類型爲數組,每一項是一個 Plugin 的實例,參數都經過構造函數傳入。
Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程:
初始化參數
:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數開始編譯
:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,執行對象的 run 方法開始執行編譯肯定入口
:根據配置中的 entry 找出全部的入口文件編譯模塊
:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理完成模塊編譯
:在通過第4步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容以及它們之間的依賴關係輸出資源
:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再把每一個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是能夠修改輸出內容的最後機會輸出完成
:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,把文件內容寫入到文件系統在以上過程當中,Webpack
會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果。
簡單說
對源碼感興趣的同窗能夠移步個人另外一篇專欄從源碼窺探Webpack4.x原理
(這道題還蠻注重實際,用戶的體驗仍是要從小抓起的)
webpack-dashboard
:能夠更友好的展現相關打包信息。webpack-merge
:提取公共配置,減小重複配置代碼speed-measure-webpack-plugin
:簡稱 SMP,分析出 Webpack 打包過程當中 Loader 和 Plugin 的耗時,有助於找到構建過程當中的性能瓶頸。size-plugin
:監控資源體積變化,儘早發現問題HotModuleReplacementPlugin
:模塊熱替換source map
是將編譯、打包、壓縮後的代碼映射回源代碼的過程。打包壓縮後的代碼不具有良好的可讀性,想要調試源碼就須要 soucre map。
map文件只要不打開開發者工具,瀏覽器是不會加載的。
線上環境通常有三種處理方案:
hidden-source-map
:藉助第三方錯誤監控平臺 Sentry 使用nosources-source-map
:只會顯示具體行數以及查看源代碼的錯誤棧。安全性比 sourcemap 高sourcemap
:經過 nginx 設置將 .map 文件只對白名單開放(公司內網)注意:避免在生產中使用 inline-
和 eval-
,由於它們會增長 bundle 體積大小,並下降總體性能。
Webpack 實際上爲每一個模塊創造了一個能夠導出和導入的環境,本質上並無修改
代碼的執行邏輯,代碼執行順序與模塊加載順序也徹底一致。
在發現源碼發生變化時,自動從新構建出新的輸出文件。
Webpack開啓監聽模式,有兩種方式:
缺點:每次須要手動刷新瀏覽器
原理:輪詢判斷文件的最後編輯時間是否變化,若是某個文件發生了變化,並不會馬上告訴監聽者,而是先緩存起來,等 aggregateTimeout
後再執行。
module.export = { // 默認false,也就是不開啓 watch: true, // 只有開啓監聽模式時,watchOptions纔有意義 watchOptions: { // 默認爲空,不監聽的文件或者文件夾,支持正則匹配 ignored: /node_modules/, // 監聽到變化發生後會等300ms再去執行,默認300ms aggregateTimeout:300, // 判斷文件是否發生變化是經過不停詢問系統指定文件有沒有變化實現的,默認每秒問1000次 poll:1000 } }
(敲黑板,這道題必考)
Webpack
的熱更新又稱熱替換(Hot Module Replacement
),縮寫爲 HMR
。 這個機制能夠作到不用刷新瀏覽器而將新變動的模塊替換掉舊的模塊。
HMR的核心就是客戶端從服務端拉去更新後的文件,準確的說是 chunk diff (chunk 須要更新的部分),實際上 WDS 與瀏覽器之間維護了一個 Websocket
,當本地資源發生變化時,WDS 會向瀏覽器推送更新,並帶上構建時的 hash,讓客戶端與上一次資源進行對比。客戶端對比出差別後會向 WDS 發起 Ajax
請求來獲取更改內容(文件列表、hash),這樣客戶端就能夠再借助這些信息繼續向 WDS 發起 jsonp
請求獲取該chunk的增量更新。
後續的部分(拿到增量更新以後如何處理?哪些狀態該保留?哪些又須要更新?)由 HotModulePlugin
來完成,提供了相關 API 以供開發者針對自身場景進行處理,像react-hot-loader
和 vue-loader
都是藉助這些 API 實現 HMR。
細節請參考Webpack HMR 原理解析
(面試官:不錯不錯,小夥子表達能力不錯)
(基操,勿6)
VSCode
中有一個插件 Import Cost
能夠幫助咱們對引入模塊的大小進行實時監測,還可使用 webpack-bundle-analyzer
生成 bundle
的模塊組成圖,顯示所佔體積。
bundlesize
工具包能夠進行自動化資源體積監控。
文件指紋是打包後輸出的文件名的後綴。
Hash
:和整個項目的構建相關,只要項目文件有修改,整個項目構建的 hash 值就會更改Chunkhash
:和 Webpack 打包的 chunk 有關,不一樣的 entry 會生出不一樣的 chunkhashContenthash
:根據文件內容來定義 hash,文件內容不變,則 contenthash 不變設置 output 的 filename,用 chunkhash。
module.exports = { entry: { app: './scr/app.js', search: './src/search.js' }, output: { filename: '[name][chunkhash:8].js', path:__dirname + '/dist' } }
設置 MiniCssExtractPlugin 的 filename,使用 contenthash。
module.exports = { entry: { app: './scr/app.js', search: './src/search.js' }, output: { filename: '[name][chunkhash:8].js', path:__dirname + '/dist' }, plugins:[ new MiniCssExtractPlugin({ filename: `[name][contenthash:8].css` }) ] }
設置file-loader的name,使用hash。
佔位符名稱及含義
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename:'bundle.js', path:path.resolve(__dirname, 'dist') }, module:{ rules:[{ test:/\.(png|svg|jpg|gif)$/, use:[{ loader:'file-loader', options:{ name:'img/[name][hash:8].[ext]' } }] }] } }
可使用 enforce
強制執行 loader
的做用順序,pre
表明在全部正常 loader 以前執行,post
是全部 loader 以後執行。(inline 官方不推薦使用)
(這個問題就像能不能說一說從URL輸入到頁面顯示發生了什麼同樣)
(我只想說:您但願我講多長時間呢?)
(面試官:。。。)
高版本
的 Webpack 和 Node.js多進程/多實例構建
:HappyPack(不維護了)、thread-loader壓縮代碼
多進程並行壓縮
圖片壓縮
縮小打包做用域
:
提取頁面公共資源
:
基礎包分離:
DLL
:
充分利用緩存提高二次構建速度
:
Tree shaking
使用 PurifyCSS(不在維護) 或者 uncss 去除無用 CSS 代碼
Scope hoisting
動態Polyfill
更多優化請參考官網-構建性能
代碼分割的本質其實就是在源代碼直接上線
和打包成惟一腳本main.bundle.js
這兩種極端方案之間的一種更適合實際場景的中間狀態。
阿卡麗:榮耀劍下取,均衡亂中求
用可接受的服務器性能壓力增長來換取更好的用戶體驗。
源代碼直接上線:雖然過程可控,可是http請求多,性能開銷大。
打包成惟一腳本:一把梭完本身爽,服務器壓力小,可是頁面空白期長,用戶體驗很差。
(Easy peezy right)
Loader 支持鏈式調用,因此開發上須要嚴格遵循「單一職責」,每一個 Loader 只負責本身須要負責的事情。
Loader的API 能夠去官網查閱
加載本地 Loader 方法
webpack在運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在特定的階段鉤入想要添加的自定義功能。Webpack 的 Tapable 事件流機制保證了插件的有序性,使得整個系統擴展性良好。
Plugin的API 能夠去官網查閱
找出合適的事件點去完成想要的功能
大多數JavaScript Parser遵循 estree
規範,Babel 最初基於 acorn
項目(輕量級現代 JavaScript 解析器)
Babel大概分爲三大部分:
解析:將代碼轉換成 AST
轉換:訪問 AST 的節點進行變換操做生產新的 AST
想了解如何一步一步實現一個編譯器的同窗能夠移步 Babel 官網曾經推薦的開源項目
the-super-tiny-compiler
面試官:(我聽的口渴了,我們休息一會,一會進行下半場)
面試官拿起旁邊已經涼透的龍井,喝了一口。
(這小夥子有點東西)
持續更新……
1.看到這裏了就點個贊支持下吧,你的「贊」是我創做的動力。
2.關注公衆號前端食堂,「你的前端食堂,記得按時吃飯」!
3.本文已收錄在前端食堂Github
github.com/Geekhyt,求個小星星,感謝Star。