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

webpackThe 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 accessentryAnd 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

useES6Async 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.rulesThe configuration of the
{
    test: /\.js$/.use: ['babel-loader'.'eslint-loader']}Copy the code
  • .babelrcFile configuration
{
  "presets": [
    "@babel/preset-env"]."plugins": [// set async and await support"@babel/plugin-transform-runtime"."@babel/plugin-transform-regenerator"]}Copy the code
  • .eslintrc.jsThe 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

.browserslistrcconfiguration

# 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

CSSThe 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

jsCode 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

CSSThe related configuration

  • CSSCode compression, useoptimize-css-assets-webpack-pluginandcssnano
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')

plugins: [
  new OptimizeCSSAssetsPlugin({
      assetNameRegExp: /\.css$/g.cssProcessor: require('cssnano')})]Copy the code
  • CSSSeparate 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
  • PostCSSThe plug-inautoprefixerauto-completeCSS3The 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 importhtmlFragment, and write the following code in place like, and notice,html-loaderThe loadedhtmlFragments insideThe < % % >Syntax is broken
< % =require("html-loader! @/components/recent.html") % >Copy the code
  • htmlLoading of images in. inhtmlIn the relevantimgThe image of the tag,html-loaderAutomatically called when loadedurl-loader, but there is a problem, the loaded path does not"". To avoid this problem, I stoppedhtml-loaderThe loadedhtmlUse the image in the fragment, but directly againhtmlDirectly 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