在三大框架潮流的推動下,大大小小的SPA
單頁面應用層出不窮,工程化 / 模塊化 / 自動化 漸漸成爲開發的核心思想,但是他們都有一個特點:
源代碼無法在瀏覽器裏直接運行,必需通過編譯纔行
因此也帶來了很多構建工具的興起;諸如具有代表性的 Gulp
、Grunt
、webpack
等等
今天,我們具體介紹 webpack 4.X
,webpack
也從V1過渡到V4,不久之後發佈V5版本( lz學不動了!!!)
文章目錄
- 一、初始化安裝
- 二、搭建項目
- 三、基礎配置
- `mode`
- `entry`
- `output`
- `module`
- ①:處理 Css | Less | Sass | Stylus
- ②:處理 JS | JSX
- ③:處理 jpe?g | png | gif | svg ...
- ④:處理 mp4 | webm | ogg | mp3 ...
- ⑤:處理 woff2 | eot | ttf | otf ...
- `plugins`
- ①:HtmlWebpackPlugin
- ②:CleanWebpackPlugin
- ③:MiniCssExtractPlugin
- ④:OptimizeCSSPlugin
- ⑤:PurifyCssWebpack
- ⑥:UglifyJsPlugin
- ⑦:GenerateAssetPlugin
- ⑧:SplitChunksPlugin
- ⑨:BundleAnalyzerPlugin
- ⑩:CompressionPlugin
- ⑪:CopyWebpackPlugin
- ⑫:Happypack | DllPlugin
- `devServer`
一、初始化安裝
建議node
版本在 5.0以上
npm init // 可選屬性創建 | npm init -y // 自動創建
npm i webpack -D
npm i webpack-cli -D
安裝完,執行 webpack -v
| webpack-cli -v
,驗證是否安裝成功
二、搭建項目
這裏我們基礎配置index.html
用於測試我們的打包後的效果,webpack.config.js
用來配置編譯需求,src
項目源碼,package.json
項目基本配置
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack 4.X</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/build.js"></script>
</body>
</html>
webpack.config.js
webpack
採用CommonJS
的規範,moudle.exports
導出
const path = require('path') // node提供的path工具,用來做路徑的拼接、轉換等
const fs = require('fs') // node提供的fs文件系統,用來操作文件、文件夾等
module.exports = {
mode: 'development ',
entry: '',
output: {},
module: {},
plugins: [],
devServer: {}
}
src
– index.js
入口文件
– home.js
測試多入口文件
– assets
靜態資源
:::-- img
圖片
:::-- font
字體文件
:::-- media
音視頻文件
:::-- css
樣式文件
package.json
這裏我們使用 npm run test 命令啓動 webpack 編譯
...
"scripts": {
"test": "webpack -p --progress --color --config webpack.config.js"
},
webpack
執行命令之後可以添加一些參數,下面是參數列表:
參數名 | 作用 |
---|---|
webpack --config XXX.js | 使用另一份配置文件來打包 |
webpack --watch | 監聽變動並自動打包 |
webpack -p | 壓縮混淆腳本,這個非常非常重要! |
webpack -d | 生成map映射文件,告知哪些模塊被最終打包到哪裏 |
webpack --progress | 顯示進度條 |
webpack --color | 添加顏色 |
三、基礎配置
webpack
從 編譯 -> 輸出 -> 運行 等等都是由很多配置內容完成
webpack
的核心配置:- 1.
mode
:模式----4.X新增,配置當前環境 - 2.
entry
:入口----要打包的文件 - 3.
output
:出口----配置編譯完成目錄 - 4.
module
:模塊----瀏覽器不識別的文件 - 5.
plugins
:插件----hook函數輔助開發,提高開發效率 - 6.
devServer
:服務器----webpack提供的本地服務器
mode
-
作用:代表當前的環境:
development
代表開發模式,production
(默認)代表生產模式 -
區別:mode
entry
-
作用:將要打包的入口文件
-
可選類型:String | Array | Object
// String
entry: './src/index.js',
// Array
entry: ['./src/index.js', './src/home.js'],
// Object
entry: {
'path/js': './src/index.js',
home: './src/home.js'
}
output
- 作用:向硬盤寫入編譯文件配置
- 屬性:↓↓↓↓↓↓↓
屬性名 | 作用 |
---|---|
filename | 向硬盤寫入編譯文件的名稱 |
path | 向硬盤寫入編譯文件的絕對路徑 |
publicPath | 指定資源文件引用的目錄 |
①:當entry
爲String
時,編譯入口文件並輸出
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist'),
}
}
②:當entry
爲Array
時,編譯入口文件合併輸出
const path = require('path')
module.exports = {
mode: 'development',
entry: ['./src/index.js', './src/home.js'],
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist'),
}
}
③:當entry
爲Object
時,編譯多個入口文件並輸出
entry
入口指定多個key
,value
時,output
的filename
要注意,不可寫死entry
入口的key
可以指定爲路徑(多頁面),編譯輸出也是路徑entry
入口的value
可以指定數組形式,編譯合併輸出
const path = require('path')
module.exports = {
mode: 'development',
entry: {
vendor: ['./src/index.js', './src/home.js'], // 合併打包爲輸出爲vender.js
'path/index': './src/index.js', // 路徑模式打包
home: './src/home.js' // 打包輸出爲home.js
},
output: {
filename: '[name].[hash:7].[ext]', // path原名/路徑指定hash輸出
path: path.resolve(__dirname, 'dist'),
}
}
module
- 介紹:
webpack
中任何一個東西都稱爲模塊(css/img/video/woff
…),而且只識別js
。 - 作用:藉助
loader
編譯js
爲供瀏覽器識別
①:處理 Css | Less | Sass | Stylus
install
npm i style-loader -D — 把處理完的 css 插入到 style 標籤裏
npm i css-loader -D — 處理 css
npm i postcss-loader -D — 處理不用的瀏覽器廠商前綴
npm i autoprefixer -D — 搭配 postcss-loader 個性化配置
瀏覽器 | 內核 | 前綴 |
---|---|---|
Chrome、Safari | webkit | -webkit- |
Firefox | gecko | -moz- |
Opera | presto | -o- |
IE | trident | -ms- |
… | … | … |
base
:
// index.css
section {
display: flex;
align-items: center;
transition:all 1s;
}
// index.js
import './asset/css/index.css'
document.getElementById('root').innerHTML = `<section>Webpack</section>`
webpack.config.js
:
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(css)$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
]
}
- 這裏要注意
loader
的使用順序:從後向前 - 1.首先把
.css
文件利用postcss-loader
過濾 - 2.接着利用
css-loader
處理css
爲瀏覽器所能識別的文件 - 3.最後利用
style-loader
插入到瀏覽器style
標籤中 - 4.如果是
less
、stylus
、sass
,借住各自的loader
追加到postcss-loader
即可
這裏編譯會報錯:No PostCSS Config found in: W:\webpack\src\asset\css
,缺少PostCSS config
配置文件,這裏我們搭配 autoprefixer
進行配置
根目錄新建 postcss.config.js
module.exports = {
"plugins": {
"autoprefixer": {}
}
}
package.json
追加 browserslist
...
"browserslist": [
"defaults",
"not ie < 11",
"last 2 versions",
"> 1%",
"iOS 7",
"last 3 iOS versions"
]
...
:::編譯完成
:::打開 index.html
,我們可以看到一切OK!
②:處理 JS | JSX
上面的代碼我們在 Chrome
無壓力,我們在毒瘤 IE11
中 運行試試效果
果不其然,報錯了,因爲我們在項目中使用到 ES6
提供的 模板字符串
,一些瀏覽器在ES6
發佈後沒有做出相應更新,出現不識別的情況
隨之出現babel-loader
:可以用來處理ES6語法,將其編譯爲瀏覽器可以執行的js
語法
接下來,我們安裝使用:
- 這裏要注意版本的一致性
babel-preset-es2015
落伍了,官方推薦使用babel-preset-env
install
這裏的 babel-loader 與 babel/core 版本要對應
npm i babel-loader @babel/core @babel/preset-env -D
webpack.config.js
...
rules: [
{
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
include: path.join(__dirname, 'src'), // 具體到 src 更快的搜索速度
exclude: '/node_modules/', // 排除node_modules,第三方代碼已經處理,不需要二次處理
presets: '@babel/preset-env' // 將ES6 解析 爲ES5
}
},
]
...
:::編譯完成
:::然後我們在毒瘤 IE
中 運行,IE11
、IE10
、IE9
都沒有問題
因爲在IE8
中,Object.defineProperty
沒有實現,並且不讓別人訪問或修改!!!
③:處理 jpe?g | png | gif | svg …
install
npm i url-loader file-loader -D
base
:
// index.css
section {
display: flex;
align-items: center;
transition: all 1s;
width: 100vw;height: 100vh;
background-image: url(../img/background.jpg) // 引入背景圖
}
// index.js
import './asset/css/index.css'
import icon from './asset/img/icon.png'
// 創建img對象
let img = new Image()
img.width = 200
img.height = 200
img.src = icon
// 創建section對象
let section = document.getElementById('section')
section.appendChild(img)
section.innerHTML += '<img src="./asset/img/bj.jpg">'
// 插入
document.getElementById('root').appendChild(section)
:::這裏配置如果文件大於 10K file-loader
原路徑輸出,否則利用url-loader
打包爲base64
,從而減少http請求,但是會變大!
`webpack.config.js
...
rules: [
{
test: /\.(jpe?g|png|gif|svg)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[hash:8].[ext]', // 以原圖片名輸出
outputPath: 'images/', // 輸出路徑
publicPath:'./dist/images', // 公共路徑 預防404
limit: 10240 // 超過10K打包爲圖片,反之打包爲base64
}
}
},
]
...
我們看到效果完全一致,icon
打包爲base64
,background.jpg
原路徑輸出
:::但是,有一個問題,我們看到頁面無法顯示圖片,這張圖片,我們是直接頁面 img
src
引入的
這裏因爲html
中直接使用img
標籤src
加載圖片的話,因爲沒有被依賴,圖片將不會被打包
官方文檔,提供loader
解決類似問題 html-withimg-loader
npm i html-withimg-loader -D
wepback.config.js
追加插件
{
test: /\.(htm|html)$/i,
loader: 'html-withimg-loader'
}
④:處理 mp4 | webm | ogg | mp3 …
webpack.config.js
...
rules: [
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[hash:8].[ext]', // 以原文件名輸出
outputPath: 'media/', // 輸出目錄
publicPath:'./dist/media', // 公共路徑 預防404
limit: 102400
}
}
},
]
...
⑤:處理 woff2 | eot | ttf | otf …
webpack.config.js
...
rules: [
{
test: /\.(woff2|eot|ttf|otf)$/,
use: {
loader: 'url-loader',
options: {
name: '[name].[hash:8].[ext]', // 以原文件名輸出
outputPath: 'font/',
publicPath:'./dist/font',
limit: 10240
}
}
}
]
...
plugins
- 介紹:用於擴展
webpack
的功能,Hook Funtion
- 作用:提高開發效率,自動化 / 工程化
①:HtmlWebpackPlugin
- 自動或依據模板生成一個
html
,動態hash
引入JS
CSS
等 - 可配置單頁面、多頁面入口
安裝:
npm i html-webpack-plugin -D
使用:
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入
...
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack 4.X', // 文件標題
filename: 'index.html', // 文件名
template: path.resolve(__dirname, 'index.html'), // 依賴模板
inject: true, // js放置位置: true -- body 底部 | head -- head標籤 | false -- 不加載js
hash: true, // 添加hash
minify: {
collapseWhitespace: true, // 移除空格
removeAttributeQuotes: true, // 移除引號
removeComments: true // 移除註釋
}
})
]
...
index.html
模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- EJS 語法引入title,配置 minify :removeComments 會移除註釋 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="root"></div>
</body>
</html>
:::構建成功,測試效果 :::
②:CleanWebpackPlugin
- 作用:清空上一次編譯結果目錄
安裝:
npm i clean-webpack-plugin -D
使用:
webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // webpack 4.0 解構賦值,源碼 export { CleanWebpackPlugin }
...
plugins: [
new CleanWebpackPlugin(['dist']) // 可配置絕對路徑
new CleanWebpackPlugin({
dry:true, // true -- 僅僅報告要刪除的文件並不刪除 fale -- 全部刪除
cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, 'test')] // 指定絕對路徑
})
]
...
③:MiniCssExtractPlugin
- 作用:抽取合併
css
,減小JS
文件體積,並自動添加hash
值
安裝:
npm i mini-css-extract-plugin -D
使用:
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
module: {
rules: [
{
test: /\.(css)$/,
use: [
MiniCssExtractPlugin.loader, // 當前是production環境,如果是development環境 style-loader
'css-loader',
'postcss-loader'
]
},
]
}
plugins: [
new MiniCssExtractPlugin({
filename: 'css/common.css', // 指定路徑,默認hash
})
]
...
:::測試效果 :::
④:OptimizeCSSPlugin
- 作用:用於合併,壓縮
css
代碼
安裝:
npm i optimize-css-assets-webpack-plugin -D
使用:
webpack.config.js
const OptimizeCSSPlugin= require('optimize-css-assets-webpack-plugin');
...
plugins: [
new OptimizeCSSPlugin()
]
...
:::測試效果 :::
⑤:PurifyCssWebpack
- 作用:用於去除無用
css
樣式
安裝:
npm i purifycss-webpack purify-css glob -D
使用:
webpack.config.js
const PurifyCssWebpack = require('purifycss-webpack') // 依賴 purify-css
const glob = require("glob") // 搜索資源
...
plugins: [
new PurifyCssWebpack({
//*.html 表示 src 文件夾下的所有 html 文件,還可以清除其它文件 *.js、*.php···
paths: glob.sync(path.join(__dirname, 'src/*.js'))
})
]
...
:::測試效果 :::
// index.css
section {
display: flex;
align-items: center;
transition: all 1s;
width: 100vw;height: 100vh;
background-image: url(../img/background.jpg);
background-color: #000;
}
.aaa {
font-size: 12px;
}
.bbb {
font-size: 12px;
}
⑥:UglifyJsPlugin
- 作用:壓縮
js
代碼,去除debugger
console
等等
安裝:
npm i uglifyjs-webpack-plugin -D
使用:
webpack.config.js
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
...
plugins: [
new UglifyJsPlugin({
cache: true, // 開啓緩存
parallel: true, // 多線程加速構建
sourceMap: true, // 使用sourceMap捕獲錯誤
uglifyOptions: {
compress: {
drop_console:true, // 放棄對 console 函數的調用
drop_debugger:true, // 刪除 debugger語句
}
},
})
]
...
:::測試效果 :::
⑦:GenerateAssetPlugin
- 作用:抽離生成額外配置文件
安裝:
npm i generate-asset-webpack-plugin -D
使用:
webpack.config.js
const GenerateAssetPlugin = require('generate-asset-webpack-plugin')
// 配置方法
const createJson = function () {
let serveConfigJson = {
baseURI: '172.16.0.94:80'
}
return JSON.stringify(serveConfigJson,null,4)
}
...
plugins: [
new GenerateAssetPlugin({
filename: 'serve.config.json', // 輸出到dist根目錄下的serve.config.json
fn: (compilation, cb) => {
cb(null, createJson(compilation))
},
extraFiles: []
})
]
...
:::測試效果 :::
⑧:SplitChunksPlugin
- 作用:打包分割代碼
配置:
optimization
屬性 splitChunks
默認屬性:
屬性 | 作用 |
---|---|
chunks | all, async, initial 三選一, 插件作用的chunks範圍 |
minSize | 最小尺寸 |
misChunks | 最小chunks |
maxAsyncRequests | 最大異步請求chunks |
maxInitialRequests | 最大初始化chunks |
name | split 的 chunks name |
cacheGroups | 緩存組,細分打包 |
… | … |
我們這裏就不寫默認屬性配置了,直接把node_modules
的依賴包打到verder.js
,引用2次以上的公共模塊打到common.js
webpack.config.js
...
optimization: {
splitChunks: {
chunks:'initial', // 對入口文件處理
cacheGroups: {
vendor:{
test: /node_modules\//,
name:'js/vendor',
priority: 10,
enforce: true
},
common: {
minChunks:2,
name: 'js/common',
priority: 10,
enforce: true
}
},
},
...
:::測試效果 :::
⑨:BundleAnalyzerPlugin
- 作用:打包體積分析,方便定位優化
安裝:
npm i webpack-bundle-analyzer -D
使用:
webpack.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
...
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'server', // 服務器啓動
analyzerHost: '127.0.0.1', // host
analyzerPort: '5555', // 端口
reportFilename: 'report.html', // 輸出文件
defaultSizes: 'parsed', // 報告顯示模塊大小
openAnalyzer: true, // 瀏覽器自動打開
generateStatsFile: false, // 不輸出json文件
statsFilename: 'stats.json', // 輸出json文件名
statsOptions: null, // 外配置
logLevel: 'info' // 插件輸出的詳細程度
}),
]
...
:::測試效果
⑩:CompressionPlugin
- 作用:開啓gizp壓縮,打包體積優化
- 注意:需要和服務器配合
- 版本:新版本可能不兼容
webpack 4.X
目前用的@1.1.12
安裝:
npm i [email protected] -D
使用:
webpack.config.js
const CompressionWebpackPlugin = require('compression-webpack-plugin')
...
plugins: [
new CompressionPlugin({
test: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i, // 匹配資源
filename: '[path].gz[query]', // 輸出名稱
algorithm: 'gzip', // 壓縮方式
threshold: 10240, // 處理大於10240字節纔會壓縮
minRatio: 0.8 // 壓縮率小於纔會被壓縮
}),
]
...
:::測試效果
⑪:CopyWebpackPlugin
- 作用:在
webpack
中拷貝文件或者文件夾
安裝:
npm i copy-webpack-plugin -D
使用:
webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin');
...
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, './src/static'), // 起始位置
to: path.resolve(__dirname, './dist/static') // 結束位置
}
]
})
]
...
:::測試效果 :::
⑫:Happypack | DllPlugin
Happypack
:多進程Loader
轉換,加速構建DllPlugin
:抽離第三方模塊,加速構建
總結:都是加速webpack
構建,這裏就不在演示了
devServer
- 介紹:模塊熱替換功能會在應用程序運行過程中替換、添加或刪除模塊,無需重新加載整個頁面。
- 保留在完全重新加載頁面時丟失的應用程序狀態
- 只更新變更內容,以節省寶貴的開發時間
- 調整樣式更加快速,幾乎相當於在瀏覽器調試器中更改樣式
安裝:
npm install webpack-dev-server -D
使用:
webpack.config.js
const webpack = require('webpack')
...
plugins: [
new webpack.NamedModulesPlugin(), // 顯示熱加載模塊名稱
new webpack.HotModuleReplacementPlugin() // 熱模塊替換
],
devServer: {
host: 'localhost', // host地址
port: '8080', // 端口
open: true, //自動拉起瀏覽器
hot: true, //熱加載
hotOnly: true, // 熱加載不更新
publicPath: '', // 基礎路徑
// proxy: {}, // 跨域
// bypass: {} // 攔截器
},
...
package.json
...
"scripts": {
"dev": "webpack-dev-server --progress --mode development",
"test": "webpack -p --progress --color --config webpack.config.js"
},
...
:::測試效果 :::
我們然後修改index.js
,發現熱加載不會生效,並且控制檯有警告信息
查閱文檔,我們缺少熱模塊更細,在需要的地方,更新即可
module.hot.accept(
總結:這些只是配置方面,研究事物不能侷限在表面…
待續…