「吐血整理」再來一打Webpack面試題🔥(持續更新)

https://juejin.im/post/5e6f4b4e6fb9a07cd443d4a5css


觀感度:🌟🌟🌟🌟🌟html

口味:清蒸鱸魚前端

烹飪時間:15minvue


本文已收錄在Github github.com/Geekhyt,歡迎Star。node

從頭髮的濃密程度和幹練的走路姿式我察覺到,面前坐着的這位面試官也是一把好手。我像以往同樣,準備花3分鐘的時間進行自我介紹。在此期間,個人目光被16寸的MacBook Pro所吸引,此次的自我介紹我作足了準備,頗有信心征服面試官。不出我所料,面試官被我引入了我擅長的領域。react

看來你對Webpack很熟悉,那我來考考你webpack

0.有哪些常見的Loader?你用過哪些Loader?

(我開始熟悉的報起了菜名)nginx

  • raw-loader:加載文件原始內容(utf-8)git

  • file-loader:把文件輸出到一個文件夾中,在代碼中經過相對 URL 去引用輸出的文件 (處理圖片和字體)github

  • url-loader:與 file-loader 相似,區別是用戶能夠設置一個閾值,大於閾值時返回其 publicPath,小於閾值時返回文件 base64 形式編碼 (處理圖片和字體)

  • source-map-loader:加載額外的 Source Map 文件,以方便斷點調試

  • svg-inline-loader:將壓縮後的 SVG 內容注入代碼中

  • image-loader:加載而且壓縮圖片文件

  • json-loader 加載 JSON 文件(默認包含)

  • handlebars-loader: 將 Handlebars 模版編譯成函數並返回

  • babel-loader:把 ES6 轉換成 ES5

  • ts-loader: 將 TypeScript 轉換成 JavaScript

  • awesome-typescript-loader:將 TypeScript 轉換成 JavaScript,性能優於 ts-loader

  • sass-loader:將SCSS/SASS代碼轉換成CSS

  • css-loader:加載 CSS,支持模塊化、壓縮、文件導入等特性

  • style-loader:把 CSS 代碼注入到 JavaScript 中,經過 DOM 操做去加載 CSS

  • postcss-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 請參考官網

(面試官:挺好,知道的還挺多)

1.有哪些常見的Plugin?你用過哪些Plugin?

(這大兄弟好像聽上癮了,繼續開啓常規操做)

  • 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 Hoisting

  • speed-measure-webpack-plugin: 能夠看到每一個 Loader 和 Plugin 執行耗時 (整個打包耗時、每一個 Plugin 和 Loader 耗時)

  • webpack-bundle-analyzer: 可視化 Webpack 輸出文件的體積 (業務組件、依賴第三方模塊)

更多 Plugin 請參考官網

(Double Kill)

2.那你再說一說Loader和Plugin的區別?

(就知道你會問這個,我用手掩蓋着嘴角的微笑)

Loader 本質就是一個函數,在該函數中對接收到的內容進行轉換,返回轉換後的結果。 由於 Webpack 只認識 JavaScript,因此 Loader 就成了翻譯官,對其餘類型的資源進行轉譯的預處理工做。

Plugin 就是插件,基於事件流框架 Tapable,插件能夠擴展 Webpack 的功能,在 Webpack 運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在合適的時機經過 Webpack 提供的 API 改變輸出結果。

Loader 在 module.rules 中配置,做爲模塊的解析規則,類型爲數組。每一項都是一個 Object,內部包含了 test(類型文件)、loader、options (參數)等屬性。

Plugin 在 plugins 中單獨配置,類型爲數組,每一項是一個 Plugin 的實例,參數都經過構造函數傳入。

3.Webpack構建流程簡單說一下

Webpack 的運行流程是一個串行的過程,從啓動到結束會依次執行如下流程:

  • 初始化參數:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數

  • 開始編譯:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,執行對象的 run 方法開始執行編譯

  • 肯定入口:根據配置中的 entry 找出全部的入口文件

  • 編譯模塊:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理

  • 完成模塊編譯:在通過第4步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容以及它們之間的依賴關係

  • 輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再把每一個 Chunk 轉換成一個單獨的文件加入到輸出列表,這步是能夠修改輸出內容的最後機會

  • 輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,把文件內容寫入到文件系統

在以上過程當中,Webpack 會在特定的時間點廣播出特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果。

簡單說

  • 初始化:啓動構建,讀取與合併配置參數,加載 Plugin,實例化 Compiler

  • 編譯:從 Entry 出發,針對每一個 Module 串行調用對應的 Loader 去翻譯文件的內容,再找到該 Module 依賴的 Module,遞歸地進行編譯處理

  • 輸出:將編譯後的 Module 組合成 Chunk,將 Chunk 轉換成文件,輸出到文件系統中

