1、介紹
Webpack 是一個(gè)前端資源加載/打包工具。它將根據(jù)模塊的依賴關(guān)系進(jìn)行靜態(tài)分析,然后將這些模塊按照指定的規(guī)則生成對應(yīng)的靜態(tài)資源。
2、安裝
-
全局安裝
以下的 NPM 安裝方式,將使 webpack 在全局環(huán)境下可用:
npm install -g webpack
不推薦全局安裝 webpack。這會將您項(xiàng)目中的 webpack 鎖定到指定版本,并且在使用不同的 webpack 版本的項(xiàng)目中,可能會導(dǎo)致構(gòu)建失敗。
-
本地安裝
//安裝最新版本
npm install --save-dev webpack
//安裝特定版本
npm install --save-dev webpack@<version>
對于大多數(shù)項(xiàng)目,我們建議本地安裝。這可以使我們在引入破壞式變更(breaking change)的依賴時(shí),更容易分別升級項(xiàng)目。
-
安裝操作
首先我們創(chuàng)建一個(gè)目錄,初始化 npm,以及在本地安裝 webpack:
mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack


2、Webpack使用
-
簡單使用webpack
現(xiàn)在我們將創(chuàng)建以下目錄結(jié)構(gòu)和內(nèi)容

src/index.js
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello, webpack';
return element;
}
document.body.appendChild(component());
dist/index.html
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
執(zhí)行 webpack,會將我們的腳本作為入口起點(diǎn),然后輸出為 bundle.js
./node_modules/.bin/webpack src/index.js dist/bundle.js

在瀏覽器中打開 index.html,如果一切訪問都正常,你應(yīng)該能看到以下文本:'Hello webpack'。

-
使用配置文件webpack.config.js
大多數(shù)項(xiàng)目會需要很復(fù)雜的設(shè)置,這就是為什么 webpack 要支持配置文件。這比在終端(terminal)中輸入大量命令要高效的多,所以讓我們創(chuàng)建一個(gè)取代以上使用 CLI 選項(xiàng)方式的配置文件:webpack.config.js

webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
現(xiàn)在,讓我們再次執(zhí)行構(gòu)建,通過使用我們的新配置:
./node_modules/.bin/webpack --config webpack.config.js

NPM 腳本(NPM Scripts)
考慮到用 CLI 這種方式來運(yùn)行本地的 webpack 不是特別方便,我們可以設(shè)置一個(gè)快捷方式。在 package.json 添加一個(gè) npm 腳本(npm script):
package.json

現(xiàn)在,可以使用 npm run build 命令,來替代我們之前用到的較長命令。注意,使用 npm 的 scripts,我們可以通過模塊名,來引用本地安裝的 npm 包,而不是寫出完整路徑。這是大多數(shù)基于 npm 的項(xiàng)目遵循的標(biāo)準(zhǔn),允許我們直接調(diào)用 webpack,而不是去調(diào)用 ./node_modules/.bin/webpack。
現(xiàn)在運(yùn)行以下命令,然后看看你的腳本別名是否正常運(yùn)行:
npm run build

2、React和ES6以及JSX語法的支持
-
ES6以及JSX語法的支持
Babel其實(shí)是幾個(gè)模塊化的包,其核心功能位于稱為babel-core的npm包中,webpack可以把其不同的包整合在一起使用,對于每一個(gè)你需要的功能或拓展,你都需要安裝單獨(dú)的包(用得最多的是解析Es6的babel-preset-es2015包和解析JSX的babel-preset-react包)
安裝Babel依賴包
// npm一次性安裝多個(gè)依賴模塊,模塊之間用空格隔開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

.babelrc
{
"presets": ["react", "es2015"]
}
babel 在webpack.config.js 中進(jìn)行配置

React的支持
安裝 React 和 React-DOM
npm install --save react react-dom
使用React和ES6的語法更新index.js
注意這里我沒有直接渲染到 document.body上,而是特別選擇了 id 為 root 的 :
ReactDOM.render(<Hello />, document.getElementById('root'));
React官方不推薦將組件渲染到 document.body 上,因?yàn)檫@個(gè)節(jié)點(diǎn)很可能會改變,比如動態(tài)添加一個(gè) <script> 標(biāo)簽等,這將使 React 的 DOM diff計(jì)算變得更加困難。
import ReactDOM from 'react-dom';
import React, {Component} from 'react';
class Hello extends Component{
render() {
return (
<div>
Hello, webpack
</div>
);
}
}
ReactDOM.render(<Hello />, document.getElementById('root'));
在body中增加一個(gè)div標(biāo)簽
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<div id='root'></div>
<script src="bundle.js"></script>
</body>
</html>
運(yùn)行


