
1. Entry
入口(Entry)指示webpack 以哪個檔案為入口起點開始打包,分析建構內部相依圖。
2. Output
輸出(Output)指示webpack 打包後的資源bundles 輸出到哪裡去,以及如何命名。
3. Loader
webpack 只能理解JavaScript 和JSON 文件,這是webpack 開箱可用的自帶能力。 loader 讓webpack 能夠去處理其他類型的文件,並將它們轉換為有效模組,以供應用程式使用,以及被添加到依賴圖中。
4. Plugins
插件(Plugins)可以用來執行範圍更廣的任務。插件的範圍包括,從打包最佳化和壓縮,一直到重新定義環境中的變數等。
5. Mode
模式(Mode)指示webpack 使用對應模式的配置。
以下就來跟大家詳細介紹一下webpack這五個核心概念。
entry 物件是用於webpack 查找啟動並建構bundle。 entry 是應用程式的起點入口,從這個起點開始,應用程式啟動執行。如果傳遞一個數組,那麼數組的每一項都會執行。入口起點(entry point) 指示webpack 應該使用哪個模組,來作為建立其內部依賴圖(dependency graph) 的開始。進入入口起點後,webpack 會找出有哪些模組和函式庫是入口起點(直接和間接)依賴的。
簡單規則:每個HTML 頁面都有一個入口起點。單頁應用(SPA):一個入口起點,多頁應用(MPA):多個入口起點。
預設值是./src/index.js ,但你可以透過在webpack configuration 中配置entry屬性,來指定一個(或多個)不同的入口起點。例如:
//單入口--字串module.exports = {
entry: './path/to/my/entry/file.js',
};
//多入口--數組module.exports = {
entry: ['./src/index.js', './src/add.js']
};
//多入口--物件module.exports = {
entry: {
home: './home.js',
about: './about.js',
contact: './contact.js'
}
}; entry的值類型:
字串:單一入口,打包形成一個chunk,最終只會輸出一個bundle文件,chunk 的名稱預設是main
陣列:多入口,所有的入口文件最終也只會形成一個chunk,最終輸出一個bundle 文件,chunk 的名稱預設為main。一般只用在HMR 功能中讓html熱更新生效
物件:多入口,有多少個key 就會形成多少個chunk,也就輸出多少個bundle 文件,每個鍵(key)會是chunk 的名稱。在物件類型中,每個key的值還可以是一個數組,而不僅僅是一個字串
output指示webpack 如何去輸出、以及在哪裡輸出你的bundle、asset 和其他你所打包或使用webpack 載入的任何內容。輸出的bundle 的預設值是./dist/main.js ,其他產生檔案預設放置在./dist資料夾中。
你可以透過在設定中指定一個output字段,來設定這些處理過程:
//webpack.config.js
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js',
},
};我們可以透過output.filename和output.path屬性,來告訴webpack bundle 的名稱,以及bundle 產生到哪裡。
2.1、output.filename(檔案名稱和目錄)
此選項決定了每個輸出bundle 的目錄和名稱。這些bundle 將寫入到output.path選項指定的目錄下。
對於單一入口起點,filename 會是一個靜態名稱。然而,當透過多個入口起點(entry point)、程式碼分割(code splitting)或各種插件(plugin)建立多個bundle,應該使用其他方法來讓每個bundle 都有一個唯一的名稱。
//單入口時:
module.exports = {
//...
output: {
filename: 'js/bundle.js'
}
};
//多入口--使用入口名稱:
module.exports = {
//...
output: {
filename: '[name].bundle.js'
}
};
//多入口--使用每次建置過程中,唯一的hash 產生module.exports = {
//...
output: {
filename: '[name].[hash].bundle.js'
}
};
... 2.2、output.path(檔案目錄)
output.path 指定所有輸出檔案的目錄,即將來所有資源輸出的公共目錄。 path 必須是絕對路徑。
module.exports = {
//...
output: {
path: path.resolve(__dirname, 'dist/assets')
}
}; 2.3、output.publicPath(引用資源的路徑前綴)
publicPath 指定的是html 檔案中的所有資源所引入的公共路徑前綴。它不會對生成檔案的路徑造成影響,而是在html 檔案引入各種資源時,將publicPath 作為前綴加到引入資源的路徑前面。
實例:
在vue-cli 產生的webpack 配置中,生產環境下publicPath 的值預設為'/',即目前目錄的根目錄。


打包過後,我們打開html 文件,可以看到html 文件中引入的資源路徑為:

