1. 安裝webpack到全局
在構(gòu)建之前芽腾,我們來在本地文件新建一個存放項(xiàng)目的文件夾,比如叫
demo1
這個項(xiàng)目栈源,然后進(jìn)入demo1
該項(xiàng)目的根目錄后逊抡,執(zhí)行命令npm init
運(yùn)行下,一路回車(先簡單的來)火窒,就會生成一個package.json
文件硼补。
在項(xiàng)目中直接運(yùn)行如下命令,就可以把webpack安裝到全局去熏矿;如下命令:
npm install -g webpack
2. 安裝webpack到本項(xiàng)目已骇。
在本項(xiàng)目中,執(zhí)行如下命令票编,就可以把webpack安裝到本地項(xiàng)目中褪储,如下命令:
npm install --save-dev webpack
3. 如何使用webpack?
在編寫webpack代碼之前慧域,我們先搭建好目錄結(jié)構(gòu)如下:
### 目錄結(jié)構(gòu)如下:
demo1 # 工程名
| |--- dist # 打包后生成的目錄文件
| |--- node_modules # 所有的依賴包
| |--- js # 存放所有js文件
| | |-- demo1.js
| | |-- main.js # js入口文件
| |
| |--- webpack.config.js # webpack配置文件
| |--- index.html # html文件
| |--- styles # 存放所有的css樣式文件
| |--- .gitignore
| |--- README.md
| |--- package.json
index.html代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
</body>
</html>
js/demo1.js 代碼假如是如下:
function demo1Func() {
console.log(11);
};
module.exports = demo1Func;
js/main.js 代碼如下:
const demo1Func = require('./demo1.js');
demo1Func();
如上的簡單的代碼鲤竹,我們先從上面的簡單的代碼來學(xué)起,然后逐漸會慢慢的深入的講解webpack知識點(diǎn)昔榴;
我們在項(xiàng)目的根目錄中新建 webpack.config.js件辛藻;
編寫配置文件如下:
const path = require('path');
module.exports = {
entry: './js/main.js',
output: {
// 將所有依賴的模塊合并輸出到一個叫bundle.js文件內(nèi)
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
};
一切編寫文件好了以后,我們一般情況下會在項(xiàng)目的根目錄下 運(yùn)行webpack命令互订;但是當(dāng)我們運(yùn)行webpack后揩尸,會報錯如下信息:
One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:
- webpack-cli (https://github.com/webpack/webpack-cli)
The original webpack full-featured CLI.
- webpack-command (https://github.com/webpack-contrib/webpack-command)
A lightweight, opinionated webpack CLI.
We will use "npm" to install the CLI via "npm install -D".
Which one do you like to install (webpack-cli/webpack-command):
因此我們可以在項(xiàng)目的目錄下 安裝下 webpack-cli ,如下命令安裝:
npm install webpack-cli -g
安裝完成后屁奏,我們再執(zhí)行webpack命令后岩榆,就會在項(xiàng)目中根目錄生成dist文件夾,該文件夾內(nèi)會生成 bundle.js 文件坟瓢。這時候我們再打開index.html勇边, 運(yùn)行后,會發(fā)現(xiàn)執(zhí)行了依賴的demo1.js了折联。
3.1 理解配置文件 entry(入口)和 出口 (output)
- 入口(entry) 是指示webpack應(yīng)該使用哪個模塊粒褒,來作為構(gòu)建內(nèi)部依賴js的開始,進(jìn)入入口起點(diǎn)后诚镰,webpack會找出有哪些模塊和庫是入口js依賴的奕坟。
- 出口(output) 是告訴webpack在什么地方輸出它所創(chuàng)建的bundles祥款,以及如何命名這些文件,默認(rèn)值為 './dist'.
4. 使用loader
webpack
的構(gòu)建是一個采用CommonJS
規(guī)范的模塊化項(xiàng)目月杉,現(xiàn)在我們還是繼續(xù)上面的項(xiàng)目刃跛,我們再在main.js
會引入一個css
文件。因此會在main.js
代碼修改如下:
require('../styles/main.css');
const demo1Func = require('./demo1.js');
demo1Func();
然后main.css代碼如下:
#app {
font-size: 18px;
}
如果我們現(xiàn)在直接去執(zhí)行webpack的話苛萎,就會報錯桨昙,因此我們需要在webpack中加入Loader的機(jī)制。將webpack代碼改成如下:
const path = require('path');
module.exports = {
entry: './js/main.js',
output: {
// 將所有依賴的模塊合并輸出到一個叫bundle.js文件內(nèi)
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
},
module: {
rules: [
{
// 用正則去匹配以 .css結(jié)尾的文件腌歉,然后需要使用loader進(jìn)行轉(zhuǎn)換
test: /\.css$/,
use: ['style-loader', 'css-loader?minimize']
}
]
}
};
如上代碼蛙酪,我們在webpack編譯之前需要安裝 style-loader 和 css-loader, 如下命令進(jìn)行安裝:
npm install --save-dev style-loader css-loader
如上配置代碼中的 module.rules 數(shù)組配置了一組規(guī)則翘盖,是來告訴webpack在遇到哪些文件時使用哪些loader去加載和轉(zhuǎn)換桂塞,比如上面的使用test正則去匹配
以 .css 結(jié)尾的文件,會先使用 css-loader 讀取css文件馍驯,然后使用 style-loader將css的內(nèi)容注入到j(luò)avascript里面去阁危。
注意:
1. use屬性的值是一個使用Loader名稱組成的數(shù)組,Loader的執(zhí)行順序是由后往前的泥彤。由于loader有順序的欲芹,因此我們在配置中不能如下編寫loader卿啡;
代碼如下:
module: {
rules: [
{
// 用正則去匹配以 .css結(jié)尾的文件吟吝,然后需要使用loader進(jìn)行轉(zhuǎn)換
test: /\.css$/,
use: ['css-loader?minimize', 'style-loader']
}
]
}
2. 每個Loader都可以通過 URL queryString 的方式傳入?yún)?shù),比如 css-loader?minimize是告訴css-loader是開啟css壓縮颈娜。
現(xiàn)在我們可以在項(xiàng)目中的根目錄運(yùn)行 webpack命令了剑逃,一切正常后,我們打開index.html后官辽,查看代碼蛹磺,發(fā)現(xiàn)css被加載到style標(biāo)簽內(nèi)了;
style-loader的原理:是將css的內(nèi)容使用javascript的字符串存儲起來同仆,在網(wǎng)頁執(zhí)行javascript時通過DOM操作萤捆,動態(tài)地向HTML head標(biāo)簽里插入 HTML style標(biāo)簽。
3. 配置loader的方式也可以使用Object來實(shí)現(xiàn)俗批,比如如上的匹配css代碼俗或,可以改成如下:
use: [
'style-loader',
{
loader: 'css-loader',
options: {
minimize: true
}
}
]
因此webpack所有的配置代碼變成如下:
const path = require('path');
module.exports = {
entry: './js/main.js',
output: {
// 將所有依賴的模塊合并輸出到一個叫bundle.js文件內(nèi)
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
},
module: {
rules: [
{
// 用正則去匹配以 .css結(jié)尾的文件,然后需要使用loader進(jìn)行轉(zhuǎn)換
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
minimize: true
}
}
]
}
]
}
};
5. 使用插件(Plugin)
loader
的作用是被用于轉(zhuǎn)換某些類型的模塊岁忘,而插件則可以用于執(zhí)行范圍更廣的任務(wù)辛慰,插件的范圍包括,從打包優(yōu)化和壓縮干像,一直到重新定義環(huán)節(jié)中的變量。
如果想要使用一個插件挽封,我們只需要require()
它,然后把它添加到plugins
數(shù)組中苗踪。我們可以在一個配置文件中因?yàn)椴煌哪康亩啻问褂糜靡粋€插件朋截,因此我們可以使用new
操作符來創(chuàng)建它的實(shí)列赵抢。
上面我們是通過loader加載了css文件到j(luò)s中去,下面我們通過plugin將注入的bundle.js文件里的css提取到單獨(dú)的文件中乐埠,我們需要使用到extract-text-webpack-plugin 插件抗斤,配置代碼如下:
const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './js/main.js',
output: {
// 將所有依賴的模塊合并輸出到一個叫bundle.js文件內(nèi)
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
},
module: {
rules: [
{
// 使用正則去匹配要用該loader轉(zhuǎn)換的css文件
test: /\.css$/,
loaders: ExtractTextPlugin.extract({
// 轉(zhuǎn)換 .css文件需要使用的Loader
use: ['css-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 從js文件中提取出來的 .css文件的名稱
filename: `main.css`
})
]
};
要使用 extract-text-webpack-plugin 插件的話,首先我們需要安裝該插件丈咐,安裝插件的命令如下:
npm install --save-dev extract-text-webpack-plugin
當(dāng)安裝成功后瑞眼,我們需要運(yùn)行webpack命令后,發(fā)現(xiàn)命令行報錯了棵逊,報錯信息如下:
(node:86210) DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead /usr/local/lib/node_modules/webpack/lib/Chunk.js:802
解決的辦法是 安裝 extract-text-webpack-plugin 時伤疙,需要如下安裝,安裝命令如下:
npm install extract-text-webpack-plugin@next
安裝成功后辆影,再運(yùn)行webpack就可以了徒像,我們可以看到在dist文件夾內(nèi)會多一個main.css, 因此我們可以在index.html中把 css文件引入進(jìn)去即可。
6. 使用DevServer
前面是使用
webpack
進(jìn)行打包蛙讥,但是在開發(fā)中我們還需要一個本地文件的服務(wù)器锯蛀,并且當(dāng)我們保存代碼的時候會自動進(jìn)行打包,并且還支持Source Map
次慢,以方便代碼調(diào)試等功能旁涤,因此我們現(xiàn)在需要使用到DevServer
了。
首先我們需要安裝 webpack-dev-server, 如下安裝命令:
npm install --save-dev webpack-dev-server
當(dāng)然我們還需要全局安裝一下经备,安裝命令如下:
npm install webpack-dev-server -g
然后當(dāng)我們在命令行輸入 webpack-dev-server 運(yùn)行后拭抬,發(fā)現(xiàn)報錯了部默,報錯如下信息:
The CLI moved into a separate package: webpack-cli.
Please install 'webpack-cli' in addition to webpack itself to use the CLI.
-> When using npm: npm install webpack-cli -D
-> When using yarn: yarn add webpack-cli -D
module.js:471
因此我們需要重新運(yùn)行下如下命令:
npm install webpack-cli -D
當(dāng)成功后侵蒙,我們再次運(yùn)行 webpack-dev-server 可以看到已經(jīng)啟動了服務(wù)器了,端口號默認(rèn)是 8080, 然后我們訪問 http://localhost:8080/index.html
就可以訪問到我們項(xiàng)目中頁面了傅蹂。
6.1 實(shí)時預(yù)覽
webpack在啟動時可以開啟監(jiān)聽模式纷闺,默認(rèn)是關(guān)閉的,開啟后webpack會監(jiān)聽本地文件系統(tǒng)的變化份蝴,在發(fā)生變化時候會重新構(gòu)建出新的結(jié)果犁功,在上面運(yùn)行
webpack-dev-server 后,我們需要打開一個新的命令行婚夫,在命令行中輸入如下命令來開啟監(jiān)聽模式:
webpack --watch
當(dāng)項(xiàng)目中入口文件或入口依賴的文件有改變的時候浸卦,它會自動重新構(gòu)建,構(gòu)建完成后會自動刷新下頁面案糙,但是如果修改的不是入口文件或依賴的文件
是不會有任何效果的限嫌,比如修改的是index.html 是不會重新構(gòu)建的靴庆。但是要完成上面的實(shí)時預(yù)覽,html頁面的bundle.js 要直接引入即可:如下html頁面代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>
那為什么 http://localhost:8080/bundle.js
這樣也能訪問的到呢怒医?原因是DevServer會將webpack構(gòu)建出的文件保存在內(nèi)存中炉抒,DevServer不會理會webpack.config.js里配置的output.path屬性的。
6.2 模塊熱替換
- 除了上面介紹的改動本地入口文件或依賴文件后稚叹,會自動打包焰薄,然后會自動刷新瀏覽器即可看到更新效果外,我們還可以使用模塊熱替換技術(shù)扒袖,
- 模塊熱替換技術(shù)能做到在不重新加載整個網(wǎng)頁的情況下塞茅,通過將已更新的模塊替換舊模塊,它默認(rèn)是關(guān)閉的季率,要開啟模塊熱替換凡桥,我們只需在啟動DevServer時帶上 --inline 參數(shù)即可。
如下命令:
webpack-dev-server --inline 或 webpack-dev-server --inline --hot
webpack-dev-server 有如下兩種啟動模式:
- iFrame: 該模式下修改代碼后會自動打包蚀同,但是瀏覽器不會自動刷新缅刽。
- inline: 內(nèi)聯(lián)模式,該模式下修改代碼蠢络,webpack將自動打包并刷新瀏覽器衰猛。
6.3 支持Source Map
在瀏覽器中運(yùn)行javascript代碼都是編譯器輸出的代碼,但是如果在代碼中碰到一個bug的時候刹孔,我們不好調(diào)式啡省,因此我們需要 Source Map來映射到源代碼上,Webpack支持生成 Source Map, 只需在啟動時帶上 --devtool source-map參數(shù)即可髓霞;如下命令:
webpack-dev-server --inline --hot --devtool source-map
注意:每次運(yùn)行 如上命令卦睹,感覺非常長,因此我們可以在項(xiàng)目的根目錄的package.json文件的scripts配置中添加如下配置:
"scripts": {
"dev": "webpack-dev-server --devtool source-map --hot --inline"
}
加上如上配置后方库,我們只需要在命令行中 運(yùn)行 npm run dev 即可结序;
其他配置常見的選項(xiàng):
- --quiet 控制臺中不輸出打包的信息
- --compress 開啟gzip的壓縮
- --progress 顯示打包的進(jìn)度
因此在項(xiàng)目中scripts經(jīng)常會做如下配置:
"scripts": {
"dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
"build": "webpack --progress --colors"
}
這樣的話,打包的時候會顯示打包進(jìn)度纵潦。
1. context
基礎(chǔ)目錄徐鹤,絕對路徑,用于從配置中解析入口起點(diǎn)和加載器邀层。
什么意思呢返敬?比如如下代碼配置:
- context: path.resolve(__dirname, 'js'), 含義是使用當(dāng)前目錄下的js文件下查找入口文件。
比如如下代碼的配置也是可以的:
module.exports = {
context: path.resolve(__dirname, 'js'),
entry: './main.js'
}
含義是從當(dāng)前項(xiàng)目目錄下的js文件查找main.js文件作為入口文件寥院,如果在當(dāng)前目錄沒有找到該入口文件劲赠,就會報錯。
當(dāng)然我們也可以如下寫配置代碼也是可以的:如下代碼配置:
module.exports = {
context: path.resolve(__dirname, ''),
entry: './js/main.js'
};
或者context配置項(xiàng)不要,它也是默認(rèn)從當(dāng)前目錄下查找的凛澎,因此如果使用context的話泌绣,建議加上第二個參數(shù),是從當(dāng)前目錄下那個文件內(nèi)查找的预厌“⒙酰或者直接不要這個配置項(xiàng)。
2. entry
應(yīng)用程序的起點(diǎn)入口轧叽,從這個起點(diǎn)開始苗沧,應(yīng)用程序啟動執(zhí)行,如果傳遞一個數(shù)組的話炭晒,那么數(shù)組的每一項(xiàng)都會執(zhí)行待逞。它的類型可以是String, array, 或 object;
2.1 string(類型為字符串):
如下配置:
module.exports = {
entry: './js/main.js',
output: {
// 將所有依賴的模塊合并輸出到一個叫bundle.js文件內(nèi)
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
};
2.2 類型為數(shù)組
module.exports = {
entry: ['./js/main.js', './js/main2.js'],
output: {
// 將所有依賴的模塊合并輸出到一個叫bundle.js文件內(nèi)
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
};
如果類型為數(shù)組的話,將會創(chuàng)建多個主入口网严,并且把數(shù)組中的js打包在一起到一個文件里面去识樱。
2.3 類型為對象時
module.exports = {
entry: {
'main': './js/main.js',
'main2': './js/main2.js'
},
output: {
filename: '[name].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
};
3. Output
output配置是輸出最終想要的代碼,它是一個object, 里面包含很多配置項(xiàng)震束。
3.1 filename 和 path的理解
filename: 輸出文件的名稱怜庸,為string類型.
- 如果只有一個輸出文件,可以將名稱寫死垢村;如:filename: 'bundle.js';
path: 文件被寫入硬盤的位置割疾。必須是string類型的絕對路徑,一般通過Node.js的path模塊獲取絕對路徑嘉栓,如下代碼:
path: path.resolve(__dirname, './dist')
單個入口配置代碼如下:
{
entry: './js/main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, './dist')
}
}
多個入口
如果有多個chunk要輸出時宏榕,就需要借助[name]變量了,webpack會為每個chunk取一個名稱侵佃,因此我們根據(jù)chunk的名稱來區(qū)分輸出的文件名麻昼。如下:filename: '[name].js'
多個入口配置代碼如下:
module.exports = {
entry: {
'main': './js/main.js',
'main2': './js/main2.js'
},
output: {
filename: '[name].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
};
如上配置,會輸出 main.js 和 main2.js馋辈。
內(nèi)置變量除了包括name抚芦,還包括,id, hash, chunkhash首有。
id: Chunk的唯一標(biāo)識燕垃,從0開始枢劝。
hash: Chunk的唯一標(biāo)識的Hash值井联。比如[hash:8] 代表8位的hash值。默認(rèn)是20位您旁。
chunkhash: Chunk內(nèi)容的Hash值烙常。
如下hash配置代碼:
entry: {
'main': './js/main.js',
'main2': './js/main2.js'
},
output: {
filename: '[name]_[hash:8].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
就會生成 main_xxxx.js 和 main2_xxxx.js 其中xxxx是hash值的隨機(jī)八位。
3.2 chunkFilename的理解
chunkFilename 和 filename非常類似,但是chunkFilename是未被列在entry中的蚕脏,但是又需要被打包出來的文件命名配置侦副,什么場景需要這樣的呢?
比如 異步按需加載模塊的時候驼鞭,一般這樣的文件沒有被列在entry中秦驯,比如如下項(xiàng)目在js文件內(nèi)再新建plugins文件夾,存放比如js插件使用的挣棕,目錄
結(jié)構(gòu)如下:
### 目錄結(jié)構(gòu)如下:
demo1 # 工程名
| |--- dist # 打包后生成的目錄文件
| |--- node_modules # 所有的依賴包
| |--- js # 存放所有js文件
| | |-- demo1.js
| | |-- main.js # js入口文件
| | |-- main2.js # js的多個入口文件
| | |-- plugins # plugins文件夾译隘,存放js插件類的
| | | |--- a.js
| |
| |--- webpack.config.js # webpack配置文件
| |--- index.html # html文件
| |--- styles # 存放所有的css樣式文件
| |--- .gitignore
| |--- README.md
| |--- package.json
如上目錄結(jié)構(gòu) 在js文件夾內(nèi),再新建plugins文件夾洛心,里面包含一個a.js文件固耘;代碼如下:
function a() {
console.log('a.js');
}
module.exports = a;
然后我們在main2.js入口文件如下編寫代碼;使用異步方式引用a.js; 代碼如下:
require.ensure(['./plugins/a.js'], function(require) {
var aModule = require('./plugins/a.js');
}, 'a');
然后在webpack的output配置添加 chunkFilename 配置如下:
module.exports = {
context: path.resolve(__dirname, ''),
entry: {
'main': './js/main.js',
'main2': './js/main2.js'
},
output: {
filename: '[name]_[hash:8].js', // [name] 的值是entry的鍵值, 會輸出多個入口文件
chunkFilename: '[name].min.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, './dist')
}
};
其中上面的 require.ensure() API的第三個參數(shù)是給這個模塊命名的词身,因此在webpack配置完成后厅目,繼續(xù)打包下,會在dist文件夾下打出 a.min.js 文件
了法严。所以這就是 chunkFilename 的用途場景了损敷。
3.3 publicPath的理解
output.path 是指所有輸出文件的本地文件目錄(絕對路徑)。比如配置如下:
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist')
}
那么webpack會將所有文件輸出到 dist/下深啤。也就是說path是存放打包后的文件的輸出目錄嗤锉。
正式環(huán)境下publicPath的理解:
publicPath正式環(huán)境可以理解為改變相對目錄下的靜態(tài)資源文件的路徑為正確的路徑。比如圖片引入的是相對路徑墓塌,可以配置publicPath成為正確的路徑瘟忱。
它是指定資源文件引用的目錄(相對于服務(wù)器的根目錄來講)。
先看styles/main.css代碼如下:
#app {
font-size: 18px;
width: 200px;
height: 200px;
backround: url('../images/1.jpg') no-repeat;
}
iamges文件夾內(nèi)有一張圖片為1.jpg, 因此上面是css的引入路徑苫幢。然后手動創(chuàng)建 index.html代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="app"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
現(xiàn)在我們執(zhí)行 npm run build 進(jìn)行打包后访诱,打開index.html發(fā)現(xiàn)css中圖片的引用的路徑為:backround: url(1.jpg) no-repeat; 很明顯圖片的引用路徑不對,它引入是根目錄下的圖片韩肝,因此如果我們在打包的時候加上 publicPath触菜,配置如下:
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/'
}
重新打包下,再看下css引入路徑為 backround: url(/dist/1.jpg) no-repeat哀峻;說明引入是對的涡相。
開發(fā)環(huán)境下publicPath的理解:
output的配置如下:
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
// publicPath: '/dist/'
}
然后把dist目錄刪除掉,執(zhí)行 npm run dev后剩蟀,開發(fā)環(huán)境打包后并沒有生成dist目錄催蝗,而我們的index.html引入了 dist/bundle.js 和 dist/main.css, 因此頁面會發(fā)現(xiàn)找不到j(luò)s和css文件。那么打包后文件放在那里去了呢育特?我們可以看如下圖:
啟動了webpack-dev-server, 然后整個項(xiàng)目運(yùn)行在localhost:8080下面丙号,也就是服務(wù)器地址是localhost:8080, 當(dāng)我們在瀏覽器中輸入服務(wù)器地址,服務(wù)器會簽就會到服務(wù)器請求js 文件犬缨,js的地址是dist/bundle.js喳魏, 沒有,報錯了怀薛。
看如上截圖的:wepback output is served from / , 這里指明了webpack 打包后文件放到了/ 目錄下刺彩,也就是根目錄下,webpack-dev-server 進(jìn)行打包時
它默認(rèn)把css和js及圖片打包到根目錄下枝恋。
現(xiàn)在把output配置改成如下:
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/'
};
再進(jìn)行運(yùn)行下 npm run dev 后看到如下所示:
wepback output is served from /dist/ 這里指明了webpack 打包后文件放到了/dist/ 目錄下了迂苛,因?yàn)閖s和css文件可以重新訪問了。
3.4 crossOriginLoading
Webpack輸出的部分代碼有可能需要異步加載鼓择,而異步加載是通過JSON方式實(shí)現(xiàn)的三幻。JSONP的原理是動態(tài)地向HTML中插入一個<script src="url"></script>,crossOriginLoading則是用于配置這個異步插入的標(biāo)簽的 crossorigin的值呐能。
script標(biāo)簽的crossorigin屬性可以取以下的值:
1. anonymous(默認(rèn))念搬,啟用跨域加載,在加載此腳本資源時不會帶上用戶的Cookies; 即發(fā)送不帶憑據(jù)的 credential的請求摆出。
2. use-credentials 啟用跨域加載朗徊,在加載此腳本資源時會帶上用戶的Cookies. 發(fā)送帶憑據(jù)的credential的請求。
3. false 禁用跨域加載偎漫。
3.5 libraryTarget 和 library
這兩個屬性大家可能比較陌生爷恳,一般項(xiàng)目中不需要關(guān)注這兩個屬性,但是當(dāng)我們開發(fā)類庫象踊,使用webpack去構(gòu)建一個可以被其他模塊導(dǎo)入使用的庫時會使用到温亲。一般情況下,webpack對js模塊進(jìn)行打包杯矩,即多個js模塊和一個入口模塊栈虚,打包成一個bundle文件,可以直接被瀏覽器或者其他javascript引擎執(zhí)行史隆,
相當(dāng)于直接編譯生成一個完成的可執(zhí)行文件魂务。但是當(dāng)我們需要發(fā)布一個javascript庫的時候,比如在npm社區(qū)中發(fā)布自己的庫泌射,這個時候我們的webpack就需要相應(yīng)的配置.
libraryTarget 是配置以何種方式導(dǎo)出庫粘姜。可以是 var, commonjs, 'commonjs2', amd, this,umd模式等熔酷。
library 配置導(dǎo)出庫的名稱孤紧。
他們一般都組合使用的。
我們下面來編寫一個簡單的庫纯陨,假如名字叫 demo1.js坛芽,代碼如下:
function sayHello() {
console.log('Hello');
}
function sayFunc() {
console.log('say');
}
export default {
sayHello: sayHello,
sayFunc: sayFunc
}
然后我們在main.js代碼引入該文件
import foo from './demo1.js';
console.log(foo);
foo.sayHello(); // 可以執(zhí)行
webpack打包配置如下:
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist')
}
這樣會輸出一個立即執(zhí)行的函數(shù)留储,bundle代碼大致結(jié)構(gòu)如下:
(function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
// ....
}
return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
"./js/demo1.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n console.log('Hello');\n}\n\nfunction sayFunc() {\n console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n sayHello: sayHello,\n sayFunc: sayFunc\n});\n\n//# sourceURL=webpack:///./js/demo1.js?");
}),
"./js/main.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\nconsole.log(_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"].sayHello();\n\n\n\n//# sourceURL=webpack:///./js/main.js?");
})
});
3.5.1 commonjs2 模式
如果我們需要將打包返回值交給編譯后的文件 module.export, 因此我們這邊可以使用 libraryTarget 和 library翼抠, webpack配置如下:
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'commonjs2',
library: 'util'
}
main.js 代碼如下:
import demo1 from './demo1.js';
demo1.js 代碼如下:
function sayHello() {
console.log('Hello');
}
function sayFunc() {
console.log('say');
}
export default {
sayHello: sayHello,
sayFunc: sayFunc
}
打包后的文件是如下格式代碼:
module.exports = (function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
// ....
}
return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
"./js/demo1.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n console.log('Hello');\n}\n\nfunction sayFunc() {\n console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n sayHello: sayHello,\n sayFunc: sayFunc\n});\n\n//# sourceURL=webpack:///./js/demo1.js?");
}),
"./js/main.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\nconsole.log(_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n_demo1_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"].sayHello();\n\n\n\n//# sourceURL=webpack:///./js/main.js?");
})
});
那么從npm社區(qū)下載庫后咙轩,使用庫的方法是如下:
const util1 = require('library-name-in-npm');
util1.xxx(); // xxx就是 某個方法名稱
3.5.2 commonjs
編寫的庫將通過commonjs導(dǎo)出。
webpack 配置代碼如下:
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'commonjs',
library: 'util'
}
打包后的文件變成如下代碼:
exports["util"] =
(function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
// ....
}
return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
"./js/demo1.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n console.log('Hello');\n}\n\nfunction sayFunc() {\n console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n sayHello: sayHello,\n sayFunc: sayFunc\n});\n\n//# sourceURL=webpack://util/./js/demo1.js?");
}),
"./js/main.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\n\n\n\n//# sourceURL=webpack://util/./js/main.js?");
})
});
如上代碼:配置了 output.library = 'LibraryName'; webpack就會輸出代碼格式為:
exports['LibraryName'] = lib_code;
使用庫的方法代碼如下:
require('library-name-in-npm')['LibraryName'].doSomething();
注意:lib_code 為所有的webpack打包的代碼阴颖;library-name-in-npm 是指模塊被發(fā)布到npm代碼倉庫的名稱活喊。
3.5.3 this
編寫的庫將通過this被賦值給library指定的名稱。
webpack配置代碼如下:
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'this',
library: 'util'
}
打包后的js文件如下格式:
this["util"] =
(function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
// ....
}
return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
"./js/demo1.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n console.log('Hello');\n}\n\nfunction sayFunc() {\n console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n sayHello: sayHello,\n sayFunc: sayFunc\n});\n\n//# sourceURL=webpack://util/./js/demo1.js?");
}),
"./js/main.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\n\n\n\n//# sourceURL=webpack://util/./js/main.js?");
})
});
使用庫的方法如下:
this.LibraryName.doSomething();
3.5.4 window
編寫的庫將通過window賦值給library指定的名稱量愧,輸出的代碼格式如下:
window['LibraryName'] = lib_code;
webpack的配置如下:
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'window',
library: 'util'
}
打包后的js代碼如下:
window["util"] =
(function(modules) { // webpackBootstrap
var installedModules = {};
function __webpack_require__(moduleId) {
// ....
}
return __webpack_require__(__webpack_require__.s = "./js/main.js");
})
({
"./js/demo1.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\nfunction sayHello() {\n console.log('Hello');\n}\n\nfunction sayFunc() {\n console.log('say');\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n sayHello: sayHello,\n sayFunc: sayFunc\n});\n\n//# sourceURL=webpack://util/./js/demo1.js?");
}),
"./js/main.js":
(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _demo1_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./demo1.js */ \"./js/demo1.js\");\n\n\n\n\n\n//# sourceURL=webpack://util/./js/main.js?");
})
});
使用庫的方法如下代碼:
window.LibraryName.doSomething();
3.5.5 global
編寫的庫將通過global賦值給通過library指定的名稱钾菊,即把庫掛載到global上,輸出格式的代碼如下:
global['LibraryName'] = lib_code;
和上面的window是一樣的偎肃,無非就是把window改成global煞烫;
使用庫的方法如下所示:
global.LibraryName.doSomething();
也可以打包成 'amd' 或 'umd' 模式結(jié)構(gòu)代碼, 在此介紹省略哦~
4. 模式(mode)
提供mode配置項(xiàng)累颂,告訴webpack使用對應(yīng)的模式滞详。
在webpack配置中提供mode選項(xiàng)。如下代碼:
module.exports = {
mode: 'production' // 或開發(fā)環(huán)境下 'development'
};
注意:使用 development模式代碼不會被壓縮紊馏,使用 production 代碼會被壓縮料饥。
也可以在CLI參數(shù)中傳遞,比如如下代碼:
webpack --mode=production
5.理解使用Loader
我們都知道webpack是適用于資源進(jìn)行打包的朱监,里面的所有資源都是模塊岸啡,內(nèi)部實(shí)現(xiàn)了對模塊資源進(jìn)行加載機(jī)制,但是webpack只能處理js模塊赫编,如果要處理其他類型的文件巡蘸,就需要使用loader進(jìn)行轉(zhuǎn)換。Loader可以理解為是模塊和資源的轉(zhuǎn)換器擂送,它本身也是一個函數(shù)赡若,接收源文件作為參數(shù),返回轉(zhuǎn)換的結(jié)果团甲。
配置loader逾冬,需要使用rules模塊來讀取和解析規(guī)則,它是一個數(shù)組躺苦,數(shù)組里面中的每一項(xiàng)描述了如何處理部分文件身腻。
1. 條件匹配: 通過配置test,include匹厘,exclude三個配置項(xiàng)來選中l(wèi)oader需要應(yīng)用規(guī)則的文件嘀趟。
2. 應(yīng)用規(guī)則: 對選中的文件通過use配置項(xiàng)來應(yīng)用loader,可以只應(yīng)用一個loader或者按照從右往左的順序應(yīng)用一組loader(切記:loader使用順序是從右往左的)愈诚,也可以向loader傳入?yún)?shù)她按。
3. 重置順序: Loader執(zhí)行的順序默認(rèn)是從右到左執(zhí)行的牛隅,但是我們可以通過enforce選項(xiàng)可以將其中一個Loader的執(zhí)行順序放到最前或最后。
具體的配置代碼可以參考如下:
module.exports = {
module: {
rules: [
{
// 正則匹配 以 js結(jié)尾的
test: /\.js$/,
// babel 轉(zhuǎn)換js文件酌泰,?cacheDirectory 用于緩存babel的編譯結(jié)果媒佣,加快重新編譯的速度
use: ['babel-loader?cacheDirectory'],
// include只包含src目錄下的js文件,加快查找速度
include: path.resolve(__dirname, 'src')
},
{
// 正則匹配以 styl結(jié)尾的文件
test: /\.styl$/,
/*
use使用順序從右到左執(zhí)行陵刹,先使用stylus-loader插件去解析以styl結(jié)尾的文件成css文件默伍,
再使用css-loader插件去讀取css文件,最后由 style-loader 將css的內(nèi)容注入到j(luò)avascript里面衰琐。
*/
use: ['style-loader', 'css-loader', 'stylus-loader'],
// 排除node_modules目錄下的文件
exclude: path.resolve(__dirname, 'node_modules')
},
{
// 對非文本文件采用file-loader去加載
test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
use: ['file-loader']
}
]
}
}
注意:在loader需要傳入多個參數(shù)時也糊,我們可以通過一個Object來描述,如下面是babel-loader配置:
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
},
/*
enforce: 'post' 的含義是將Loader執(zhí)行的順序放到最后
enforce: 'pre' 的含義是將Loader執(zhí)行順序放到最前面
*/
enforce: 'post'
}
]
當(dāng)然羡宙,上面的test狸剃,include,exclude配置也可以傳入多個狗热,因此他們也支持?jǐn)?shù)組的方式來傳遞了钞馁。比如如下配置:
{
test: [
/\.js$/,
/\.jsx$/
],
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'src2')
],
exclude: [
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, 'node_modules2')
]
}
6.理解noParse
該配置項(xiàng)可以讓webpack忽略對部分未采用模塊化文件的遞歸解析和處理,該忽略的文件不能包含import斗搞,require指攒, define等模塊化語句。
那么這樣做的好處是可以提高構(gòu)建性能僻焚,比如像一些庫jquery等就沒有必要去使用webpack去遞歸解析和處理了允悦。
使用配置代碼如下:
module.exports = {
module: {
noParse: /jquery|xxjs/
}
}
也可以使用函數(shù),但是webpack需要從3.0版本才支持虑啤;如下配置代碼:
module.exports = {
module: {
noParse: (content) => {
// content代表一個模塊的文件路徑
return /jquery|xxjs/.test(content);
}
}
}
7. 理解alias
resolve.alias 是通過別名來將原導(dǎo)入路徑映射成一個新的導(dǎo)入路徑隙弛。比如如下配置:
module.exports = {
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist')
},
resolve: {
alias: {
components: './src/components'
}
}
}
如上代碼配置,當(dāng)我通過 import xxx from 'components/xxx' 導(dǎo)入時狞山,實(shí)際上被alias替換成了
import xxx from './src/components/xxx';
8.理解extensions
在使用 import 導(dǎo)入文件時全闷,有時候沒有帶入文件的后綴名,webpack會自動帶上后綴去訪問文件是否存在萍启,那么默認(rèn)的后綴名為
['.js', '.json']; 即:
extensions: ['.js', '.json']; 如果我們想自己配置.vue后綴名总珠,防止在導(dǎo)入文件時候不需要加上后綴;我們可以使用webpack做如下配置:
module.exports = {
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist')
},
resolve: {
alias: {
components: './src/components'
},
extensions: ['.js', '.vue'];
}
}
9.理解Externals
Externals 用來告訴webpack在構(gòu)建代碼時使用了不處理應(yīng)用的某些依賴庫勘纯,依然可以在代碼中通過AMD局服,CMD或window全局方式訪問。
什么意思呢驳遵?就是說我們在html頁面中引入了jquery源文件后淫奔,比如如下代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="http://code.jquery.com/jquery-1.12.0.min.js"></script>
<link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="app"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
但是我們想在模塊化的源代碼里導(dǎo)入和使用jquery,首先我們肯定需要安裝下 jquery依賴包堤结,npm install --save jquery, 然后我們編寫如下代碼進(jìn)行使用:
const $ = require('jquery');
console.log($);
構(gòu)建打包后我們會發(fā)現(xiàn)輸出的文件里面包含jquery的內(nèi)容唆迁,這導(dǎo)致了jquery庫引入了兩次鸭丛,并且導(dǎo)致了bundle.js文件變得更大,浪費(fèi)加載流量唐责。因此 Externals 配置項(xiàng)就是來解決這個問題的鳞溉。
通過externals 可以告訴webpack在javascript運(yùn)行環(huán)境中已經(jīng)內(nèi)置了哪些全局變量,不用將這些代碼打包到代碼里面去妒蔚。
因此我們可以做如下配置:
module.export = {
externals: {
jquery: 'jQuery'
}
};