3、通過 loader 引入任何其他類型的文件
-
加載 CSS
為了從 JavaScript 模塊中 import 一個(gè) CSS 文件,你需要在 module 配置中 安裝并添加 style-loader 和 css-loader:
npm install --save-dev style-loader css-loader
加載css在webpack.config.js 中進(jìn)行配置

我們嘗試一下,通過在項(xiàng)目中添加一個(gè)新的 style.css 文件,并將其導(dǎo)入到我們的 index.js 中:

src/style.css
.hello {
color: red;
}
src/index.js

運(yùn)行


CSS module
CSS modules 的技術(shù)就意在把JS的模塊化思想帶入CSS中來,通過CSS模塊,所有的類名,動畫名默認(rèn)都只作用于當(dāng)前模塊。Webpack從一開始就對CSS模塊化提供了支持,在CSS loader中進(jìn)行配置后,你所需要做的一切就是把”modules“傳遞到所需要的地方,然后就可以直接把CSS的類名傳遞到組件的代碼中,且這樣做只對當(dāng)前組件有效,不必?fù)?dān)心在不同的模塊中使用相同的類名造成沖突。
CSS module在webpack.config.js 中進(jìn)行配置

src/index.js

運(yùn)行


-
加載圖片
使用 file-loader,我們可以輕松地將這些內(nèi)容混合到 CSS 中:
npm install --save-dev file-loader
加載圖片在webpack.config.js 中進(jìn)行配置

增加一張test.png圖片

src/style.css

運(yùn)行


4、webpack plugin
-
HtmlWebpackPlugin
作用
1、html文件中引入的外部資源如script、link動態(tài)添加每次compile后的hash,防止引用緩存的外部文件問題
2、可以生成創(chuàng)建html入口文件,比如單頁面可以生成一個(gè)html文件入口,配置N個(gè)html-webpack-plugin可以生成N個(gè)頁面入口
安裝
npm install --save-dev html-webpack-plugin
創(chuàng)建src/index.tmpl.html,并刪除dist/index.html

src/index.tmpl.html
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<div id='root'></div>
</body>
</html>
webpack.config.js配置

運(yùn)行



了解更多 HtmlWebpackPlugin 插件
如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和選項(xiàng),那么你就應(yīng)該多多熟悉HtmlWebpackPlugin 倉庫。
你還可以看一下 html-webpack-template,除了默認(rèn)模板之外,還提供了一些額外的功能。
-
clean-webpack-plugin
你可能已經(jīng)注意到,由于過去的指南和代碼示例遺留下來,導(dǎo)致我們的 /dist 文件夾相當(dāng)雜亂。webpack 會生成文件,然后將這些文件放置在 /dist 文件夾中,但是 webpack 無法追蹤到哪些文件是實(shí)際在項(xiàng)目中用到的。
通常,在每次構(gòu)建前清理 /dist 文件夾,是比較推薦的做法,因此只會生成用到的文件。讓我們完成這個(gè)需求。
安裝
npm install clean-webpack-plugin --save-dev
webpack.config.js配置

現(xiàn)在執(zhí)行 npm run build,再檢查 /dist 文件夾。如果一切順利,你現(xiàn)在應(yīng)該不會再看到舊的文件,只有構(gòu)建后生成的文件!
5、開發(fā)常用配置
-
webpack-dev-server
webpack-dev-server 為你提供了一個(gè)簡單的 web 服務(wù)器,并且能夠?qū)崟r(shí)重新加載(live reloading)。
安裝
npm install --save-dev webpack-dev-server
webpack.config.js配置

localhost:8080 下建立服務(wù),并啟用gzip壓縮
package.json配置npm script

運(yùn)行