可以看到,都在路徑前面加了/ 符號。當我們開啟瀏覽器存取產生的html 檔案時,會發現報錯,資源存取不到,封包404,此時資源的存取類似如下:

在伺服器上可能會是如下,但訪問一樣可能會有問題。

我們可以將publicPath 修改為相對路徑,或直接把它註解掉也行。
2.3.1、path和publicPath的區別
打包后文件在硬盘中的存储位置,是webpack所有文件的输出的路径,必须是绝对路径。比如:输出的js、图片,HtmlWebpackPlugin生成的html文件等,都会存放在以path为基础的目录下。2.4、output.chunkFilename(非入口chunk的名稱)
output.chunkFilename 決定了非入口(non-entry) chunk 檔案的名稱。也就是除了入口檔案產生的chunk外,其他檔案產生的chunk檔案命名。
module.exports = {
//...
output: {
chunkFilename: 'js/[name]_chunk.js' //非入口chunk的名稱}
}; webpack 本身只能打包JavaScript 和JSON 檔案( webpack3+和webpack2+內建可處理JSON文件,但webpack1+并不支持,需要引入json-loader ),這是webpack 開箱可用的自帶能力。 webpack 本身不支援打包其他類型文件,例如css、vue 等,但我們可以透過各種loader 來讓webpack 去處理這些類型的文件。 loader 可以將檔案從不同的語言(如TypeScript)轉換為JavaScript 或將內嵌圖片轉換為data URL,loader 甚至允許你直接在JavaScript 模組中import CSS檔案!
透過使用不同的loader , webpack有能力調用外部的腳本或工具,實現對不同格式的文件的處理,比如說分析轉換scss為css,或者把下一代的JS檔(ES6,ES7)轉換為現代瀏覽器相容的JS檔。對React的開發而言,適當的Loaders可以把React的中所使用的JSX檔轉換成JS檔。
在webpack 的設定中,loader 有兩個屬性:
test屬性,辨識出哪些檔案會被轉換。
use屬性,定義出在進行轉換時,應該使用哪個loader。
include/exclude(可选):手動新增必須處理的檔案(資料夾)或封鎖不需要處理的檔案(資料夾)
query(可选) :為loaders提供額外的設定選項
//範例:webpack.config. js
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
rules: [
{ test: /.txt$/, loader: 'raw-loader' },
{ test: /.css$/, use: ['style-loader', 'css-loader'] } //使用多個loader的話應該用use
],
},
};以上配置中,對單獨的module 物件定義了rules屬性,裡麵包含兩個必須屬性: test和use 。這相當於告訴webpack 編譯器在碰到require() / import語句中被解析為'.txt' 的路徑時,在對它打包之前,先使用raw-loader轉換一下。
使用多個loader的話應該用use,use 數組中的loader 執行順序:從右到左,依序執行。例如上面的css 文件,首先css-loader 會將css 檔案編譯成JS 載入到JS檔案中,然後再由style-loader 建立style 標籤,將JS 中的樣式資源插入到head 標籤中。
3.1.CSS-loader
webpack提供兩個工具處理樣式表, css-loader和style-loader ,二者處理的任務不同。 css-loader使你能夠使用類似import的方法來引入css 文件, style-loader將所有的計算後的樣式加入頁面中,二者組合在一起使你能夠把樣式表嵌入webpack打包後的JS文件中,由此就可以在JS檔中引入css檔了。
//安裝npm install --save-dev style-loader css-loader //css-loader版本太高編譯可能會出錯,建議降低版本例如[email protected] 可用
//使用module.exports = {
…
module: {
rules: [
{
test: /(.jsx|.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /.css$/, //對同一個檔案引入多個loader的方法。 loader的作用順序是後面的loader先開始作用use: [
{
loader: "style-loader"
}, {
loader: "css-loader"
}
]
}
]
}
};假設有一個main.css 檔案:
body {
backgroud: green;
}為了讓webpack能找到」main.css「文件,我們把它導入」main.js 「中,如下:
//main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css';//使用require導入css檔案render(<Greeter />, document.getElementById('root'));通常情況下,css會和js打包到同一個檔案中,並不會打包為一個單獨的css檔。不過透過合適的設定webpack也可以把css打包為單獨的檔案的。
loader 用於轉換某些類型的模組,而插件則可以用於執行範圍更廣的任務,包括:打包優化、壓縮、資源管理、注入環境變數等。插件目的在於解決loader 無法實現的其他事。
要使用某個插件,我們需要透過npm安裝它,然後在plugins 屬性下新增該插件的一個實例。由於插件可以攜帶參數/選項,你必須在webpack 配置中,向plugins屬性傳入new實例。多數插件可以透過選項自訂,你也可以在一個設定檔中因為不同目的而多次使用同一個插件。
//webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 透過npm 安裝const webpack = require('webpack'); // 用於存取內建外掛module.exports = {
module: {
rules: [{ test: /.txt$/, use: 'raw-loader' }],
},
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};在上面的範例中, html-webpack-plugin為應用程式產生一個HTML 文件,並自動注入所有產生的bundle。
4.1、BannerPlugin插件(新增版權說明)
下面我們新增了一個為打包後程式碼添加版權聲明的插件。該插件是webpack中的內建插件不用安裝。
const webpack = require('webpack');
module.exports = {
…
module: {
rules: [
{
test: /(.jsx|.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true
}
}, {
loader: "postcss-loader"
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('wenxuehai版權所有,翻版必究')
],
}; 4.2、Hot Module Replacement 外掛程式(熱載入)
Hot Module Replacement (HMR)是webpack裡很有用的一個插件,它允許你在修改元件程式碼後,自動刷新即時預覽修改後的效果。熱加載和webpack-dev-server不同,熱替換在應用運行時,無需刷新頁面,便能查看程式碼更新後的效果,就跟直接在瀏覽器上修改dom樣式一樣,而webpack-dev-server是要刷新頁面的。
(1) 在webpack設定檔中加入HMR插件;
(2) 在Webpack Dev Server中新增「hot」參數;
4.2.1、react 實作熱載入
React模組可以使用Babel實作功能熱載入。 Babel有一個叫做react-transform-hrm的插件,可以在不對React模組進行額外的配置的前提下讓HMR正常工作;
安裝react-transform-hmr
npm install --save-dev babel-plugin-react-transform react -transform-hmr
const webpack = require('webpack');
module.exports = {
entry: __dirname + "/app/main.js",//已多次提及的唯一入口檔案output: {
path: __dirname + "/public",
filename: "bundle.js"
},
devtool: 'eval-source-map',
devServer: {
contentBase: "./public",//本地伺服器所載入的頁面所在的目錄historyApiFallback: true,//不跳到inline: true,
hot: true
},
module: {
rules: [
{
test: /(.jsx|.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true
}
}, {
loader: "postcss-loader"
}
]
}
]
},
plugins: [
new webpack.BannerPlugin('版權所有,翻版必究'),
new webpack.HotModuleReplacementPlugin() //熱載入插件],
};配置Babel
// .babelrc
{
"presets": ["react", "env"],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
} //Greeter,js
import React, {
Component
} from 'react'
import styles from './main.css'
class Greeter extends Component {
render() {
return (
< div>
<h1>
aaaf
</h1>
</div>
);
}
}
export default Greeter //main.js
import React from 'react';
import {
render
} from 'react-dom';
import Greeter from './greeter.js';
render( < Greeter / > , document.getElementById('root'));現在如果我們就可以實現熱加載模組了,每次保存就能在瀏覽器上直接看到更新內容,瀏覽器不必刷新也不會自動刷新。
(有時候沒有效果可能是版本問題)
4.3、ExtractTextWebpackPlugin插件(抽離css)
在預設情況下,webpack 不會將css 樣式作為一個獨立文件,而是會將css 也打包到js 文件中,打包生成的js 檔案會在渲染時透過js 語法來將樣式透過style 標籤的形式來插入頁面中。但這樣的話,打包出來的bundle 檔案可能會太大,此時我們可以透過ExtractTextWebpackPlugin 插件來將css 樣式獨立成css 檔案。
ExtractTextWebpackPlugin 外掛程式會將入口chunk 中引用到的*.css(包括引入的css檔案和vue檔案中style所寫的樣式),移到一個獨立分離的CSS 檔案中。 ExtractTextPlugin對每個入口chunk 都會產生一個對應的css文件,也就是說一個入口對應著一個css 文件,多個入口的話就會分別生成多個對應的css 文件。
透過ExtractTextWebpackPlugin 插件,你的樣式將不再內嵌到JS bundle 中,而是會放到一個單獨的CSS 檔案(即styles.css )當中。 如果你的樣式檔案大小較大,這會做更快提前加載,因為CSS bundle 會跟JS bundle 並行加載。
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'), //ExtractTextPlugin 對每個入口chunk 都產生一個對應的文件,所以當你配置多個入口chunk 的時候,必須使用[ name], [id] 或[contenthash]
// allChunks: true, //當使用`CommonsChunkPlugin` 並且在公共chunk 中有提取的chunk(來自`ExtractTextPlugin.extract`)時,`allChunks` **必須設定為`true`。
}),
]
} 4.3.1、allChunks選項(是否也將非同步載入的樣式一起擷取出來)
ExtractTextWebpackPlugin 外掛程式的allChunks 選項的預設值為false。
allChunks 選項的意思是是否需要將非同步載入的樣式一起提取出來。因為在預設情況下,就算使用了ExtractTextWebpackPlugin 插件,如果該樣式或樣式檔案是非同步載入的話,那麼這些樣式是不會被提取到獨立的css 檔案中的,而是仍然會打包到js 檔案中。
所以allChunks:false為預設值,預設是從entry 的入口提取程式碼,但是不會提取非同步載入的程式碼; allChunks:true則是提取所有模組的程式碼(包含非同步載入的模組)到一個檔案裡面。如果使用了非同步載入樣式,但是allChunks 又設為了false,那麼我們就需要設定ExtractTextPlugin.extract 的fallback, fallback是在非同步程式碼載入的css 程式碼沒有被擷取的情況下, 以style-loader的情況去載入非同步組件的樣式。
可參考:
https://github.com/sevenCon/blog-github/blob/master/articles/webpack學習筆記(2)-ExtractTextWebpackPlugin的使用.md
https://blog.csdn.net/weixin_41134409/article/details /88416356
透過選擇development , production或none之中的一個,來設定mode參數,你可以啟用webpack 內建在對應環境下的最佳化。其預設值為production 。
module.exports = {
mode: 'production',
};在設定檔中直接設定mode 選項將告知webpack 使用對應模式的內建最佳化,mode選項有development、production、none。
development : 開發模式,打包的程式碼不會被壓縮,開啟程式碼調試,
production : 生產模式,則正好反之。

