1. I recently had a project to make a multi-page app for SEO purposes. In order to ensure the speed and efficiency of development, we decided to use Webpack to make a modular configuration scheme.
  2. The following are mainly for some important points to provide ideas, not in detail. Complete code, I will put on Github (project address) for your reference, if there is optimization, please point out in the comment area.

directory

| | - build webpack configuration | -- utils. Js processing webpack configuration of public methods | | -- webpack. Base. Conf., js common configuration | | -- webpack. Dev. Conf., js development environment configuration . | | - webapck prod. Conf. Js production environment configuration | | -- webpack. Rules. Conf. | js file handling rules - dist where mutated files | -- - | | - SRC the source file | | - assets | | - pages | | | - the index page | | | | -- index. HTML page template | | | | -- index. | js page entry documents htmlarrary. Js page configuration fileCopy the code

Multiple page

Multi-page, the first and most important is to handle multiple HTML templates and corresponding multiple entry files.

HTML template

Create a htmlarry.js file in the project root directory to store the page configuration:

// htmlarrary.js

module.exports = [
  {
    _html: 'index'.title: 'home'.chunks: ['index'.'manifest'.'vendors'] // The vendor module used by the page
  },
  {
    _html: 'login'.title: 'login'.chunks: ['login']}]Copy the code

Then create the getHtmlArray method at /build/utils.js to automatically generate multiple template configurations:

// /build/utils.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlArray = require('.. /htmlarray.js')

