preface
After learning Webpack, I reconstructed the front end of my blog. Since my blog is a multi-page application, I studied the packaging scheme of multi-page application. Here is the final configuration of the results to share, shortcomings, please correct. (text is not much, all code, not configuration tutorial, so there is no special detailed write, just a reference)
Project address: github.com/Ray-daydayu…
File directory structure
Project directory structure
First look at the directory structure of my project
├─ SRC // Source code │ ├─ API │ ├─ SRC // Source code │ ├─ API Store packaging request method │ ├ ─ ─ assets / / static resource folders │ ├ ─ ─ lib/library/some │ ├ ─ ─ pages / / page │ │ ├ ─ ─ the about / / the name of the page │ │ │ ├ ─ ─ index. The HTML / / HTML template │ │ │ └ ─ ─ index. The js/js/entrance │ │ ├ ─ ─ the category │ │ │ ├ ─ ─ index. The HTML │ │ │ └ ─ ─ index. The js │ │ ├ ─ ─ the detail │ │ │ ├ ─ ─ Index.html │ │ │ └ ─ ─ index. The js │ │ ├ ─ ─ index │ │ │ ├ ─ ─ index. The HTML │ │ │ └ ─ ─ index. The js │ │ └ ─ ─ the tag │ │ ├ ─ ─ index. The HTML │ │ └ ─ ─ index. Js │ ├ ─ ─ styles / / style file │ └ ─ ─ utils / / tools function ├ ─ ─ package. The json ├ ─ ─ the README. Md ├ ─ ─ package - lock. Json ├ ─ ─ Webpack. Base. Js / / common configuration ├ ─ ─ webpack. Dev. Js / / development environment configuration └ ─ ─ webpack. Prod. Js / / production environment configurationCopy the code
Package the output file directory structure
The directory for the final packaged output is as follows
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 Markdown - it - integrated. Min. Js. LICENSE. TXT │ │ └ ─ ─ markdown - it. Vendors. Js │ └ ─ ─ tagf1c1035c. Js ├ ─ ─ the about the HTML ├ ─ ─ ├── class.html ├── detail.html ├── favicon.ico ├─ index.html ├─ tag.htmlCopy the code
webpack
The configuration file
Note: the package name required for each part of the article is not written, please find it in the appendix!! The meaning of the configuration can be found in the official documentation
Common configuration
Dynamic accessentry
And set uphtml-webpack-plugin
The packing idea of multi-page application is that each page has an entry and an HTml-webpack-plugin. However, the WebPack configuration needs to be changed every time a new or deleted page is added or deleted. Therefore, the htML-webpack-plugin needs to be dynamically obtained according to the file directory.
Use a library glob to achieve the file directory to read the specific code as follows, related configuration can be found in the official documentation
const path = require('path')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const setMPA = (a)= > {
const entry = {}
const htmlWebpackPlugins = []
const entryFiles = glob.sync(path.join(__dirname, './src/pages/*/index.js')) // Match the corresponding entry file for each page
entryFiles.forEach((item) = > {
const pageName = item.match(/pages\/(.*)\/index.js/) [1] // Get the folder name, which is the page name
entry[pageName] = item // Set the entry file path
// Set the html-webpack-plugin array
htmlWebpackPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `./src/pages/${pageName}/index.html`),// Template address
filename: `${pageName}.html`.// Output the file name
chunks: [pageName], // Insert js chunk name, related to output
inject: true.favicon: path.join(__dirname, './src/assets/favicon.ico'), / / icon
minify: { // Compress the code
html5: true.collapseWhitespace: true.preserveLineBreaks: false.minifyCSS: true.minifyJS: true.removeComments: false}}})))return {
entry,
htmlWebpackPlugins
}
}
const { entry, htmlWebpackPlugins } = setMPA()
Copy the code
useES6
和 Async and await
, as well aseslint
To use ES6 and Async, await, and ESLint, you need to use babel-Loader and ESLint-Loader, which are very simple to configure
Babel /core, @babel/preset-env, @babel/plugin-transform-regenerator, @babel/ plugin-transform-Runtime, esLint Babel – eslint, eslint
module.rules
The configuration of the
{
test: /\.js$/.use: ['babel-loader'.'eslint-loader']}Copy the code
.babelrc
File configuration
{
"presets": [
"@babel/preset-env"]."plugins": [// set async and await support"@babel/plugin-transform-runtime"."@babel/plugin-transform-regenerator"]}Copy the code
.eslintrc.js
The configuration of the
module.exports = {
parser: 'babel-eslint'.extends: ['alloy'].// Download the ESLint standard of your choice
env: {
browser: true.node: true
},
rules: {
'no-new': 'off'.'no-proto': 'off'
// 'no-console': 'error'}}Copy the code
Loading pictures
Using urL-loader, configure module.rules as follows
{
test: /\.(png|svg|jpg|gif)$/.use: [{loader: 'url-loader'.options: {
esModule: false.name: '[name][hash:8].[ext]'.// File fingerprint
limit: 10240.// 转base64
outputPath: './assets/images/' // The image file output directory is related to publicPath}}}]Copy the code
Clean up the package directory and copy files to the package directory
The clean-webpack-plugin plugin is used to automatically clean the package directory and the copy-webpack-plugin is used to copy files
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const basePlugins = [
...htmlWebpackPlugins, // The htmlWebpackPlugin array configured previously
new CleanWebpackPlugin(), // Automatically clean up the build directory
new CopyWebpackPlugin({
patterns: [{from: path.join(__dirname, 'src/lib/markdown-it'), / / source
to: path.join(__dirname, 'dist/js/markdown-it') // Target position}}]]Copy the code
.browserslistrc
configuration
# Browsers that we support
last 2 version
> 1%
iOS 7
Copy the code
Development Environment Configuration
Hot updates anddevServer
The configuration is as follows:
plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()], // Hot update plugin
devServer: {
contentBase: 'dist'.// Open the service directory
hot: true.// Hot update is enabled
proxy: {
'/api': {
target: 'http://raydaydayup.cn:3000'./ / agent
pathRewrite: { '^/api': ' '}}}}Copy the code
CSS
The related configuration
The configuration of module.rules is as follows
{
test: /\.less$/.use: ['style-loader'.'css-loader'.'less-loader'] {},test: /\.css$/.use: ['style-loader'.'css-loader']}Copy the code
exit
output: {
filename: '[name].js'.path: path.join(__dirname, 'dist')}Copy the code
Production Environment Configuration
exit
output: {
filename: 'js/[name][chunkhash:8].js'.// All output to js folder
path: path.join(__dirname, 'dist'),
publicPath: '/' // Service path
}
Copy the code
js
Code compression
Configuration Optimization using the Terser-Webpack-plugin
const TerserPlugin = require('terser-webpack-plugin')
optimization: {
minimize: true.minimizer: [
new TerserPlugin({
include: [/\.js$/]]}})Copy the code
CSS
The related configuration
CSS
Code compression, useoptimize-css-assets-webpack-plugin
andcssnano
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g.cssProcessor: require('cssnano')})]Copy the code
CSS
Separate individual files and utilizemini-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' // Output the file name and address}),].Copy the code
PostCSS
The plug-inautoprefixer
auto-completeCSS3
The prefix
module: {
rules: [
...baseModuleRules,
{
test: /\.less$/.use: [{loader: MiniCssExtractPlugin.loader,
options: {
esModule: true}},'css-loader',
{
loader: 'postcss-loader'.options: {
plugins: (a)= > [require('autoprefixer')]}},'less-loader']},]},Copy the code
Separate common files (CSS and JS)
The configuration optimization. SplitChunks
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons'.chunks: 'all'.minChunks: 2 // At least two references}}}}Copy the code
Usage related notes
Html-webpack-plugin template use and htMl-loader
- The import
html
Fragment, and write the following code in place like, and notice,html-loader
The loadedhtml
Fragments insideThe < % % >
Syntax is broken
< % =require("html-loader! @/components/recent.html") % >Copy the code
html
Loading of images in. inhtml
In the relevantimg
The image of the tag,html-loader
Automatically called when loadedurl-loader
, but there is a problem, the loaded path does not""
. To avoid this problem, I stoppedhtml-loader
The loadedhtml
Use the image in the fragment, but directly againhtml
Directly in the templaterequire
<img src="<%= require('@/assets/logo362x82.png') %>" alt="" />
Copy the code
The appendix
Configuration file complete version
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 = (a)= > {
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
}
Copy the code
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: {
// Set the alias
alias: {
The '@': path.join(__dirname, 'src') // @ points to the SRC directory}},plugins: [...basePlugins, new webpack.HotModuleReplacementPlugin()],
devServer: {
contentBase: 'dist'.hot: true.proxy: {
'/api': {
target: 'http://raydaydayup.cn:3000'.pathRewrite: { '^/api': ' ' }
}
}
}
}
Copy the code
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: {
// Set the alias
alias: {
The '@': path.resolve('src') // @ points to the SRC directory}},module: {
rules: [
...baseModuleRules,
{
test: /\.less$/.use: [{loader: MiniCssExtractPlugin.loader,
options: {
esModule: true}},'css-loader',
{
loader: 'postcss-loader'.options: {
plugins: (a)= > [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
}
}
}
}
}
Copy the code
Package. The json file
{
"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"}}Copy the code