-
source map
介紹
當(dāng) webpack 打包源代碼時(shí),可能會很難追蹤到錯(cuò)誤和警告在源代碼中的原始位置。例如,如果將三個(gè)源文件(a.js, b.js 和 c.js)打包到一個(gè) bundle(bundle.js)中,而其中一個(gè)源文件包含一個(gè)錯(cuò)誤,那么堆棧跟蹤就會簡單地指向到 bundle.js。這并通常沒有太多幫助,因?yàn)槟憧赡苄枰獪?zhǔn)確地知道錯(cuò)誤來自于哪個(gè)源文件。
為了更容易地追蹤錯(cuò)誤和警告,JavaScript 提供了 source map 功能,將編譯后的代碼映射回原始源代碼。如果一個(gè)錯(cuò)誤來自于 b.js,source map 就會明確的告訴你。
devtool選項(xiàng)
以下選項(xiàng)是開發(fā)的理想選擇:
eval- 每個(gè)模塊都用eval()和執(zhí)行//@ sourceURL。這很快。主要的缺點(diǎn)是它不會正確顯示行號,因?yàn)樗挥成涞秸郫B代碼而不是原始代碼(沒有來自加載器的源地圖)。
eval-source-map- 每個(gè)模塊都執(zhí)行,eval()并將SourceMap作為DataUrl添加到eval()。最初它很慢,但它提供了快速的重建速度,并產(chǎn)生真實(shí)的文件。行號被正確映射,因?yàn)樗挥成涞皆即a。它產(chǎn)生最好的質(zhì)量SourceMaps進(jìn)行開發(fā)。
cheap-eval-source-map- 類似于eval-source-map每個(gè)模塊都執(zhí)行eval()。它是“便宜的”,因?yàn)樗鼪]有列映射,它只映射行號。它忽略來自Loader的SourceMaps,并且僅顯示與devtool類似的折疊代碼eval。
cheap-module-eval-source-map- cheap-eval-source-map但是,與此類似,處理來自加載程序的源地圖以獲得更好的結(jié)果。然而,Loader Source Maps簡化為每行一個(gè)映射。
對小到中型的項(xiàng)目中,eval-source-map是一個(gè)很好的選項(xiàng),再次強(qiáng)調(diào)你只應(yīng)該開發(fā)階段使用它。
cheap-module-eval-source-map方法構(gòu)建速度更快,但是不利于調(diào)試,推薦在大型項(xiàng)目考慮時(shí)間成本時(shí)使用。
webpack.config.js 配置

-
代碼分離及優(yōu)化
介紹
- OccurenceOrderPlugin :為組件分配ID,通過這個(gè)插件webpack可以分析和優(yōu)先考慮使用最多的模塊,并為它們分配最小的ID
- UglifyJsPlugin:壓縮JS代碼;
- ExtractTextPlugin:用于將 CSS 從主應(yīng)用程序中分離
安裝
OccurenceOrder 和 UglifyJS plugins 都是內(nèi)置插件,你需要做的只是安裝其它非內(nèi)置插件
npm install --save-dev extract-text-webpack-plugin
webpack.config.js配置

運(yùn)行

運(yùn)行后將css文件分離到style.css文件中

運(yùn)行后js文件被壓縮

-
緩存
介紹
通過使用 output.filename 進(jìn)行文件名替換,可以確保瀏覽器獲取到修改后的文件。[hash] 替換可以用于在文件名中包含一個(gè)構(gòu)建相關(guān)(build-specific)的 hash,但是更好的方式是使用 [chunkhash] 替換,在文件名中包含一個(gè) chunk 相關(guān)(chunk-specific)的哈希。
webpack.config.js 配置

-
生產(chǎn)環(huán)境構(gòu)建
現(xiàn)在,我們把 scripts 重新指向到新配置。我們將 npm start 定義為開發(fā)環(huán)境腳本,并在其中使用 webpack-dev-server,將 npm run build 定義為生產(chǎn)環(huán)境腳本
創(chuàng)建webpack.config.prod.js文件

webpack.config.prod.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: {
loader: "css-loader",
options: {
modules: true,
minimize: true
}
}
})
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
},
plugins: [
new ExtractTextPlugin("[name].[contenthash].css"),
new CleanWebpackPlugin([__dirname + "/dist"]),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({
template: __dirname + "/src/index.tmpl.html",//new 一個(gè)這個(gè)插件的實(shí)例,并傳入相關(guān)的參數(shù)
minify: {
removeComments: true, //是否去掉注釋
collapseWhitespace: true, //是否去掉空格
minifyJS: true, //是否壓縮html里的js(使用uglify-js進(jìn)行的壓縮)
minifyCSS: true, //是否壓縮html里的css(使用clean-css進(jìn)行的壓縮)
},
})
],
};
webpack.config.js去除一些配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist')
},
devtool: 'eval-source-map',
module: {
rules: [{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_modules/
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
modules: true
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/src/index.tmpl.html",//new 一個(gè)這個(gè)插件的實(shí)例,并傳入相關(guān)的參數(shù)
minify: {
removeComments: true, //是否去掉注釋
collapseWhitespace: true, //是否去掉空格
minifyJS: true, //是否壓縮html里的js(使用uglify-js進(jìn)行的壓縮)
minifyCSS: true, //是否壓縮html里的css(使用clean-css進(jìn)行的壓縮)
},
})
],
devServer: {
//一切服務(wù)都啟用gzip 壓縮:
compress: true,
port: 8080
},
};
package.json配置

最后
本篇文章將涵蓋webpack安裝、使用、plugin、開發(fā)中常用的配置、緩存等等,我在邊寫邊跟著構(gòu)建,并將每一步構(gòu)建生成git記錄,代碼傳送門,如果有疑問的地方歡迎在下面評論或者私信給我。
如果您覺得有幫助,請給我點(diǎn)個(gè)??。