1. 簡介
- 本質(zhì)上,
webpack
是一個現(xiàn)代JavaScript
應(yīng)用程序的靜態(tài)模塊打包器(module bundler
)碰镜。當(dāng)webpack
處理應(yīng)用程序時,它會遞歸地構(gòu)建一個依賴關(guān)系圖(dependency graph
),其中包含應(yīng)用程序需要的每個模塊毕箍,然后將所有這些模塊打包成一個或多個bundle
.
2. 核心概念
- 入口(
entry
)
1.指示 webpack
應(yīng)該使用哪個模塊,來作為構(gòu)建其內(nèi)部依賴圖的開始值骇。進入入口起點后莹菱,webpack
會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。
2.可以通過在 webpack
配置中配置 entry
屬性吱瘩,來指定一個入口起點(或多個入口起點)道伟。默認值為./src
。
- 輸出(
output
)
output
屬性告訴 webpack
在哪里輸出它所創(chuàng)建的 bundles
使碾,以及如何命名這些文件蜜徽,默認值為 ./dist
〔看基本上娜汁,整個應(yīng)用程序結(jié)構(gòu),都會被編譯到你指定的輸出路徑的文件夾中兄朋。你可以通過在配置中指定一個 output
字段掐禁,來配置這些處理過程
加載(
loader
)loader
讓webpack
能夠去處理那些非JavaScript
文件(webpack
自身只理解JavaScript
)。loader
可以將所有類型的文件轉(zhuǎn)換為webpack
能夠處理的有效模塊在更高層面颅和,在
webpack
的配置中loader
有兩個目標(biāo):
1.test
屬性傅事,用于標(biāo)識出應(yīng)該被對應(yīng)的 loader
進行轉(zhuǎn)換的某個或某些文件。
2.use
屬性峡扩,表示進行轉(zhuǎn)換時蹭越,應(yīng)該使用哪個 loader
。
注意:Webpack選擇了compose方式教届,即從右到左執(zhí)行l(wèi)oader
- 插件(
plugins
)
1.插件的范圍包括响鹃,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量案训。插件接口功能極其強大买置,可以用來處理各種各樣的任務(wù)。
2.plugins
需要暴露出一個class
, 在new WebpackPlugin()
的時候通過構(gòu)造函數(shù)傳入這個插件需要的參數(shù)强霎,在webpack
啟動的時候會先實例化plugin
再調(diào)用plugin.apply()
方法忿项,插件需要在apply
函數(shù)里監(jiān)聽webpack
生命周期里的事件,做相應(yīng)的處理
- 模式(
mode
)
通過選擇 development
或 production
之中的一個城舞,來設(shè)置 mode
參數(shù)轩触,你可以啟用相應(yīng)模式下的 webpack
內(nèi)置的優(yōu)化
// 多個入口
module.exports = {
mode: 'production',
entry: {
index: ["./src/index.js"],
main: ["./src/main.js"]
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[hash:8].js'
},
module: {
rules: [{
test: /\.js$/, // 正則匹配文件名
exclude: '/node_modules/', // 排除
use: ['babel-loader']
}
},
plugins: [ // 插件
new copyWebpackPlugin([{
from: path.resolve(__dirname, 'public/static'),
to: path.resolve(__dirname, 'dist'),
ignore: ['index.html']
}])
}
3. 基本流程
解析
shell
和config
中的配置項,用于激活webpack
的加載項和插件webpack
初始化工作家夺,包括構(gòu)建compiler
對象脱柱,初始化compiler
的上下文,loader
和file
的輸入輸出環(huán)境解析入口
js
文件拉馋,通過對應(yīng)的工廠方法創(chuàng)建模塊褐捻,使用acron
生成AST樹并且遍歷AST掸茅,處理require
的module
,如果依賴中包含依賴則遍歷build module
柠逞,在遍歷過程中會根據(jù)文件類型和loader
配置找出合適的loader
用來對文件進行轉(zhuǎn)換調(diào)用
seal
方法昧狮,封裝,逐次對每一個module
板壮,chunk
進行整理逗鸣,生成編輯后的代碼
4. 模塊打包
通過
fs
將模塊讀取成字符串,然后用warp
包裹一下绰精,使之成為一個字符串形式的的函數(shù)然后調(diào)用vm.runInNewContext
這樣類型的方法撒璧,這個字符串會變成一個函數(shù)。這些模塊的函數(shù)會被存放在數(shù)組里笨使,然后進行解析執(zhí)行卿樱。
module
和export
都是傳入的對象,webpack
會實現(xiàn)require
函數(shù)硫椰,去加載其他模塊繁调。如果是異步模塊,則會通過
jsonp
的形式去加載該模塊打包好生成的chunk
靶草。異步加載模塊可以使用import
和require.ensure
函數(shù)蹄胰,函數(shù)將會返回一個promise
。上面方法都是公共的奕翔,可以抽離成模板的js文件裕寨,
webpack
負責(zé)做依賴分析,并將模塊讀成函數(shù)填充入數(shù)組派继。(這里說的只是js的模塊)
<!-- 同步模塊 -->
var moduleDepList = [
{'./moduleA': 1}, // module[0] 的依賴 他依賴moduleA 且 moduleA的下標(biāo)在moduleList 中 為 1
{}
]
function require(id, parentId) {
var currentModlueId = parentId !== undefined ? moduleDepList[parentId][id] : id
var module = {exports: {}}
var moduleFunc = moduleList[currentModlueId]
moduleFunc(id => require(id, currentModlueId), module, module.exports)
return module.exports
}
<!-- 異步模塊 -->
var cache = {}
window.__jsonp = function(chunkId, moduleFunc) {
var chunk = cache[chunkId]
var resolve = chunk[0]
var module = {exports: {}}
moduleFunc(require, module, module.exports)
resolve(module.exports)
}
require.ensure = function(chunkId, parentId) {
var currentModlueId = parentId !== undefined ? moduleDepList[parentId][chunkId] : chunkId
var currentChunk = cache[currentModlueId]
if (currentChunk === undefined) {
var $script = document.createElement('script')
$script.src = `chunk.${chunkId}.js`
document.body.appendChild($script)
var promise = new Promise(function(resolve) {
var chunkCache = [resolve] // 數(shù)組形式是為了保存promise
chunkCache.status = true // 異步模塊加載中 如果有別的包 在 異步加載在模塊 那么下面的
cache[chunkId] = chunkCache
})
cache[chunkId].push(promise)
return promise
}
if (currentChunk.status) {
return currentChunk[1] // 這里的promise 這里的就直接返回promise 這樣模塊只會加載一次
}
return currentChunk
}
5. 熱更新
client
和server
建立一個websocket
通信當(dāng)有文件發(fā)生變動(如
fs.watchFile
)的時候宾袜,webpack
編譯文件,并通過websocket
向client
發(fā)送一條更新消息client
根據(jù)收到的hash
值驾窟,通過ajax
獲取一個manifest
描述文件client
根據(jù)manifest
獲取新的JS
模塊的代碼當(dāng)取到新的
JS
代碼之后庆猫,會更新modules tree
,(installedModules
)調(diào)用之前通過module.hot.accept
注冊好的回調(diào)纫普,可能是loader
提供的阅悍,也可能是你自己寫的manifest
: 描述資源文件對應(yīng)關(guān)系如下好渠,打包后的文件擁有了hash
值昨稼,所以需要進行映射。
{
"a.js": "a.41231243.js"
}
6. 如何開發(fā)一個plugin
一個 JavaScript 命名函數(shù)拳锚。
在插件函數(shù)的 prototype 上定義一個 apply 方法假栓。
指定一個綁定到 webpack 自身的事件鉤子。
處理 webpack 內(nèi)部實例的特定數(shù)據(jù)霍掺。
功能完成后調(diào)用 webpack 提供的回調(diào)匾荆。
tapable 工具拌蜘,它提供了 webpack 插件接口的支柱
// 一個 JavaScript 命名函數(shù)。
function plugin() {};
// 在插件函數(shù)的 prototype 上定義一個 `apply` 方法牙丽。
plugin.prototype.apply = function(compiler) {
// 指定一個掛載到 webpack 自身的事件鉤子简卧。
compiler.plugin('webpacksEventHook', function(compilation, callback) {
callback();
});
// 使用taptable的寫法
//基本寫法
compiler.hooks.someHook.tap(...)
//如果希望在entry配置完畢后執(zhí)行某個功能
compiler.hooks.entryOption.tap(...)
//如果希望在生成的資源輸出到output指定目錄之前執(zhí)行某個功能
compiler.hooks.emit.tap(...)
};
7. Compiler和Compliation 對象和鉤子
- 對象
1.compiler 對象代表了完整的 webpack 環(huán)境配置。這個對象在啟動 webpack 時被一次性建立烤芦,并配置好所有可操作的設(shè)置举娩,包括 options,loader 和 plugin构罗。
2.compilation 對象代表了一次資源版本構(gòu)建铜涉。當(dāng)運行 webpack 開發(fā)環(huán)境中間件時,每當(dāng)檢測到一個文件變化遂唧,就會創(chuàng)建一個新的 compilation芙代,從而生成一組新的編譯資源。一個 compilation 對象表現(xiàn)了當(dāng)前的模塊資源盖彭、編譯生成資源纹烹、變化的文件、以及被跟蹤依賴的狀態(tài)信息谬泌。compilation 對象也提供了很多關(guān)鍵時機的回調(diào)滔韵,以供插件做自定義處理時選擇使用
- 鉤子:總體分成兩大類:Compiler和Compliation
1.Compiler暴露了和webpack整個生命周期相關(guān)的鉤子
2.Compilation暴露了與模塊和依賴有關(guān)的粒度更小的事件鉤子,官方文檔中的說法是模塊會經(jīng)歷加載(loaded),封存(sealed),優(yōu)化(optimized),分塊(chunked),哈希(hashed)和重新創(chuàng)建(restored)這幾個典型步驟掌实,從上面的示例可以看到陪蜻,compilation是Compiler生命周期中的一個步驟,使用compilation相關(guān)鉤子的通用寫法為:
compiler.hooks.compilation.tap('SomePlugin',function(compilation, callback){
compilation.hooks.someOtherHook.tap('SomeOtherPlugin',function(){
....
})
});
- 鉤子的類型
1.同步鉤子
(1)syncHook: 不關(guān)心返回值
(2)syncBailHook: 有一個返回值不為null就跳過剩下的邏輯
(3)SyncWaterfallHook: 下一個任務(wù)要拿到上一個任務(wù)的返回值
(4)SyncLoopHook: 監(jiān)聽函數(shù)返回true表示繼續(xù)循環(huán)贱鼻,返回undefine表示結(jié)束循環(huán)
2.異步鉤子
(1)AsyncParallelHook: 異步并發(fā)執(zhí)行宴卖,仍是單線程
(2)AsyncParallelBailHook: 異步并發(fā)執(zhí)行,有一個失敗了邻悬,其他的都不用走了
(3)AsyncSeriesHook: 異步串行執(zhí)行
(4)AsyncSeriesBailHook: 異步串行執(zhí)行症昏,有一個返回值不為null就跳過剩下的邏輯
(5)AsyncSeriesWaterfallHook: 異步串行執(zhí)行,下一個任務(wù)要拿到上一個任務(wù)的返回值
8. 常見plugin
clean-webpack-plugin
: 在構(gòu)建之前刪除上一次build的文件夾copy-webpack-plugin
: 復(fù)制文件或文件夾到生成后的目錄extract-text-webpack | mini-css-extract-plugin
: 將所有入口的chunk(entry chunks)
中引用的*.css
父丰,移動到獨立分離的 CSS 文件html-webpack-plugin
: 將build后生成的資源以標(biāo)簽的形式嵌入到HTML模板內(nèi)hot-module-replacement
: 模塊熱更新
9. 常見loader
babel-loader: 語法肝谭,源碼轉(zhuǎn)換以便能夠運行在當(dāng)前和舊版本的瀏覽器或其他環(huán)境中
css-loader: 配合style-loader可以解析在js中引入的css文件,并以<style>便簽將css-loader內(nèi)部樣式注入到我們的HTML頁面
file-loader: 可以解析js中require的文件蛾扇,輸出到輸出目錄并返回 public URL
html-loader: 可以對HTML模板中指定哪個標(biāo)簽屬性組合(tag-attribute combination)元素應(yīng)該被此 loader 處理
less-loader: 依賴less攘烛,可以將less編譯成css
postcss-loader: 配合一些plugin如cssnano,autoprefixer可以對css進行壓縮,優(yōu)化镀首,自動補足前綴等
scss-loader: 配合node-scss坟漱,可以將scss編譯成css
style-loader: 配合css-loader可以解析在js中引入的css文件,并以<style>便簽將css-loader內(nèi)部樣式注入到我們的HTML頁面
url-loader: url-loader 功能類似于 file-loader更哄,但是在文件大杏蟪荨(單位 byte)低于指定的限制時腥寇,可以返回一個 DataURL(base64)
10. 常見打包優(yōu)化
使用dll
移除prefetch, preload觅捆,關(guān)閉sourceMap
webpack-bundle-analyzer打包分析赦役,將大的模塊可能的移至CDN。打包時間分析使用speed-measure-webpack-plugin
開啟gzip栅炒,服務(wù)器需要支持
使用多線程:thread-loader或HappyPack
webpack4內(nèi)置的terser啟動多線程壓縮
對項目進行拆分
11. 性能優(yōu)化
-
webapck
優(yōu)化與開啟gzip
壓縮
1.babel-loader
用 include
或 exclude
來幫我們避免不必要的轉(zhuǎn)譯扩劝,不轉(zhuǎn)譯node_moudules
中的js文件,其次在緩存當(dāng)前轉(zhuǎn)譯的js文件职辅,設(shè)置loader: 'babel-loader?cacheDirectory=true'
2.文件采用按需加載等等
3.具體的做法非常簡單棒呛,只需要你在你的 request headers
中加上這么一句:
accept-encoding:gzip
,該功能需要服務(wù)器支持才能正常顯示頁面域携。
4.圖片優(yōu)化簇秒,采用svg
圖片或者字體圖標(biāo)
5.瀏覽器緩存機制,它又分為強緩存和協(xié)商緩存
- 本地存儲——從
Cookie
到Web Storage
秀鞭、IndexedDB
說明一下SessionStorage
和localStorage
還有cookie
的區(qū)別和優(yōu)缺點
- 代碼優(yōu)化
1.事件代理
2.事件的節(jié)流和防抖
3.頁面的回流和重繪
4.EventLoop事件循環(huán)機制
5.代碼優(yōu)化等等
概念
1. MVVM
View 和 Model 之間并沒有直接的聯(lián)系趋观,而是通過ViewModel進行交互,Model 和 ViewModel 之間的交互是雙向的锋边, 因此View 數(shù)據(jù)的變化會同步到Model中皱坛,而Model 數(shù)據(jù)的變化也會立即反應(yīng)到View 上。
ViewModel 通過雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來豆巨,而View 和 Model 之間的同步工作完全是自動的剩辟,無需人為干涉,因此開發(fā)者只需關(guān)注業(yè)務(wù)邏輯往扔,不需要手動操作DOM, 不需要關(guān)注數(shù)據(jù)狀態(tài)的同步問題贩猎,復(fù)雜的數(shù)據(jù)狀態(tài)維護完全由 MVVM 來統(tǒng)一管理。
2. 組件化思想
簡單的說組件就是:將一段UI樣式和其對應(yīng)的功能作為獨立的整體去看待萍膛,無論這個整體放在哪里去使用吭服,它都具有一樣的功能和樣式,從而實現(xiàn)復(fù)用蝗罗,這種整體化的思想就是組件化艇棕。
組件化設(shè)計就是為了增加復(fù)用性,靈活性串塑,提高系統(tǒng)設(shè)計沼琉,從而提高開發(fā)效率。
3. 虛擬DOM
使用Javascript來操縱DOM拟赊,操作效率往往很低刺桃,由于DOM被表示為樹結(jié)構(gòu)粹淋,每次DOM中的某些內(nèi)容都會發(fā)生變化吸祟,因此對DOM的更改非成龋快,但更改后的元素屋匕,并且它的子項必須經(jīng)過Reflow / Layout階段葛碧,然后瀏覽器必須重新繪制更改,這很慢的过吻。
因此进泼,回流/重繪的次數(shù)越多,您的應(yīng)用程序就越卡頓纤虽。但是乳绕,Javascript運行速度很快,虛擬DOM是放在JS 和 HTML中間的一個層逼纸。它可以通過新舊DOM的對比洋措,來獲取對比之后的差異對象,然后有針對性的把差異部分真正地渲染到頁面上杰刽,從而減少實際DOM操作菠发,最終達到性能優(yōu)化的目的。
4. SPA 和 多頁面應(yīng)用
單頁面應(yīng)用: 僅僅在web頁面初始化時加載相應(yīng)的HTML贺嫂、JavaScript滓鸠、CSS,一旦頁面加載完成了第喳,SPA不會因為用戶的操作而進行頁面的重新加載或跳轉(zhuǎn)糜俗,而是利用 JavaScript 動態(tài)的變換HTML的內(nèi)容,從而實現(xiàn)UI與用戶的交互曲饱。
多頁面應(yīng)用: 多頁面跳轉(zhuǎn)刷新所有資源吩跋,每個公共資源(js、css等)需選擇性重新加載渔工,常用于 app 或 客戶端
5. CDN
CDN的全稱是Content Delivery Network锌钮,即內(nèi)容分發(fā)網(wǎng)絡(luò)∫兀基本原理是在用戶和服務(wù)器之間增加Cache層梁丘,主要是通過接管DNS實現(xiàn),將用戶的請求引導(dǎo)到Cache上獲得源服務(wù)器的數(shù)據(jù)旺韭,從而降低網(wǎng)絡(luò)的訪問時間氛谜。CDN的關(guān)鍵技術(shù)主要有負載均衡,內(nèi)容存儲和分發(fā)技術(shù)区端。
負載均衡:使用整體性的網(wǎng)絡(luò)負載均衡技術(shù)布隔,通過內(nèi)容路由器中的重定向(DNS)機制,在多個遠程POP上均衡用戶的請求嗅战,以使用戶請求得到最近內(nèi)容源的響應(yīng)。
內(nèi)容分發(fā):借助于建立索引酱塔、緩存、流分裂危虱、組播(Multicast)等技術(shù)羊娃,將內(nèi)容發(fā)布或投遞到距離用戶最近的遠程服務(wù)點(POP)處。
內(nèi)容存儲:在功能上包括對各種內(nèi)容格式的支持埃跷,對部分緩存的支持;在性能上包括支持的容量蕊玷、多文件吞吐率、可靠性弥雹、穩(wěn)定性垃帅,都是存儲需要考慮的問題。
6. 函數(shù)式編程
函數(shù)式編程是種編程方式剪勿,它將電腦運算視為函數(shù)的計算挺智。在函數(shù)編程中,函數(shù)是第一等公民窗宦,且該函數(shù)應(yīng)該是一個純函數(shù)赦颇,即相同的輸入,永遠會得到相同的輸出赴涵,而且沒有任何可觀察的副作用媒怯。
列如含有:
1.log
2.http請求
3.可變數(shù)據(jù)如new Date()
4.DOM操作
- 純函數(shù)帶來的好處就是:更好的進行單元測試和調(diào)試,一對一的數(shù)據(jù)關(guān)系可以便于緩存髓窜。函數(shù)式編程還有其他特性:
1.閉包和高階函數(shù)
2.惰性計算
3.遞歸
- 函數(shù)式編程有兩個最基本的運算:合成compose和柯里化curry扇苞。
結(jié)束語
2020前端面試就分享到這里,前端就是一個大雜燴寄纵,亂燉鳖敷,需要會的、了解的東西太多了程拭,學(xué)無止境定踱,如果發(fā)現(xiàn)問題,歡迎評論區(qū)指正恃鞋。