深入淺出Webpack學(xué)習(xí)筆記
基本概念
常用的構(gòu)建工具
所有的構(gòu)建工具所做的工做大致一樣抬探,都是把源代碼翻譯轉(zhuǎn)換成可執(zhí)行的代碼院领,包括如下內(nèi)容:
- 代碼轉(zhuǎn)換:TypeScript轉(zhuǎn)換成JavaScript附鸽,SCSS轉(zhuǎn)換成CSS;
- 文件優(yōu)化:壓縮JavaScript恬总、CSS盆犁、圖片等資源,利用一些優(yōu)化手段诈铛,如搖樹優(yōu)化乙各,移除無關(guān)代碼;
- 代碼分割:提取多個頁面的公共代碼幢竹、提取首屏不需加載的代碼讓其異步加載耳峦,防止首次進入應(yīng)用等待時間過長;
- 模塊合并:在一些模塊化的項目中會有很多個模塊和文件妨退,需要構(gòu)建工具把這些模塊文件分類合并成一個文件妇萄;
- 自動刷新:監(jiān)聽本地代碼蜕企,自動構(gòu)建加載,方便開發(fā)冠句;
- 代碼校驗:提交代碼前進行代碼規(guī)范檢查轻掩;
- 自動發(fā)布:更新完代碼后,自動構(gòu)建出線上代碼懦底,推送到線上或其他環(huán)境上唇牧;
Npm Script
Grunt
Gulp
Fis3
Webpack
https://www.webpackjs.com/
Webpack是一個模塊化打包工具,專注于構(gòu)建模塊化項目聚唐,在Webpack眼里一切文件都是模塊丐重,通過Loader轉(zhuǎn)換翻譯文件,通過Plugin注入鉤子杆查,最后輸出由多個模塊組合成的文件扮惦。
之所以一切文件皆模塊,如:JavaScript亲桦、CSS崖蜜、SCSS以及圖片等資源,在Webpack眼中都是模塊客峭,因為這樣可以更好的理清描述各個模塊之間的依賴關(guān)系豫领,方便Webpack對模塊進行打包組合,輸出瀏覽器使用的靜態(tài)資源舔琅。
簡單使用:
module.exports = {
// 定義入口文件
entry: './index.js',
// 定義打包輸出文件
output: {
// 最終會把依賴的所有模塊打包成一個bundle.js文件
filename: './bundle.js'
}
}
Webpack的優(yōu)點
- 專注于處理模塊化項目等恐,可以做到開箱即用,一步到位备蚓;
- 通過Plugin進行擴展课蔬,完整好用又不失靈活;
- 使用場景豐富星著,除了web端购笆,其他場景也可以;
- 社區(qū)活躍虚循;
- 開發(fā)體驗好;
缺點是只能采用模塊化開發(fā)項目样傍。
Rollup
安裝與使用
安裝只需要一行命令横缔,當然可以全局安裝,但是不推薦衫哥。
npm install -D webpack
或者指定版本號:
npm install -D webpack@2.xxx
或者直接安裝最新版:
npm install -D webpack@beta
注意:如果你使用的webpack版本較新茎刚,在webpack4.x測試下,你需要額外安裝一依賴:
npm i webpack-cli @webpack-cli/init
撤逢。 可以參考webpack-cli
運行Webpack命令:
node_modules/.bin/webpack
或者通過配置npm script來運行:
"script": {
"start": "webpack --config webpack.config.js"
}
具體如何使用膛锭?
前面有了基本使用方法粮坞,但是具體落實到代碼上該怎么寫?我們可以通過構(gòu)建一個采用CommonJs模塊化的簡單Demo來理解初狰。
建立如下文件:
|-- index.html // 入口文件
|-- show.js // js文件莫杈,里面我們隨便寫一個函數(shù)
|-- main.js // 入口文件
|-- package.json // npm 配置文件
|-- webpack.config.js // webpack 配置文件
index.html
index.html
文件內(nèi)容包含了一個script和一個id等于app的div。
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div id="app"></div>
<!-- 導(dǎo)入 Webpack 輸出的 JavaScript 文件 -->
<script src="./dist/bundle.js"></script>
</body>
</html>
show.js
show.js
文件定義了一個show
函數(shù)奢入,該方法將給頁面中的div插入一段文本筝闹;同時,我們利用CommonJs規(guī)范腥光,將該函數(shù)導(dǎo)出关顷。
function show() {
document.getElementById('app').innerText = 'hello world';
}
module.exports = show;
main.js
main.js
文件將show.js
引入,并執(zhí)行show
函數(shù)武福。
const show = require('./show.js');
show();
webpack.config.js
執(zhí)行webpack構(gòu)建執(zhí)行命令的時候议双,會自動讀取項目根目錄下的webpack.config.js
文件,所以我們新建該文件捉片,并指明入口文件和打包輸出文件聋伦。
const path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: './bundle.js',
path: path.resolve(__dirnam, './dist') // 輸出路徑
}
}
之所以使用CommonJs規(guī)范來導(dǎo)出webpack配置界睁,是因為webpack運行在Node下觉增,所以我們要使用CommonJs規(guī)范來描述一個如何構(gòu)建的Object對象。
執(zhí)行webpack構(gòu)建命令后翻斟,在項目根目錄下會多出一個dist文件夾逾礁,以及一個bundle.js文件。bundle.js
依賴main.js
和show.js
兩個文件以及內(nèi)置的webpackBootstrap
啟動函數(shù)访惜,從入口文件main.js
出發(fā)嘹履,識別出源碼中模塊化導(dǎo)入的語句,把入口文件所依賴的模塊或文件遞歸的打包到一個文件中:bundle.js文件债热。
此時砾嫉,直接打開index.html
文件可以正常顯示一段文案。
使用Loader
繼續(xù)前面的內(nèi)容窒篱,這次我們創(chuàng)建一個CSS文件: main.css
焕刮。
建立如下文件:
|-- index.html // 入口文件
|-- show.js // js文件,里面我們隨便寫一個函數(shù)
|-- main.js // 入口文件
|-- package.json // npm 配置文件
|-- webpack.config.js // webpack 配置文件
|-- main.css
文件中我們添加一段文本居中的樣式:
#app {
text-align: center;
}
然后墙杯,我們在main.js
引入這個CSS文件:
// 引入css
require('./main.css');
const show = require('./show.js');
show();
編寫工作做完后配并,我們自然的想到直接執(zhí)行webpack構(gòu)建命令,但是此時還不可以高镐,因為Webpack原生僅支持解析JavaScript文件溉旋,如果需要
解析其他類型的文件,需要引入相應(yīng)的Loader嫉髓,這里观腊,我們因為需要解析CSS邑闲,所以需要引入CSS Loader。
手動的去配置webpack.config.js
文件:
const path = require('path');
module.exports = {
entry: './main.js',
output: {
filename: './bundle.js',
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
// 用正則匹配css文件
test: '/\.css$/',
use: ['style-loader', 'css-loader?minimize'], // minimize:需要進行壓縮
}
]
}
}
上面我們簡單的配置了一個Loader規(guī)則梧油。
Loader相當于一個翻譯員苫耸,將某個文件源碼翻譯成可執(zhí)行的代碼。配置規(guī)則要求我們在rules
數(shù)組中配置一個對象婶溯,指定test
屬性值來匹配那些文件需要翻譯鲸阔,通過use
來指定需要使用哪些Loader,這里我們使用了style-loader
和css-loader
迄委。
需要注意的是褐筛,在配置use
屬性的時候:
-
use
屬性值是一個數(shù)組,數(shù)組中的每個元素為loader的名字叙身,尤其要注意的是渔扎,Loader的執(zhí)行順序是由后到前; - 可以給Loader以URL querystring的形式傳遞參數(shù)寨典,比如前面的
css-loader?minimize
破喻,具體參可以參考所使用的Loader文檔;
理解了Loader后阴孟,我們需要進行安裝相應(yīng)的Loader依賴:
npm install -D style-loader css-loader
所有準備工作做完后瓣赂,我們執(zhí)行構(gòu)建命令:
npm start
或者 node_modules/.bin/webpack
然后再觀察bundle.js
文件,會發(fā)現(xiàn)代碼更新了丑掺,并且CSS代碼也被打包了進來谷浅,打開index.html秘血,可以看到居中效果即彪。
這里我們提一下紧唱,CSS之所可以寫在JavaScript中,歸功于剛才引入的style-lader
隶校,大概遠離就是將CSS樣式以字符串的形式存儲到JavaScript對象中漏益,然后在網(wǎng)頁執(zhí)行的時候,通過DOM操作動態(tài)的加入到頁面中的<style>
標簽中深胳。
當然绰疤,這樣會導(dǎo)致頁面加載時間變長,一定程度上需要我們再去優(yōu)化處理舞终,比如將CSS單獨打包成一個文件轻庆,單獨的輸出,這種操作权埠,我們可以通過Plugin來實現(xiàn)榨了。Plugin也是Webpack的一個重要概念。
Tips:
use
的配置中攘蔽,給Loader傳遞參數(shù)除了剛才的寫法,我們還可以傳遞一個對象來實現(xiàn):
module.exports = {
rules: [
{
test: '/\.css$/',
use: ['style-loader', { loader: 'css-loader', options: { minimize: true } }],
}
]
}
除了在webpack.config.js
中配置Loader外呐粘,還可以在代碼文件中直接引入相關(guān)Loader满俗,比如剛才的場景就可以這么處理:
// main.js
requrie('style-loader!css-loader?minimize!.main.css');
這樣就能指定對引入的main.css
文件先進行css-loader
在采用style-loder
轉(zhuǎn)換转捕。
另外,前面我們提到了Loader的記載順序是從后到前的唆垃,所以這里我們必須把css-loader
放在后面五芝,也就是先執(zhí)行。因為css-loder
是將css代碼編譯辕万,而style-loader
是將編譯好的css加到頁面中枢步。
使用Plugin
Plugin是用來擴展Webpack功能的,給Webpack帶來了很大的靈活性渐尿,通過在構(gòu)建流程中注入鉤子來實現(xiàn)醉途。
繼續(xù)前面的操作,我們這次需要優(yōu)化一下砖茸,把main.css
代碼打包到單獨的一個文件中隘擎。
我們需要在配置文件webpack.config.js
文件中添加plugins
屬性,來配置Plugin凉夯。
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: 'main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
{
test: '/\.css$/',
use: ExtractTextPlugin({
use: ['css-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 從.js中提取.css文件
filename: `[name]_[contenthash:8].css`
})
]
}
前面我們引入了新的插件货葬,需要先安裝:
npm install -D extract-text-webpack-plugin
然后我們執(zhí)行構(gòu)建命令,會發(fā)現(xiàn)dist目錄下多出來一個.css
結(jié)尾的CSS文件劲够,bundle.js中也沒有CSS代碼了震桶,然后我們手動將該CSS文件引入index.html
就可以了。
通過上面的代碼我們可以看到征绎,我們可以通過配置plugins
屬性來配置蹲姐,其值是一個數(shù)組,數(shù)組中的每一項是一個實例炒瘸,并且在實例化一個對象的時候淤堵,我們可以通過構(gòu)造函數(shù)傳入這個組件支持的屬性配置。
上面用到的extract-text-webpack-plugin
就是一個插件顷扩,用來提取JavaScript中的CSS代碼到一個單獨的文件拐邪,filename
屬性指定了輸出的文件名,[name]_[contenthash:8].css
中name
代表文件名隘截,contenthash:8
意思是根據(jù)文件內(nèi)容算出8位hash值扎阶。
該插件的其他配置可以在官網(wǎng)上找到。
使用DevServer
到目前為止婶芭,我們也只是做了打包構(gòu)建的工作东臀,在正常的開發(fā)過程中,還需要實現(xiàn)下面的功能:
- 代碼自動構(gòu)建犀农,自動刷新惰赋,實現(xiàn)文件變化監(jiān)聽;
- 提供HTTP服務(wù);
- 支持Source Map赁濒,方便調(diào)試轨奄。
上面提到的,Webpack原生支持1拒炎、3兩點挪拟,對于提供HTTP服務(wù),我們可以借助DevServe击你,是官方提供的一個開發(fā)工具玉组。
DevServer會自動開啟一個本地HTTP服務(wù),同時會自動啟動Webpack構(gòu)建丁侄,并通過WebSocket協(xié)議接受Webpack的文件的實時變更惯雳,做到可以實時預(yù)覽,方便我們開發(fā)绒障。
安裝與啟動
安裝DevServer:
npm install webpack-dev-server
啟動DevServer
webpack-dev-server
啟動成功后吨凑,我們可以在控制臺看到一串輸出:
Project is running at http://localhost:8080
此時,我們訪問http://localhost:8080
就會自動執(zhí)行根目錄下的index.html
文件户辱。
如果此時訪問鸵钝,我們會發(fā)現(xiàn)引入的bundle.js
報404錯誤,是因為DevServer會把Webpack構(gòu)建的文件保存在內(nèi)存中庐镐,在要訪問輸出的文件時候恩商,必須通過HTTP服務(wù)來訪問,并且DevServer不會理會webpack.config.js
配置里的output.path
屬性必逆,所以我們需要訪問http://localhost/bundle.js
才可以怠堪。
修改index.html
文件js引用路徑:
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div id="app"></div>
<!-- 修改路徑如下 -->
<script src="./bundle.js"></script>
</body>
</html>
實時預(yù)覽
我們現(xiàn)在修改main.js
、main.css
名眉、show.js
文件中的任一一處粟矿,保存后,瀏覽器便會自動刷新损拢,加載修改后的代碼陌粹。
不過我們需要注意的是,通過DevServer啟動的Webpack會自動開啟文件監(jiān)聽福压,也就是這里的修改代碼自動觸發(fā)刷新頁面的功能掏秩;而如果我們通過webpack
來啟動默認是不會開啟監(jiān)聽模式的,只有我們顯示的指明需要開啟監(jiān)聽模式才可以荆姆。
開啟監(jiān)聽模式: webpack --watch
DevServer會讓W(xué)ebpack在構(gòu)建的過程中在JavaScript代碼中注入一個代理客戶端用于控制網(wǎng)頁蒙幻,并通過WebSocket協(xié)議進行通知,如果文件發(fā)生變化胆筒,會立刻告知剛才注入的代理客戶端邮破,代理客戶端收到信息后,執(zhí)行刷新網(wǎng)頁操作。
但是如果我們修改index.html
文件不會觸發(fā)網(wǎng)頁刷新操作决乎,這是因為Webpack在啟動時候會以配置中心的entry為口入去遞歸解析entry所以來的文件队询,只有entry本身和其所依賴的文件才會被添加到監(jiān)聽對象中派桩;另外构诚,index.html文件脫離了JavaScript模塊化系統(tǒng),所以Webpack監(jiān)聽不到铆惑。
模塊熱替換
模塊熱替換不同于前面的頁面刷新范嘱,這里的模塊熱替換,可以在不刷新頁面的情況下實現(xiàn)重新加載新的模塊代碼的效果员魏,當有新的模塊代碼時候丑蛤,會將新的替換掉老的,并重新執(zhí)行一遍代碼撕阎,從而做到不刷新頁面受裹,卻可以實時預(yù)覽的效果。
相比較來說虏束,模塊熱更新在開發(fā)體驗和效率上會略勝一籌棉饶。
模塊熱替換默認是關(guān)閉的,我們可以在啟動DevServer的時候帶上--hot
參數(shù)來開啟镇匀。
Source Map
通過指定
devtool source-map
參數(shù)來開啟Source Map功能照藻。
所謂的Source Map就是一份代碼映射。在開過過程中汗侵,我們在瀏覽器看到的代碼都是編譯過的幸缕,所以沒辦法看到未編譯的代碼,很難去調(diào)試晰韵,代碼可讀性很差发乔。
而Source Map可以將編譯前的代碼給映射出來,讓我們可以在源碼上調(diào)試雪猪。Webpack支持生成Source Map栏尚,只需要在啟動的時候帶上--devtool source-map
參數(shù)。然后啟動后浪蹂,我們便可以在Chrome開發(fā)者工具下調(diào)試抵栈。
核心概念
- Entry: 入口配置,Webpack構(gòu)建的第一步將從Entry開始坤次,可以抽象成輸入古劲;
- Module:在Webpack里一切皆模塊,一個模塊對應(yīng)一個文件缰猴。Webpack會從Entry入手产艾,遞歸的找到所有的依賴模塊;
- Chunk:代碼塊,一個Chunk由多個模塊組合而成闷堡,用于代碼分割片段隘膘;
- Loader:模塊轉(zhuǎn)換器,用于把模塊中的內(nèi)容按需求轉(zhuǎn)換成新的內(nèi)容杠览,如ES6轉(zhuǎn)換成ES5弯菊;
- Plugin:擴展插件,在Webpack構(gòu)建流程中特定時機注入擴展來改變邏輯和結(jié)果踱阿;
- Output:輸出結(jié)果管钳,在Webpack經(jīng)過前面一系列處理后返回的最終結(jié)果。
Webpack啟動后會從Entry配置的Module開始软舌,遞歸的解析其依賴的所有Module才漆,每找到一個Module,會調(diào)用相應(yīng)的Loader對其進行轉(zhuǎn)換佛点,對Module轉(zhuǎn)換后醇滥,在解析當前Module所依賴的Module,同樣會調(diào)用相應(yīng)的Loader超营。這些Module會以Entry為單位分組鸳玩,一個Entry和其依賴的所有Module都會打包成一個Chunk。最終Webpack會把Chunk轉(zhuǎn)換成文件輸出糟描,整個構(gòu)建流程中怀喉,Webpack會在特定時機執(zhí)行Plugin定義的邏輯。