參考:中文文檔:https://www.webpackjs.com/
1️⃣. 引入 | Demo
當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序需要(依賴)的每個模塊(modules),然後將所有這些模塊打包成一個或多個 assets。
操作 - 創建package.json文件
- 1、創建一個webpack的項目根目錄(如wptest),然後在根目錄進行命令行操作:
npm init -y 初始化一個package.json文件
然後將webpack安裝在本地 npm i -D webpack
注意:
- 不推薦使用全局安裝
- 超過4.0的webpack版本,需要額外再安裝一個webpack-cli 命令行工具
npm install --save-dev webpack
完成安裝之後如下如所示:
- 2、在根目錄下方新建如下文件夾與文件(dist 、src、index.html、index.js):
- 3、接着,我們做一些嘗試:使用loadsh工具庫寫一個小測試:首先先安裝 開發依賴 loadsh 工具庫
npm i lodash -P
lodash 參考:https://www.lodashjs.com/
然後在````index.js```文件中寫如下代碼:
import _ from 'lodash';
let createDomElement = ()=>{
let dom = document.createElement('div');
dom.innerHTML = _.join(['https://','blog','.csdn','.net','/imaginecode'],'');
return dom;
}
document.body.appendChild(createDomElement());
接着,在index.html文件中寫入:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
<body>
<script type="text/javascript" src="./main.js"></script>
</body>
</html>
- 4、在根目錄下添加
webpack.config.js
,編寫webpack.config.js
文件
webpack.config.js遵循Nodejs
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname,'./dist')
}
}
- 5、執行webpack
npx webpack
進行構建
你可以在package.json 的scripts
下加入"build":"npx webpack"
以後就只需要執行 npm run build
執行成功後,然後我們在瀏覽器中打開index.html
2️⃣. 一些webpack概念
前面我們用一個小Case讓大家瞭解了一下Webpack。下面,我們在開始進一步完善我們的Case前,需要先知道一些webpack的先驗知識/概念。這些概念也可以在Case的進行中在https://www.webpackjs.com/
中文文檔中進行查閱。
npm 與 package.json
- –save-dev 安裝的 插件,被寫入到 devDependencies 對象裏面去
- –save 安裝的插件,被寫入到 dependencies 對象裏面去
- devDependencies 裏面的插件只用於開發環境,不用於生產環境
- dependencies 是需要發佈到生產環境(production)
webpack.config.js
配置大都是在導出的模塊(module.exports
)對象體中完成的:
module.exports = {
}
- mode 開發模式
module.exports = {
mode: 'production', //設置開發模式爲生產模式
}
- entry入口文件
module.exports = {
mode: 'production',
entry: { //入口文件
app:'./src/index.js',
},
}
入口配置,告訴webpack應該從哪個模塊開始(上面從src/index.js這個模塊),作爲構建內部依賴圖
的開始。可以配置多個。
- output 輸出文件
module.exports = {
mode: 'production',
entry: {
app:'./src/index.js',
print:'./src/print.js'
},
output: { //輸出文件
filename: '[name].bundle.js', //如何命名
path:path.resolve(__dirname,'./dist') //在哪裏輸出bundles
},
output屬性,則是告訴webpack在哪裏(path
)輸出它所創建的assets(或者說bundles),並告訴Webpack要怎樣命名這些文件(filename
)。
module 模塊
module 模塊中的選項決定了如何處理項目中不同類型的模塊。
- module.noParse :RegExp | [RegExp] | function
這項能防止webpack解析與給定的正則表達式相匹配的文件。需要注意的是,不進行解析的文件中不能含有import,require,define
等其他導入機制。通常,我們可以選擇不對大文件庫進行解析,如jquery。
- module.rules
rules,顧名思義,是一種規則數組。即,當創建模塊時,根據規則數組進行匹配。同時,這些規則能夠對模塊應用loader
等。
- module.rules.test: Condition
Condition一般提供一個正則表達式或者正則表達式數組RegExp | [RegExp]
同時還有其他條件,如:
{include:Condition}
:匹配特定條件,一般是提供一個字符串或者字符串數組。
{exclude:Condition}
:排除特定條件。一般是提供一個字符串或字符串數組。
{and: [Condition]}
:必須匹配數組中的所有條件。
{ or: [Condition] }
: 匹配數組中任何一個條件。
{ not: [Condition] }
: 必須排除這個條件。
module: {
rules: [
{
test: /\.css$/,
include: [
path.resolve(__dirname, "app/styles"),
path.resolve(__dirname, "src/styles")
],
use: ['style-loader', 'css-loader']
}
]
}
- rules.use
告訴模塊要使用哪個loader。若有多個loader的話,從右向左(從下到上)進行應用。
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
}
];
- loader
原本,webpack是隻能處理javascript文件的,但這樣的話,豈不是很不爽?! 所以,loader來了,它能讓webpack去處理非javascript文件
。
簡單理解,loader將所有類型的文件(如css、scss、png、jpg、…等類型)進行轉換,轉換爲webpack能處理的模塊。
如css-loader
轉換css文件 :
使用前先安裝 npm i -D css-loader
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
}
- plugins 插件
如果將loader理解爲轉換某些類型的模塊,那麼plugins能處理的任務可不止這些。如打包、壓縮,定義環境變量…插件能用來處理各種各樣的任務。
3️⃣. 接着寫小Case
加載CSS文件
- 安裝:使用style-loader(把js中引入的css內容注入到Html < style >標籤中,其依賴css-loader) 和css-loader(解析js中import 的css文件)可以解析css和style
npm i -D style-loader css-loader
- 添加loader
const path = require('path');
module.exports = {
entry: {
app:'./src/index.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] //從右向左應用到模塊
}
]
}
};
- 添加 /src/style.css
.c-red {color:red;}
- index.js修改
import _ from 'lodash';
import './style.css'
let createDomElement = ()=>{
let dom = document.createElement('div');
dom.innerHTML = _.join(['https://','blog','.csdn','.net','/imaginecode'],'');
dom.className = 'c-red';
return dom;
}
document.body.appendChild(createDomElement());
加載sass(scss)文件 、配置sourceMao
- 安裝:
npm i -D sass-loader node-sass webpack
- 添加loader
module: {
rules: [{
test: /\.scss$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader",
options: {
sourceMap: true //利於開發調式,定位文件
}
}, {
loader: "sass-loader",
options: {
sourceMap: true
}
}]
}]
}
添加PostCSS
postcss 可以利用爲CSS3屬性添加前綴的方式實現CSS的模塊化,防止樣式衝突。這也是常用的方式。 參考文檔:
https://postcss.org/
- 安裝:
npm i -D postcss-loader autoprefixer
- webpack.config.js 添加loader
...
const autoprefixer = require('autoprefixer');
...
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: loader => [
require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前綴
]
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
}
將樣式輸出爲文件
注意:抽取樣式後,就不會使用style-loader注入css到HTML了。
- 安裝:
npm i -D mini-css-extract-plugin
- 添加loader和plugins
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader, //抽取樣式
'css-loader',
'postcss-loader',
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[hash].css', // 設置最終輸出的文件名
chunkFilename: '[id].[hash].css'
})
]
此時,可以運行npm run build 或 npx webpack
看結果
壓縮css、Js
- 安裝:
npm i -D optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin
- 修改webpack.config.js :增加optimization
...
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
...
plugins:[
...
],
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true
}),
],
minimizer: [new OptimizeCSSAssetsPlugin({})]
}
將CSS文件和JS文件注入到HTML模板中
- 安裝:
npm i -D html-webpack-plugin
- 修改webpack.config.js
...
const HtmlWebpackPlugin = require('html-webpack-plugin');
...
plugins: [
...
new HtmlWebpackPlugin({
minify: { //壓縮html配置,在development模式下先不進行壓縮,到production下再進行
collapseWhitespace: false,
removeComments: false,
removeAttributeQuotes: false // 移除屬性的引號
}
})
],
optimization: {
...
}
清理dist目錄
- 安裝:
npm i -D clean-webpack-plugin
- webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin');
...
let pathsToClean = [
'dist/*.js',
'dist/*.css',
'dist/*jpg',
]
let cleanOptions = {
root: '',
exclude: ['index.html'],
verbose: true,
dry: false
}
plugins: [
new CleanWebpackPlugin(pathsToClean,cleanOptions), //構建前清除dist文件夾
...
],
加載與優化圖片和用base64編碼圖片
- 安裝
npm i -D file-loader image-webpack-loader url-loader
- webpack.config.js
module: {
rules: [
...
{
test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader', // 根據圖片大小,把圖片優化成base64
options: {
limit: 10000
}
},
{
loader: 'image-webpack-loader',// 先進行圖片優化
options: {
name: 'dist/[name].[ext]',
mozjepg:{
progressive: true,
quality: 65
},
optipng: {
enabled: false,
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75
}
}
}
]
}
]
}
查找源-輔助開發
- 一般用在開發環境
- 添加
inline-source-map
module.exports = {
...
devtool: 'inline-source-map',
...
}
監控、熱更新
-
監控變化,執行命令:
npx watch
-
熱更新
- 安裝:
npm i -D webpack-dev-server
- webpack.config.js
module.exports = { ... devServer: { contentBase: './dist', //文件變化後,自動打包更新到dist目錄 hot: true //啓用熱更新 } ... }
- 運行啓動webserver:
webpack-dev-server --open
- devServer其他配置:
參考文檔
- 安裝:
devServer: {
clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值)
hot: true, // 啓用 webpack 的模塊熱替換特性, 這個需要配合: webpack.HotModuleReplacementPlugin插件
contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪裏提供內容, 默認情況下,將使用當前工作目錄作爲提供內容的目錄
compress: true, // 一切服務都啓用gzip 壓縮
host: '0.0.0.0', // 指定使用一個 host。默認是 localhost。如果你希望服務器外部可訪問 0.0.0.0
port: 8080, // 端口
open: true, // 是否打開瀏覽器
overlay: { // 出現錯誤或者警告的時候,是否覆蓋頁面線上錯誤消息。
warnings: true,
errors: true
},
publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。
proxy: { // 設置代理
"/api": { // 訪問api開頭的請求,會跳轉到 下面的target配置
target: "http://192.168.0.102:8080",
pathRewrite: {"^/api" : "/mockjsdata/5/api"}
}
},
quiet: true, // necessary for FriendlyErrorsPlugin. 啓用 quiet 後,除了初始啓動信息之外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。
watchOptions: { // 監視文件相關的控制選項
poll: true, // webpack 使用文件系統(file system)獲取文件改動的通知。在某些情況下,不會正常工作。例如,當使用 Network File System (NFS) 時。Vagrant 也有很多問題。在這些情況下,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數,比如: poll: 1000
ignored: /node_modules/, // 忽略監控的文件夾,正則
aggregateTimeout: 300 // 默認值,當第一個文件更改,會在重新構建前增加延遲
}
}
babel轉碼
- 安裝:
npm i -D babel-loader babel-core babel-preset-env babel-plugin-transform-runtime babel-runtime
- webpack.config.js
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容
use: {
loader: 'babel-loader'
}
}
]
- 項目根目錄添加.babelrc文件
{
"presets": ["env"]
}
最後,你可以在index.js文件中寫一些ES6語法運行試試。
4️⃣. 常用loader小結
文件
raw-loader
加載文件原始內容(utf-8)val-loader
將代碼作爲模塊執行,並將 exports 轉爲 JS 代碼url-loader
像 file loader 一樣工作,但如果文件小於限制,可以返回 data URLfile-loader
將文件發送到輸出文件夾,並返回(相對)URL
JSON
轉換編譯
html-loader
導出 HTML 爲字符串,需要引用靜態資源pug-loader
加載 Pug 模板並返回一個函數jade-loader
加載 Jade 模板並返回一個函數markdown-loader
將 Markdown 轉譯爲 HTMLreact-markdown-loader
使用 markdown-parse parser(解析器) 將 Markdown 編譯爲 React 組件posthtml-loader
使用 PostHTML 加載並轉換 HTML 文件handlebars-loader
將 Handlebars 轉移爲 HTMLmarkup-inline-loader
將內聯的 SVG/MathML 文件轉換爲 HTML。在應用於圖標字體,或將 CSS 動畫應用於 SVG 時非常有用。
樣式
style-loader
將模塊的導出作爲樣式添加到 DOM 中css-loader
解析 CSS 文件後,使用 import 加載,並且返回 CSS 代碼less-loader
加載和轉譯 LESS 文件sass-loader
加載和轉譯 SASS/SCSS 文件postcss-loader
使用 PostCSS 加載和轉譯 CSS/SSS 文件stylus-loader
加載和轉譯 Stylus 文件
清理和測試
mocha-loader
使用 mocha 測試(瀏覽器/NodeJS)eslint-loader PreLoader
,使用 ESLint 清理代碼jshint-loader PreLoader
,使用 JSHint 清理代碼jscs-loader PreLoader
,使用 JSCS 檢查代碼樣式coverjs-loader PreLoader
,使用 CoverJS 確定測試覆蓋率
框架
vue-loader
加載和轉譯 Vue 組件polymer-loader
使用選擇預處理器(preprocessor)處理,並且require()
類似一等模塊(first-class)的 Web 組件angular2-template-loader
加載和轉譯 Angular 組件Awesome
更多第三方 loader,查看 awesome-webpack 列表。
5️⃣. 參考開發、生產環境配置
開發環境webpack.dev.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');
const webpack = require('webpack');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, './dist')
},
devtool: 'inline-source-map',
devServer: {
clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值)
hot: true, // 啓用 webpack 的模塊熱替換特性, 這個需要配合: webpack.HotModuleReplacementPlugin插件
contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪裏提供內容, 默認情況下,將使用當前工作目錄作爲提供內容的目錄
compress: true, // 一切服務都啓用gzip 壓縮
host: '0.0.0.0', // 指定使用一個 host。默認是 localhost。如果你希望服務器外部可訪問 0.0.0.0
port: 8085, // 端口
open: true, // 是否打開瀏覽器
overlay: { // 出現錯誤或者警告的時候,是否覆蓋頁面線上錯誤消息。
warnings: true,
errors: true
},
publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。
proxy: { // 設置代理
"/api": { // 訪問api開頭的請求,會跳轉到 下面的target配置
target: "http://192.168.0.102:8080",
pathRewrite: {
"^/api": "/mockjsdata/5/api"
}
}
},
quiet: true, // necessary for FriendlyErrorsPlugin. 啓用 quiet 後,除了初始啓動信息之外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。
watchOptions: { // 監視文件相關的控制選項
poll: true, // webpack 使用文件系統(file system)獲取文件改動的通知。在某些情況下,不會正常工作。例如,當使用 Network File System (NFS) 時。Vagrant 也有很多問題。在這些情況下,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數,比如: poll: 1000
ignored: /node_modules/, // 忽略監控的文件夾,正則
aggregateTimeout: 300 // 默認值,當第一個文件更改,會在重新構建前增加延遲
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容
use: [{
loader: 'babel-loader'
},{
loader: 'eslint-loader',
options: {
fix: true
}
}]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader', {
loader: 'css-loader',
options: {
sourceMap: true
}
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: true,
plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})]
}
}, {
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}
]
}, {
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}, {
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 75
}
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({filename: '[name].css', chunkFilename: '[id].css'}),
new CleanWebpackPlugin(['dist']),
new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依賴
new webpack.HotModuleReplacementPlugin(), // 替換插件
new HtmlWebpackPlugin({
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true, // 移除屬性的引號
},
template: path.resolve(__dirname, 'src/index.html')
})
],
optimization: {}
};
生產環境webpack.prod.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'main.[hash].js',
path: path.resolve(__dirname, './dist')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容
use: [{
loader: 'babel-loader'
},{
loader: 'eslint-loader',
options: {
fix: true
}
}]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader, {
loader: 'css-loader'
}, {
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})]
}
}, {
loader: 'sass-loader'
}
]
}, {
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}
]
}, {
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
'file-loader', {
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 75
}
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({filename: '[name][hash].css', chunkFilename: '[id][hash].css'}),
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src/index.html'),
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true, // 移除屬性的引號
}
})
],
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
}
};
你可以通過npx webpack --config webpack.dev.js
或npx webpack --config webpack.prodjs
在構建時執行不同環境下的配置文件。當然,你最好是在package.json文件中添加腳本來便捷執行:
"scripts":{
"dev":"npx webpack --config webpack.dev.js",
"prod":"npm webpack --config webpack.prod.js"
}
以後只需執行:npm run dev
或npm run prod