將mode 設為development或production,webpack會自動同時也設定process.env.NODE_ENV 的值,我們可以在任何資料夾中直接拿到該值。但如果只設定NODE_ENV ,則不會自動設定mode 。 (在node中,全域變數process 表示的是目前的node流程。process.env 屬性包含使用者環境的資訊。process.env 本身並不存在NODE_ENV這個屬性,我們一般會自己去定義NODE_ENV 屬性,用它來判斷是生產環境還是開發環境)
(請注意:mode選項是webpack4新增的,在4之前都是用DefinePlugin插件設置,webpack4把DefinePlugin刪除了)
5.1、vue-cli項目mode配置詳解
在webpack 中,一般都會在設定檔中配置NODE_ENV 的值。在使用vue-cli 預設產生的vue 專案中,NODE_ENV 配置如下:
//webpack.dev.conf.js 檔案下,引入了dev.env.js 檔案new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}), //dev.env.js 檔案中module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
}) //webpack.prod.conf.js 檔案下,引入了prod.env.js 檔案const env = require('../config/prod.env')
new webpack.DefinePlugin({
'process.env': env
}), //prod.env.js 檔案中module.exports = {
NODE_ENV: '"production"'
}從上面可以知道,在開發環境下,設定檔將NODE_ENV 配置成了'development';在生產環境下,設定檔將NODE_ENV 配置成了'production'。
我們在執行專案時,會執行npm run dev 或npm run build,這兩個指令時使用了開發環境或生產環境的設定檔來產生執行項目,由此也對應著設定了對應的NODE_ENV 的值,我們也就能夠在專案的任一檔案中(設定檔不一定,因為要看配置了NODE_ENV 的值的設定檔有沒有生效了才行)取得到對應的NODE_ENV 的值。
5.2、process.env.NODE_ENV配置
process 是node 的全域變量,且process 有env 這個屬性,但是沒有NODE_ENV 這個屬性。 NODE_ENV 變數並不是process.env 直接就有的,而是透過設定得到的,但是NODE_ENV變數通常約定用來定義環境類型。這個變數的作用是:我們可以透過判斷這個變數來區分開發環境或生產環境。
(1)可以透過webpack的內建外掛DefinePlugin 來設定全域變數值:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),設定完後在執行腳本上可以取到該值,例如:
// main.js console.log(process.env.NODE_ENV); //production
但是在webpack的設定檔webpack.config.js 中取不到該值。
(2)透過cross-env 套件設定
先下載cross-env 套件:
cnpm i cross-env -D
設定package.json 檔案:
"build": "cross-env NODE_ENV=test webpack --config webpack.config.js"
此時在設定檔中可以取到該值(process.env.NODE_ENV),但在可執行腳本中取不到,需要配合DefinePlugin 外掛程式使用