1、介紹
Webpack 是一個前端資源加載/打包工具。它將根據模塊的依賴關系進行靜態分析,然后將這些模塊按照指定的規則生成對應的靜態資源。
2、安裝
-
全局安裝
以下的 NPM 安裝方式,將使 webpack 在全局環境下可用:
npm install -g webpack
不推薦全局安裝 webpack。這會將您項目中的 webpack 鎖定到指定版本,并且在使用不同的 webpack 版本的項目中,可能會導致構建失敗。
-
本地安裝
//安裝最新版本
npm install --save-dev webpack
//安裝特定版本
npm install --save-dev webpack@<version>
對于大多數項目,我們建議本地安裝。這可以使我們在引入破壞式變更(breaking change)的依賴時,更容易分別升級項目。
-
安裝操作
首先我們創建一個目錄,初始化 npm,以及在本地安裝 webpack:
mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack
2、Webpack使用
-
簡單使用webpack
現在我們將創建以下目錄結構和內容
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>
執行 webpack,會將我們的腳本作為入口起點,然后輸出為 bundle.js
./node_modules/.bin/webpack src/index.js dist/bundle.js
在瀏覽器中打開 index.html,如果一切訪問都正常,你應該能看到以下文本:'Hello webpack'。
-
使用配置文件webpack.config.js
大多數項目會需要很復雜的設置,這就是為什么 webpack 要支持配置文件。這比在終端(terminal)中輸入大量命令要高效的多,所以讓我們創建一個取代以上使用 CLI 選項方式的配置文件: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')
}
};
現在,讓我們再次執行構建,通過使用我們的新配置:
./node_modules/.bin/webpack --config webpack.config.js
NPM 腳本(NPM Scripts)
考慮到用 CLI 這種方式來運行本地的 webpack 不是特別方便,我們可以設置一個快捷方式。在 package.json 添加一個 npm 腳本(npm script):
package.json
現在,可以使用 npm run build 命令,來替代我們之前用到的較長命令。注意,使用 npm 的 scripts,我們可以通過模塊名,來引用本地安裝的 npm 包,而不是寫出完整路徑。這是大多數基于 npm 的項目遵循的標準,允許我們直接調用 webpack,而不是去調用 ./node_modules/.bin/webpack。
現在運行以下命令,然后看看你的腳本別名是否正常運行:
npm run build
2、React和ES6以及JSX語法的支持
-
ES6以及JSX語法的支持
Babel其實是幾個模塊化的包,其核心功能位于稱為babel-core的npm包中,webpack可以把其不同的包整合在一起使用,對于每一個你需要的功能或拓展,你都需要安裝單獨的包(用得最多的是解析Es6的babel-preset-es2015包和解析JSX的babel-preset-react包)
安裝Babel依賴包
// npm一次性安裝多個依賴模塊,模塊之間用空格隔開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
.babelrc
{
"presets": ["react", "es2015"]
}
babel 在webpack.config.js 中進行配置
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 上,因為這個節點很可能會改變,比如動態添加一個 <script> 標簽等,這將使 React 的 DOM diff計算變得更加困難。
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中增加一個div標簽
<html>
<head>
<title>Getting Started</title>
</head>
<body>
<div id='root'></div>
<script src="bundle.js"></script>
</body>
</html>
運行
3、通過 loader 引入任何其他類型的文件
-
加載 CSS
為了從 JavaScript 模塊中 import 一個 CSS 文件,你需要在 module 配置中 安裝并添加 style-loader 和 css-loader:
npm install --save-dev style-loader css-loader
加載css在webpack.config.js 中進行配置
我們嘗試一下,通過在項目中添加一個新的 style.css 文件,并將其導入到我們的 index.js 中:
src/style.css
.hello {
color: red;
}
src/index.js
運行
CSS module
CSS modules 的技術就意在把JS的模塊化思想帶入CSS中來,通過CSS模塊,所有的類名,動畫名默認都只作用于當前模塊。Webpack從一開始就對CSS模塊化提供了支持,在CSS loader中進行配置后,你所需要做的一切就是把”modules“傳遞到所需要的地方,然后就可以直接把CSS的類名傳遞到組件的代碼中,且這樣做只對當前組件有效,不必擔心在不同的模塊中使用相同的類名造成沖突。
CSS module在webpack.config.js 中進行配置
src/index.js
運行
-
加載圖片
使用 file-loader,我們可以輕松地將這些內容混合到 CSS 中:
npm install --save-dev file-loader
加載圖片在webpack.config.js 中進行配置
增加一張test.png圖片
src/style.css
運行
4、webpack plugin
-
HtmlWebpackPlugin
作用
1、html文件中引入的外部資源如script、link動態添加每次compile后的hash,防止引用緩存的外部文件問題
2、可以生成創建html入口文件,比如單頁面可以生成一個html文件入口,配置N個html-webpack-plugin可以生成N個頁面入口
安裝
npm install --save-dev html-webpack-plugin
創建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配置
運行
了解更多 HtmlWebpackPlugin 插件
如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和選項,那么你就應該多多熟悉HtmlWebpackPlugin 倉庫。
你還可以看一下 html-webpack-template,除了默認模板之外,還提供了一些額外的功能。
-
clean-webpack-plugin
你可能已經注意到,由于過去的指南和代碼示例遺留下來,導致我們的 /dist 文件夾相當雜亂。webpack 會生成文件,然后將這些文件放置在 /dist 文件夾中,但是 webpack 無法追蹤到哪些文件是實際在項目中用到的。
通常,在每次構建前清理 /dist 文件夾,是比較推薦的做法,因此只會生成用到的文件。讓我們完成這個需求。
安裝
npm install clean-webpack-plugin --save-dev
webpack.config.js配置
現在執行 npm run build,再檢查 /dist 文件夾。如果一切順利,你現在應該不會再看到舊的文件,只有構建后生成的文件!
5、開發常用配置
-
webpack-dev-server
webpack-dev-server 為你提供了一個簡單的 web 服務器,并且能夠實時重新加載(live reloading)。
安裝
npm install --save-dev webpack-dev-server
webpack.config.js配置
localhost:8080 下建立服務,并啟用gzip壓縮
package.json配置npm script
運行
-
source map
介紹
當 webpack 打包源代碼時,可能會很難追蹤到錯誤和警告在源代碼中的原始位置。例如,如果將三個源文件(a.js, b.js 和 c.js)打包到一個 bundle(bundle.js)中,而其中一個源文件包含一個錯誤,那么堆棧跟蹤就會簡單地指向到 bundle.js。這并通常沒有太多幫助,因為你可能需要準確地知道錯誤來自于哪個源文件。
為了更容易地追蹤錯誤和警告,JavaScript 提供了 source map 功能,將編譯后的代碼映射回原始源代碼。如果一個錯誤來自于 b.js,source map 就會明確的告訴你。
devtool選項
以下選項是開發的理想選擇:
eval- 每個模塊都用eval()和執行//@ sourceURL。這很快。主要的缺點是它不會正確顯示行號,因為它被映射到折疊代碼而不是原始代碼(沒有來自加載器的源地圖)。
eval-source-map- 每個模塊都執行,eval()并將SourceMap作為DataUrl添加到eval()。最初它很慢,但它提供了快速的重建速度,并產生真實的文件。行號被正確映射,因為它被映射到原始代碼。它產生最好的質量SourceMaps進行開發。
cheap-eval-source-map- 類似于eval-source-map每個模塊都執行eval()。它是“便宜的”,因為它沒有列映射,它只映射行號。它忽略來自Loader的SourceMaps,并且僅顯示與devtool類似的折疊代碼eval。
cheap-module-eval-source-map- cheap-eval-source-map但是,與此類似,處理來自加載程序的源地圖以獲得更好的結果。然而,Loader Source Maps簡化為每行一個映射。
對小到中型的項目中,eval-source-map是一個很好的選項,再次強調你只應該開發階段使用它。
cheap-module-eval-source-map方法構建速度更快,但是不利于調試,推薦在大型項目考慮時間成本時使用。
webpack.config.js 配置
-
代碼分離及優化
介紹
- OccurenceOrderPlugin :為組件分配ID,通過這個插件webpack可以分析和優先考慮使用最多的模塊,并為它們分配最小的ID
- UglifyJsPlugin:壓縮JS代碼;
- ExtractTextPlugin:用于將 CSS 從主應用程序中分離
安裝
OccurenceOrder 和 UglifyJS plugins 都是內置插件,你需要做的只是安裝其它非內置插件
npm install --save-dev extract-text-webpack-plugin
webpack.config.js配置
運行
運行后將css文件分離到style.css文件中
運行后js文件被壓縮
-
緩存
介紹
通過使用 output.filename 進行文件名替換,可以確保瀏覽器獲取到修改后的文件。[hash] 替換可以用于在文件名中包含一個構建相關(build-specific)的 hash,但是更好的方式是使用 [chunkhash] 替換,在文件名中包含一個 chunk 相關(chunk-specific)的哈希。
webpack.config.js 配置
-
生產環境構建
現在,我們把 scripts 重新指向到新配置。我們將 npm start 定義為開發環境腳本,并在其中使用 webpack-dev-server,將 npm run build 定義為生產環境腳本
創建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 一個這個插件的實例,并傳入相關的參數
minify: {
removeComments: true, //是否去掉注釋
collapseWhitespace: true, //是否去掉空格
minifyJS: true, //是否壓縮html里的js(使用uglify-js進行的壓縮)
minifyCSS: true, //是否壓縮html里的css(使用clean-css進行的壓縮)
},
})
],
};
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 一個這個插件的實例,并傳入相關的參數
minify: {
removeComments: true, //是否去掉注釋
collapseWhitespace: true, //是否去掉空格
minifyJS: true, //是否壓縮html里的js(使用uglify-js進行的壓縮)
minifyCSS: true, //是否壓縮html里的css(使用clean-css進行的壓縮)
},
})
],
devServer: {
//一切服務都啟用gzip 壓縮:
compress: true,
port: 8080
},
};
package.json配置
最后
本篇文章將涵蓋webpack安裝、使用、plugin、開發中常用的配置、緩存等等,我在邊寫邊跟著構建,并將每一步構建生成git記錄,代碼傳送門,如果有疑問的地方歡迎在下面評論或者私信給我。
如果您覺得有幫助,請給我點個??。