對源碼感興趣的同窗能夠移步個人另外一篇專欄從源碼窺探Webpack4.x原理

4.使用webpack開發時,你用過哪些能夠提升效率的插件?

(這道題還蠻注重實際,用戶的體驗仍是要從小抓起的)

  • webpack-dashboard:能夠更友好的展現相關打包信息。

  • webpack-merge:提取公共配置,減小重複配置代碼

  • speed-measure-webpack-plugin:簡稱 SMP,分析出 Webpack 打包過程當中 Loader 和 Plugin 的耗時,有助於找到構建過程當中的性能瓶頸。

  • size-plugin:監控資源體積變化,儘早發現問題

  • HotModuleReplacementPlugin:模塊熱替換

5.source map是什麼?生產環境怎麼用?

source map 是將編譯、打包、壓縮後的代碼映射回源代碼的過程。打包壓縮後的代碼不具有良好的可讀性,想要調試源碼就須要 soucre map。

map文件只要不打開開發者工具,瀏覽器是不會加載的。

線上環境通常有三種處理方案:

  • hidden-source-map:藉助第三方錯誤監控平臺 Sentry 使用

  • nosources-source-map:只會顯示具體行數以及查看源代碼的錯誤棧。安全性比 sourcemap 高

  • sourcemap:經過 nginx 設置將 .map 文件只對白名單開放(公司內網)

注意:避免在生產中使用 inline-eval-,由於它們會增長 bundle 體積大小,並下降總體性能。

6.模塊打包原理知道嗎?

Webpack 實際上爲每一個模塊創造了一個能夠導出和導入的環境,本質上並無修改 代碼的執行邏輯,代碼執行順序與模塊加載順序也徹底一致。

7.文件監聽原理呢?

在發現源碼發生變化時,自動從新構建出新的輸出文件。

Webpack開啓監聽模式,有兩種方式:

  • 啓動 webpack 命令時,帶上 --watch 參數
  • 在配置 webpack.config.js 中設置 watch:true

缺點:每次須要手動刷新瀏覽器

原理:輪詢判斷文件的最後編輯時間是否變化,若是某個文件發生了變化,並不會馬上告訴監聽者,而是先緩存起來,等 aggregateTimeout 後再執行。

module.export = {
// 默認false,也就是不開啓
watch: true,
// 只有開啓監聽模式時,watchOptions纔有意義
watchOptions: {
// 默認爲空,不監聽的文件或者文件夾,支持正則匹配
ignored: /node_modules/,
// 監聽到變化發生後會等300ms再去執行,默認300ms
aggregateTimeout:300,
// 判斷文件是否發生變化是經過不停詢問系統指定文件有沒有變化實現的,默認每秒問1000次
poll:1000
}
}
複製代碼












8.說一下 Webpack 的熱更新原理吧

(敲黑板,這道題必考)

Webpack 的熱更新又稱熱替換(Hot Module Replacement),縮寫爲 HMR。 這個機制能夠作到不用刷新瀏覽器而將新變動的模塊替換掉舊的模塊。

HMR的核心就是客戶端從服務端拉去更新後的文件,準確的說是 chunk diff (chunk 須要更新的部分),實際上 WDS 與瀏覽器之間維護了一個 Websocket,當本地資源發生變化時,WDS 會向瀏覽器推送更新,並帶上構建時的 hash,讓客戶端與上一次資源進行對比。客戶端對比出差別後會向 WDS 發起 Ajax 請求來獲取更改內容(文件列表、hash),這樣客戶端就能夠再借助這些信息繼續向 WDS 發起 jsonp 請求獲取該chunk的增量更新。

後續的部分(拿到增量更新以後如何處理?哪些狀態該保留?哪些又須要更新?)由 HotModulePlugin 來完成,提供了相關 API 以供開發者針對自身場景進行處理,像react-hot-loadervue-loader 都是藉助這些 API 實現 HMR。

細節請參考Webpack HMR 原理解析

(面試官:不錯不錯,小夥子表達能力不錯)

(基操,勿6)

9.如何對bundle體積進行監控和分析?

VSCode 中有一個插件 Import Cost 能夠幫助咱們對引入模塊的大小進行實時監測,還可使用 webpack-bundle-analyzer 生成 bundle 的模塊組成圖,顯示所佔體積。

bundlesize 工具包能夠進行自動化資源體積監控。

10.文件指紋是什麼?怎麼用?

