學習了webpack
以後,將本身的博客的前端進行重構,因爲本身的博客是多頁應用,因此研究了下多頁應用的打包方案。在這裏把最後配置的成果分享下,不足之處,請指正。(文字很少,全是代碼,不是配置教程,因此沒有特別詳細的寫,只是一個參考)css
項目地址:github.com/Ray-daydayu…html
首先先看下個人項目的目錄結構前端
myblog-webpack
├── dist // 打包輸出文件夾
├── src // 源代碼
│ ├── api // 請求文件夾,存放封裝的請求方法
│ ├── assets // 靜態資源文件夾
│ ├── lib // 一些庫
│ ├── pages // 頁面
│ │ ├── about // 頁面名稱
│ │ │ ├── index.html // html模板
│ │ │ └── index.js // 入口js
│ │ ├── category
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── detail
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── index
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── tag
│ │ ├── index.html
│ │ └── index.js
│ ├── styles // 樣式文件
│ └── utils // 工具函數
├── package.json
├── README.md
├── package-lock.json
├── webpack.base.js // 公共配置
├── webpack.dev.js // 開發環境配置
└── webpack.prod.js // 生產環境配置
複製代碼
最終打包輸出的目錄以下html5
dist
├── assets
├── css
│ ├── commons672a1e57.css
│ └── index6085d612.css
├── js
│ ├── aboutfc723f0e.js
│ ├── categorye4be3bd6.js
│ ├── commons78f1dd3f.js
│ ├── detail0df434c5.js
│ ├── indexe1e985d9.js
│ ├── markdown-it
│ │ ├── highlight.vendors.js
│ │ ├── markdown-it-integrated.js
│ │ ├── markdown-it-integrated.min.js
│ │ ├── markdown-it-integrated.min.js.LICENSE.txt
│ │ └── markdown-it.vendors.js
│ └── tagf1c1035c.js
├── about.html
├── category.html
├── detail.html
├── favicon.ico
├── index.html
└── tag.html
複製代碼
webpack
配置文件注意:文中各部分所需的包名,並無書寫,請在附錄中查找!!!相關配置的意義能夠查找官方文檔node
entry
和設置html-webpack-plugin
多頁應用的打包思路是每一個頁面對應一個entry
,一個html-webpack-plugin
,但每次新增或刪除頁面須要改webpack
配置,因此須要根據文件目錄動態獲取入口,來設置html-webpack-plugin
。webpack
利用一個庫glob
來實現對文件目錄的讀取 具體代碼以下,相關配置的意義能夠查找官方文檔ios
const path = require('path')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const setMPA = () => {
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js')) // 匹配各頁面的對應入口文件
entryFiles.forEach((item) => {
const pageName = item.match(/pages\/(.*)\/index.js/)[1] // 獲取文件夾名,即頁面名稱
entry[pageName] = item // 設置入口文件路徑
// 設置html-webpack-plugin數組
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `./src/pages/${pageName}/index.html`),//模板地址
filename: `${pageName}.html`, //輸出文件名
chunks: [pageName], // 插入的js chunk名稱,和output有關
inject: true,
favicon: path.join(__dirname, './src/assets/favicon.ico'), // 圖標
minify: { //壓縮代碼
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
)
})
return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins } = setMPA()
複製代碼
ES6
和 async、await
,以及eslint
使用 ES6
和 async、await
以及eslint
,須要藉助babel-loader
和 eslint-loader
,其具體配置很簡單git
babel
相關的庫:@babel/core
、@babel/preset-env
、@babel/plugin-transform-regenerator
、@babel/plugin-transform-runtime
、 eslint
相關的庫:babel-eslint
、eslint
github
module.rules
的配置{
test: /\.js$/,
use: ['babel-loader', 'eslint-loader']
}
複製代碼
.babelrc
文件的配置{
"presets": [
"@babel/preset-env"
],
"plugins": [ //設置支持async和await
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-regenerator"
]
}
複製代碼
.eslintrc.js
的配置module.exports = {
parser: 'babel-eslint',
extends: ['alloy'], // eslint標準請自行選擇下載
env: {
browser: true,
node: true
},
rules: {
'no-new': 'off',
'no-proto': 'off'
// 'no-console': 'error'
}
}
複製代碼
利用url-loader
,具體module.rules
配置以下web
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
esModule: false,
name: '[name][hash:8].[ext]', //文件指紋
limit: 10240, // 轉base64
outputPath: './assets/images/' // 圖片文件輸出目錄和publicPath有關
}
}
]
}
複製代碼
自動清理打包目錄利用clean-webpack-plugin
插件,複製文件利用copy-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const basePlugins = [
...htmlWebpackPlugins, //以前配置的htmlWebpackPlugin數組
new CleanWebpackPlugin(), // 自動清理構建目錄
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, 'src/lib/markdown-it'), // 源
to: path.join(__dirname, 'dist/js/markdown-it') // 目標位置
}
]
})
]
複製代碼
.browserslistrc
配置# Browsers that we support
last 2 version
> 1%
iOS 7
複製代碼
devServer
具體配置以下:
plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()], //熱更新插件
devServer: {
contentBase: 'dist', // 開啓服務的目錄
hot: true, //熱更新開啓
proxy: {
'/api': {
target: 'http://raydaydayup.cn:3000', //代理
pathRewrite: { '^/api': '' }
}
}
}
複製代碼
CSS
相關配置module.rules
的配置以下
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
複製代碼
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist')
}
複製代碼
output: {
filename: 'js/[name][chunkhash:8].js', // 統一輸出到js文件夾
path: path.join(__dirname, 'dist'),
publicPath: '/' // 服務路徑
}
複製代碼
js
代碼壓縮利用terser-webpack-plugin
插件,配置optimization
const TerserPlugin = require('terser-webpack-plugin')
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
include: [/\.js$/]
})
]
}
複製代碼
CSS
相關配置CSS
代碼壓縮,利用optimize-css-assets-webpack-plugin
和cssnano
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano')
})
]
複製代碼
CSS
分離單獨文件,利用mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module: {
rules: [
...baseModuleRules,
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
'css-loader',
'less-loader'
]
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
'css-loader'
]
}
]
},
plugins: [
...basePlugins,
new MiniCssExtractPlugin({
filename: 'css/[name][contenthash:8].css' // 輸出文件名和地址
}),
],
複製代碼
PostCSS
插件 autoprefixer
自動補齊CSS3
前綴module: {
rules: [
...baseModuleRules,
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: () => [require('autoprefixer')]
}
},
'less-loader'
]
},
]
},
複製代碼
配置optimization.splitChunks
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2 // 最少引用兩次
}
}
}
}
複製代碼
html-webpack-plugin
模板使用以及html-loader
html
片斷,在相應位置寫下面的代碼就像,須要注意的是,html-loader
加載的html
片斷內部<%%>
語法會失效<%= require("html-loader!@/components/recent.html") %>
複製代碼
html
中圖片的加載。在 html
中相關的img
標籤的圖片,html-loader
加載時會自動調用url-loader
,可是存在問題,加載出來的路徑沒有""
。爲了不這個問題,我就沒有再html-loader
加載的html
片斷中使用圖片,而是直接再html
模板中直接用require
<img src="<%= require('@/assets/logo362x82.png') %>" alt="" />
複製代碼
webpack.base.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const path = require('path')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const setMPA = () => {
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js'))
entryFiles.forEach((item) => {
const pageName = item.match(/pages\/(.*)\/index.js/)[1]
entry[pageName] = item
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `./src/pages/${pageName}/index.html`),
filename: `${pageName}.html`,
chunks: [pageName],
inject: true,
favicon: path.join(__dirname, './src/assets/favicon.ico'),
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
)
})
return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins } = setMPA()
const baseModuleRules = [
{
test: /\.js$/,
use: ['babel-loader', 'eslint-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
esModule: false,
name: '[name][hash:8].[ext]',
limit: 10240,
outputPath: './assets/images/'
}
}
]
}
]
const basePlugins = [
...htmlWebpackPlugins,
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, 'src/lib/markdown-it'),
to: path.join(__dirname, 'dist/js/markdown-it')
}
]
})
]
module.exports = {
entry,
baseModuleRules,
basePlugins
}
複製代碼
webpack.dev.js
const webpack = require('webpack')
const path = require('path')
const { entry, baseModuleRules, basePlugins } = require('./webpack.base.js')
module.exports = {
mode: 'development',
entry,
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist')
},
module: {
rules: [
...baseModuleRules,
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
resolve: {
// 設置別名
alias: {
'@': path.join(__dirname, 'src') // 這樣配置後 @ 能夠指向 src 目錄
}
},
plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()],
devServer: {
contentBase: 'dist',
hot: true,
proxy: {
'/api': {
target: 'http://raydaydayup.cn:3000',
pathRewrite: { '^/api': '' }
}
}
}
}
複製代碼
webpack.prod.js
const { entry, baseModuleRules, basePlugins } = require('./webpack.base.js')
const path = require('path')
const TerserPlugin = require('terser-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
mode: 'production',
entry,
output: {
filename: 'js/[name][chunkhash:8].js',
path: path.join(__dirname, 'dist'),
publicPath: '/'
},
resolve: {
// 設置別名
alias: {
'@': path.resolve('src') // 這樣配置後 @ 能夠指向 src 目錄
}
},
module: {
rules: [
...baseModuleRules,
{
test: /\.less$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: () => [require('autoprefixer')]
}
},
'less-loader'
]
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: true
}
},
'css-loader'
]
}
]
},
plugins: [
...basePlugins,
new MiniCssExtractPlugin({
filename: 'css/[name][contenthash:8].css'
}),
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano')
})
],
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
include: [/\.js$/]
})
],
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2
}
}
}
}
}
複製代碼
{
"name": "myblog-webpack",
"version": "1.0.0",
"description": "",
"main": ".eslintrc.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.prod.js",
"dev": "webpack-dev-server --config webpack.dev.js --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.10.2",
"@babel/plugin-transform-regenerator": "^7.10.3",
"@babel/plugin-transform-runtime": "^7.10.3",
"@babel/preset-env": "^7.10.2",
"autoprefixer": "^9.8.4",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.0.3",
"css-loader": "^3.6.0",
"cssnano": "^4.1.10",
"eslint": "^7.2.0",
"eslint-config-alloy": "^3.7.2",
"eslint-loader": "^4.0.2",
"file-loader": "^6.0.0",
"glob": "^7.1.6",
"html-loader": "^1.1.0",
"html-webpack-plugin": "^4.3.0",
"less-loader": "^6.1.2",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"style-loader": "^1.2.1",
"terser-webpack-plugin": "^3.0.5",
"url-loader": "^4.1.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.11.0"
},
"dependencies": {
"axios": "^0.19.2"
}
}
複製代碼