exports.getHtmlArray = function (moduleExportsPlugins) {

  // Generate the configuration required by HtmlWebpackPlugin according to the template configuration
  const getHtmlConfig = function (name, chunks, title) {
    return {
      template: `./src/pages/${name}/index.html`.filename: `. /${name}.html`.favicon: './src/assets/images/public/favicon.ico',
      title,
      inject: true.hash: true./ / open the hash
      chunks, // The package to import from the page
      minify: process.env.NODE_ENV === 'development' ? false : {
        removeComments: true.// Remove comments from HTML
        collapseWhitespace: true.// Folding white space is the same as compressing code
        removeAttributeQuotes: true.// Remove attribute references}}; };// Loop to create template configurations
  htmlArray.forEach((element) = > {
    const { _html, chunks, title } = element
    moduleExportsPlugins.push(new HtmlWebpackPlugin(getHtmlConfig(_html, chunks, title)))
  })
}
Copy the code

Add multi-page engine configuration via getHtmlArray in webpack.base.conf.js:

const { getHtmlArray } = require('./utils.js')

module.exports = {
  / /... The related configuration
}

getHtmlArray(module.exports.plugins)
Copy the code

Entrance to the file

Create the getEntry method at /build/utils.js to automatically generate the configuration of the entry file:

// /build/utils.js
const glob = require('glob')

exports.getEntry = function () {
  const entry = {}
  // Read all page entries in the SRC directory
  glob.sync('./src/pages/*/*.js').forEach((name) = > {
    const start = name.indexOf('src/') + 4;
    const end = name.length - 3;
    const eArr = [];
    const n = name.slice(start, end).split('/') [1];
    eArr.push(name);
    eArr.push('@babel/polyfill'); // This is introduced to use async await, some properties not supported by IE can be supported, compatible with IE
    entry[n] = eArr;
  })
  return entry;
}
Copy the code

Add multi-entry configuration via getEntry in webpack.base.conf.js:

// webpack.base.conf.js

const { getEntry } = require('./utils.js')

module.exports = {
  entry: getEntry(),
}
Copy the code

JS

JS, we generally have the following requirements:

  1. Eslint error alerts;
  2. Ts-loader parses typescript syntax;
  3. Babel-loader parses ES6 syntax.

For the above requirements, let’s configure the rules and make an extension:

// webpack.rules.conf.js

module.exports = [
  {
    test: /\.(js|ts)$/.exclude: /node_modules/.use: [{loader: 'babel-loader'.options: {
          presets: [['@babel/preset-env', {
              useBuiltIns: 'usage'.targets: {
                chrome: '58'.ie: '8'
              },
              corejs: 2}]]}}, {loader: 'ts-loader'
      },
      {
        loader: 'eslint-loader'.options: {
          cache: true // Optimize packaging speed}}]}]Copy the code

In production, we need to compress the JS files and remove the common code, so we need to optimize the webpack.prod.conf.js as follows:

// webpack.prod.conf.js

cconst merge = require('webpack-merge')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const baseConfig = require('./webpack.base.conf.js')

const prodConfig = {
  optimization: {
    minimizer: [
      // This will cause sourcemap to disappear
      new UglifyJsPlugin({
        uglifyOptions: ({
          compress: false})}),new OptimizeCSSAssetsPlugin({})
    ],
    splitChunks: {
      chunks: 'all'.cacheGroups: {
        vendors: { // Remove third-party plugins
          test: /[\\/]node_modules[\\/]/.// specify a third-party package under node_modules
          name: 'vendors'.priority: - 10 // Extract the priority
        },
        utilCommon: { // Remove custom
          name: 'common'.minSize: 0.// The minimum size to separate reference modules into new code files
          minChunks: 2.// Indicates how many times to reference modules such as different files before separating and generating new chunks
          priority: - 20}}},/ / optimization. RuntimeChunk is tell webpack if we need to separate packaging, this part to optimize the cache problem
    runtimeChunk: {
      name: 'manifest'}}}module.exports = merge(baseConfig, prodConfig)
Copy the code

CSS

For CSS, we generally have the following requirements:

  1. Postcss-loader Installs the autoprefixer plug-in for automatic compatibility processing.
  2. Sass-loader parses sASS syntax;
  3. MiniCssExtractPlugin Uses CSS compression.

For the above requirements, let’s configure the rules and make an extension:

// webpack.rules.conf.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = [
  {
    test: /\.scss$/i.use: [
      Object.assign(
        / / production environment compress CSS needs to use MiniCssExtractPlugin. Replace style - loader loader
        { loader: process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader' },
        // Solve the problem that CSS images cannot be displayed properly after compiling
        process.env.NODE_ENV === 'production' ? { options: { publicPath: '.. / '}} : {}),'css-loader'.'sass-loader'.'postcss-loader']}]Copy the code

In production, we need to compress the CSS file, so we need to optimize it in webpack.prod.conf.js like this:

// webpack.prod.conf.js

cconst merge = require('webpack-merge')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const baseConfig = require('./webpack.base.conf.js')

const prodConfig = {
  optimization: {
    minimizer: [
      new OptimizeCSSAssetsPlugin({})
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css'.chunkFileName: '[id].[contenthash:8].css']}}),module.exports = merge(baseConfig, prodConfig)
Copy the code

images

In terms of images, we generally have the following requirements:

  1. Images in CSS and JS can be parsed;
  2. Images of the IMG tag in HTML can be parsed.

For the above requirements, let’s configure the rules and make an extension:

// webpack.rules.conf.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = [
  {
    test: /\.html$/.use: [
      // If the SRC of the img tag is empty, an error is reported.
      {
        loader: 'html-loader',}]}, {test: /\.(png|jpg|gif|ico)$/.use: [{loader: 'url-loader'.options: {
          name: '[name].[hash:8].[ext]'.limit: 30000.outputPath: './images'}}]}]Copy the code

other

Devserver and hot updates

// webpack.dev.conf.js

const devConfig = {
  devServer: {
    open: true.host: '0.0.0.0'.port: 2000.useLocalIp: true.hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
}
Copy the code

This intelligently starts CSS hot update. If you need JS hot update, you need to add a code, please find the official website documentation.

An error

  1. If the SRC of the img tag is empty, an error is reported.
  2. If an error is reported:TS2688: Cannot find type definition file for 'unist'.Dependencies need to be installed@types/unist, other similar error, this is[email protected]An error is reported due to changing the support mode of types.
  3. After compiling CSS image path error, according to whether the production environment to dynamically addpublicPath.Click here to.