文件指紋是打包後輸出的文件名的後綴。

  • Hash:和整個項目的構建相關,只要項目文件有修改,整個項目構建的 hash 值就會更改

  • Chunkhash:和 Webpack 打包的 chunk 有關,不一樣的 entry 會生出不一樣的 chunkhash

  • Contenthash:根據文件內容來定義 hash,文件內容不變,則 contenthash 不變

JS的文件指紋設置

設置 output 的 filename,用 chunkhash。

module.exports = {
entry: {
app: './scr/app.js',
search: './src/search.js'
},
output: {
filename: '[name][chunkhash:8].js',
path:__dirname + '/dist'
}
}
複製代碼









CSS的文件指紋設置

設置 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。

佔位符名稱及含義

  • ext 資源後綴名
  • name 文件名稱
  • path 文件的相對路徑
  • folder 文件所在的文件夾
  • contenthash 文件的內容hash,默認是md5生成
  • hash 文件內容的hash,默認是md5生成
  • emoji 一個隨機的指代文件內容的emoj
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]'
}
}]
}]
}
}
複製代碼



















11.在實際工程中,配置文件上百行乃是常事,如何保證各個loader按照預想方式工做?

可使用 enforce 強制執行 loader 的做用順序,pre 表明在全部正常 loader 以前執行,post 是全部 loader 以後執行。(inline 官方不推薦使用)

12.如何優化 Webpack 的構建速度?

(這個問題就像能不能說一說從URL輸入到頁面顯示發生了什麼同樣)

(我只想說:您但願我講多長時間呢?)

(面試官:。。。)

  • 使用高版本的 Webpack 和 Node.js

  • 多進程/多實例構建:HappyPack(不維護了)、thread-loader

  • 壓縮代碼

    • 多進程並行壓縮
      • webpack-paralle-uglify-plugin
      • uglifyjs-webpack-plugin 開啓 parallel 參數 (不支持ES6)
      • terser-webpack-plugin 開啓 parallel 參數
    • 經過 mini-css-extract-plugin 提取 Chunk 中的 CSS 代碼到單獨文件,經過 css-loader 的 minimize 選項開啓 cssnano 壓縮 CSS。
  • 圖片壓縮

    • 使用基於 Node 庫的 imagemin (不少定製選項、能夠處理多種圖片格式)
    • 配置 image-webpack-loader
  • 縮小打包做用域

    • exclude/include (肯定 loader 規則範圍)
    • resolve.modules 指明第三方模塊的絕對路徑 (減小沒必要要的查找)
    • resolve.mainFields 只採用 main 字段做爲入口文件描述字段 (減小搜索步驟,須要考慮到全部運行時依賴的第三方模塊的入口文件描述字段)
    • resolve.extensions 儘量減小後綴嘗試的可能性
    • noParse 對徹底不須要解析的庫進行忽略 (不去解析但仍會打包到 bundle 中,注意被忽略掉的文件裏不該該包含 import、require、define 等模塊化語句)
    • IgnorePlugin (徹底排除模塊)
    • 合理使用alias
  • 提取頁面公共資源

    • 基礎包分離:
      • 使用 html-webpack-externals-plugin,將基礎包經過 CDN 引入,不打入 bundle 中
      • 使用 SplitChunksPlugin 進行(公共腳本、基礎包、頁面公共文件)分離(Webpack4內置) ,替代了 CommonsChunkPlugin 插件
  • DLL

    • 使用 DllPlugin 進行分包,使用 DllReferencePlugin(索引連接) 對 manifest.json 引用,讓一些基本不會改動的代碼先打包成靜態資源,避免反覆編譯浪費時間。
    • HashedModuleIdsPlugin 能夠解決模塊數字id問題
  • 充分利用緩存提高二次構建速度

    • babel-loader 開啓緩存
    • terser-webpack-plugin 開啓緩存
    • 使用 cache-loader 或者 hard-source-webpack-plugin
  • Tree shaking

    • 打包過程當中檢測工程中沒有引用過的模塊並進行標記,在資源壓縮時將它們從最終的bundle中去掉(只能對ES6 Modlue生效) 開發中儘量使用ES6 Module的模塊,提升tree shaking效率
    • 禁用 babel-loader 的模塊依賴解析,不然 Webpack 接收到的就都是轉換過的 CommonJS 形式的模塊,沒法進行 tree-shaking
    • 使用 PurifyCSS(不在維護) 或者 uncss 去除無用 CSS 代碼
      • purgecss-webpack-plugin 和 mini-css-extract-plugin配合使用(建議)
  • Scope hoisting

    • 構建後的代碼會存在大量閉包,形成體積增大,運行代碼時建立的函數做用域變多,內存開銷變大。Scope hoisting 將全部模塊的代碼按照引用順序放在一個函數做用域裏,而後適當的重命名一些變量以防止變量名衝突
    • 必須是ES6的語法,由於有不少第三方庫仍採用 CommonJS 語法,爲了充分發揮 Scope hoisting 的做用,須要配置 mainFields 對第三方模塊優先採用 jsnext:main 中指向的ES6模塊化語法
  • 動態Polyfill

    • 建議採用 polyfill-service 只給用戶返回須要的polyfill,社區維護。 (部分國內奇葩瀏覽器UA可能沒法識別,但能夠降級返回所需所有polyfill)

更多優化請參考官網-構建性能

13.你剛纔也提到了代碼分割,那代碼分割的本質是什麼?有什麼意義呢?

代碼分割的本質其實就是在源代碼直接上線打包成惟一腳本main.bundle.js這兩種極端方案之間的一種更適合實際場景的中間狀態。

阿卡麗:榮耀劍下取,均衡亂中求

用可接受的服務器性能壓力增長來換取更好的用戶體驗。

源代碼直接上線:雖然過程可控,可是http請求多,性能開銷大。

打包成惟一腳本:一把梭完本身爽,服務器壓力小,可是頁面空白期長,用戶體驗很差。

(Easy peezy right)

14.是否寫過Loader?簡單描述一下編寫loader的思路?

Loader 支持鏈式調用,因此開發上須要嚴格遵循「單一職責」,每一個 Loader 只負責本身須要負責的事情。

Loader的API 能夠去官網查閱

  • Loader 運行在 Node.js 中,咱們能夠調用任意 Node.js 自帶的 API 或者安裝第三方模塊進行調用
  • Webpack 傳給 Loader 的原內容都是 UTF-8 格式編碼的字符串,當某些場景下 Loader 處理二進制文件時,須要經過 exports.raw = true 告訴 Webpack 該 Loader 是否須要二進制數據
  • 儘量的異步化 Loader,若是計算量很小,同步也能夠
  • Loader 是無狀態的,咱們不該該在 Loader 中保留狀態
  • 使用 loader-utils 和 schema-utils 爲咱們提供的實用工具
  • 加載本地 Loader 方法
    • Npm link
    • ResolveLoader

15.是否寫過Plugin?簡單描述一下編寫Plugin的思路?

webpack在運行的生命週期中會廣播出許多事件,Plugin 能夠監聽這些事件,在特定的階段鉤入想要添加的自定義功能。Webpack 的 Tapable 事件流機制保證了插件的有序性,使得整個系統擴展性良好。

Plugin的API 能夠去官網查閱

  • compiler 暴露了和 Webpack 整個生命週期相關的鉤子
  • compilation 暴露了與模塊和依賴有關的粒度更小的事件鉤子
  • 插件須要在其原型上綁定apply方法,才能訪問 compiler 實例
  • 傳給每一個插件的 compiler 和 compilation對象都是同一個引用,若在一個插件中修改了它們身上的屬性,會影響後面的插件
  • 找出合適的事件點去完成想要的功能
    • emit 事件發生時,能夠讀取到最終輸出的資源、代碼塊、模塊及其依賴,並進行修改(emit 事件是修改 Webpack 輸出資源的最後時機)
    • watch-run 當依賴的文件發生變化時會觸發
  • 異步的事件須要在插件處理完任務時調用回調函數通知 Webpack 進入下一個流程,否則會卡住

16.聊一聊Babel原理吧

大多數JavaScript Parser遵循 estree 規範,Babel 最初基於 acorn 項目(輕量級現代 JavaScript 解析器) Babel大概分爲三大部分:

  • 解析:將代碼轉換成 AST
    • 詞法分析:將代碼(字符串)分割爲token流,即語法單元成的數組
    • 語法分析:分析token流(上面生成的數組)並生成 AST
  • 轉換:訪問 AST 的節點進行變換操做生產新的 AST
    • Taro就是利用 babel 完成的小程序語法轉換
  • 生成:以新的 AST 爲基礎生成代碼

想了解如何一步一步實現一個編譯器的同窗能夠移步 Babel 官網曾經推薦的開源項目 the-super-tiny-compiler

面試官:(我聽的口渴了,我們休息一會,一會進行下半場)

面試官拿起旁邊已經涼透的龍井,喝了一口。

(這小夥子有點東西)

持續更新……

參考

  • 深刻淺出 Webpack

  • Webpack 實戰

  • 玩轉 Webpack

❤️愛心三連擊

1.看到這裏了就點個在看支持下吧,你的在看是我創做的動力。

2.關注公衆號前端食堂你的前端食堂,記得按時吃飯

3.特殊階段,帶好口罩,作好我的防禦。

相關文章
相關標籤/搜索