---
title: webpack 多页面配置           
date: 2022/4/21 8:50:20
tags:                    
- webpack
- 多页面
- 构建工具          
author: 蔡海飞          
---


基于业务本身考虑，针对企业官网性质、面向C端用户的PC网站。我们需要有良好的搜索体验，需要有良好的页面访问体验。对此，探讨webpack配置多页面开发工程的可能性。本篇文章，默认大家对于webpack已经具有一定的了解，可以完成一些基础的webpack配置操作。如有不清楚的，请[点击查看](https://www.webpackjs.com/)。

<!-- more -->

整体配置，我将其拆分为**5**个部分的配置。分别是:

- webpack.config.js  

所有配置信息的整合合并配置，和基础的webpack配置。

- config/plugins.js  

webpack的所有插件配置信息

- config/proxy.js  

将webpack中的代理信息单独拿出来进行配置管理

- config/rules.js  

webpack的加载器配置模块独立配置管理


- router.js  

这是新增自定义的路由配置，由于自定义路由地址和指定配置路由文件。



### config/rules.js


> 本部分配置，主要是为了给工程提供更多的适配，如ts、less、scss、postcss等功能。

```
/* eslint-disable no-undef */
const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );

const rules = [
    {
        test: /\.js$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [path.resolve( __dirname, 'src' )], // 指定检查的目录
        options: { // 这里的配置项参数将会被传递到 eslint 的 CLIEngine 
            formatter: require( 'eslint-friendly-formatter' ), // 指定错误报告的格式规范
        },
    },
    {
        test: /\.[tj]s?$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
    },
    {
        test: /\.tsx?$/, exclude: /node_modules/, use: [
            {
                loader: 'ts-loader',
                options: {
                    transpileOnly: true,
                },
            },
        ],
    },
    { test: /\.ts$/, exclude: /node_modules/, use: ['ts-loader'] },
    {
        test: /\.ejs$/, use: [
            {
                loader: 'ejs-loader',
                options: {
                    esModule: false,
                    variable: 'data',
                },
            },
        ],
    },
    {
        test: /\.(sa|sc|le|c)ss$/,
        use: [
            {
                // 把js中import导入的样式文件，单独打包成一个css文件，结合html-webpack-plugin，以link的形式插入到html文件中。
                loader: MiniCssExtractPlugin.loader,
                // options: {
                //     publicPath: '../',//设置publicPath,解决css文件中background背景图片路径问题
                // },
            },
            // 把js中import导入的样式文件打包到js文件中，运行js文件时，将样式自动插入到<style标签中,style-loader不能和mini-css-extract-plugin同时使用
            // 'style-loader',    
            'css-loader',
            'postcss-loader',
            'less-loader',
            'sass-loader',
        ],
    },
    { test: /\.eot$|\.svg$|\.ttf$|\.woff$/, use: ['url-loader'], type: 'asset/resource', },
    {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
        generator: {
            filename: 'images/[name]_[hash]_[ext]'
        },
    },
    {
        test: /\.(csv|tsv)$/i,
        use: ['csv-loader'],
    },
    {
        test: /\.xml$/i,
        use: ['xml-loader'],
    },


]

module.exports = rules;


```


### config/plugins.js


这部分是用于完成webpack的插件配置工作的，我们这里提供了css合并分立插件、构建清空已有文件，最重要的是实现ejs模块化转html的工作。

```
/* eslint-disable no-undef */
const path = require('path'); //  Node.js 处理文件路径的模块
const webpack = require('webpack');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const glob = require('glob');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); // webpack 5.x 使用
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // minify extract js to css
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const router = require('./router');

const { routes } = router;
// const autoprefixer = require('autoprefixer');

// webpack.config.js 通用插件配置
const getHtmlWebpackPlugins = () => {
    const mode = process.env.TEST_MODE || null;
    console.log('get plugin mode:', mode)
    if (mode && mode === 'template') {
        // 模板测试
        return glob.sync(path.resolve(__dirname, '../test/templates') + '/**/*.{jsx,ejs}').map(item => {
            var prefixPath = path.resolve(__dirname, '..\/test\/templates');
            var filePath = item.substring(prefixPath.length + 1, item.lastIndexOf('.'));
            console.log('加载模板路径：', filePath);
            return new HTMLWebpackPlugin({
                template: item,
                filename: filePath + '.html',
                minify: {
                    // 压缩 HTML 文件
                    removeComments: true, // 移除 HTML 中的注释
                    collapseWhitespace: true, // 删除空白符与换行符
                    minifyCSS: true // 压缩内联 css
                },
                favicon: './config/icon.png',
                title: 'title',
                chunks: ['common', 'test/'+filePath],
                inject: true,
            })
        });
    }
    return routes.map(item => {
        console.log('loading template info:', JSON.stringify(item));
        const filename = item.path.lastIndexOf('\/') === String(item.path).length - 1 ? item.path + 'index.html' : item.path + ".html";
        const chunks = ['common'];
        if (item.main) {
            chunks.push(item.path.substring(1));
        }
        console.log('get chunks:',chunks)
        return new HTMLWebpackPlugin({
            template: path.resolve(__dirname, '../src/templates/' + item.template),
            filename:filename.substring(1),
            minify: {
                // 压缩 HTML 文件
                removeComments: true, // 移除 HTML 中的注释
                collapseWhitespace: true, // 删除空白符与换行符
                minifyCSS: true // 压缩内联 css
            },
            favicon: './config/icon.png',
            title: item.name,
            chunks,
            inject: true,
        })
    })
}




const plugins = [
    new webpack.ProvidePlugin({  // 配置shim预置依赖
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery',
        Popper: ['popper.js', 'default'],
        // In case you imported plugins individually, you must also require them here:
        Util: "exports-loader?Util!bootstrap/js/dist/util",
        Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
    }),
    require('autoprefixer'),        // autoprefixer可以自动在样式中添加浏览器厂商前缀，避免手动处理样式兼容问题
    new MiniCssExtractPlugin({
        // 类似 webpackOptions.output里面的配置 可以忽略  css 文件分离
        filename: 'css/[name].[chunkhash:8].css',
        chunkFilename: '[id].css',
    }),
    new CssMinimizerPlugin(), // 配置css-minimizer-webpack-plugin
    new webpack.DefinePlugin({
        'process.env.build_lang': `"${process.env.build_lang}"`,
    }),
    // 如果不想在 watch 触发增量构建后删除 index.html 文件，可以在 CleanWebpackPlugin 中配置 cleanStaleWebpackAssets 选项 来实现
    new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
    ...getHtmlWebpackPlugins(),
    new webpack.HotModuleReplacementPlugin(),
]
module.exports = plugins;


```


### config/router.js

这里主要是路由的自定义配置，实现指定页面的指定js依赖的功能。


```
/* eslint-disable no-undef */
// 路由配置文件
const routes = [
    {
        path: '/index',
        template: 'index.ejs',
        name: '首页',
    },
    {
        path: '/page/1',
        template: 'page/1.ejs',
        name: '测试页面',
        main: '/pages/test.js',
    },

]

module.exports = { routes };


```


### webpack.config.js 

```

/* eslint-disable no-undef */
const path = require("path"); //  Node.js 处理文件路径的模块
const glob = require("glob");
const plugins = require("./config/plugins"); // 配置插件
const rules = require("./config/rules"); // 加载器
const proxy = require("./config/proxy");

const router = require("./config/router");

const { routes } = router;

/**
 * 入口文件
 */
const entries = function () {
  const mode = process.env.TEST_MODE || null;
  let map = {
    common: ["@/app.js", "lodash"],
  };
  if (process.env.NODE_ENV === "development") {
    map.hot = "webpack/hot/dev-server";
    map.client = "webpack-dev-server/client/index.js?hot=true&live-reload=true";
  }
  if (mode && mode === "template") {
    //  模板测试
    // add test entries
    glob
      .sync(path.resolve(__dirname, "./test/templates") + "/**/*.{js,ts}")
      .forEach((item) => {
        let prefixPath = path.resolve(__dirname, "./test/templates");
        let filePath = item.substring(
          prefixPath.length + 1,
          item.lastIndexOf("."),
        );
        console.log("loading module", filePath);
        map["test/" + filePath] = item;
      });
  } else {
    routes
      .filter((item) => item.main)
      .map((item) => {
        const main = path.resolve(__dirname, "./src" + item.main);
        console.log("loading  entry file:", main);
        map[item.path.substring(1)] = main;
      });
  }
  return map;
};

module.exports = {
  //  入口文件
  // name: language, // 语言名称
  entry: entries(),
  watch: true,
  watchOptions: {
    poll: 1000, // 每秒询问多少次
    aggregateTimeout: 500, //防抖 多少毫秒后再次触发
    ignored: /node_modules/, //忽略时时监听
  },
  mode: process.env.NODE_ENV === "production" ? "none" : "development",
  output: {
    //  存放打包后的文件的位置
    path: path.resolve(__dirname, "./dist"),
    //  打包后的文件名
    filename: "[name].bundle.[contenthash:8].js",
    chunkFilename: "[name].js", // 代码拆分后的文件名
    // clean: true,
    // pathinfo: false,
    publicPath: "/",
  },
  target: "web",
  cache: {
    type: "memory",
  },
  module: { rules: rules },
  plugins: [
    ...plugins,
    // new I18nPlugin(rq),
  ],
  devServer: {
    devMiddleware: {
      index: true,
      mimeTypes: { "text/html": ["phtml"] },
      publicPath: "/dist",
      serverSideRender: true,
      writeToDisk: true,
    },
    static: "./dist",
    proxy,
    // publicPath: 'http://localhost:8080',
    // port: 9000,
    historyApiFallback: true,
    compress: true,
    hot: false,
    client: false,
    open: true, // when open is enabled, the dev server will open the browser.
  },
  resolve: {
    extensions: [".ts", ".js", ".json"],
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  context: path.resolve(__dirname),
};

```



webpack.config.js是webpack打包的配置整合，可通过如上配置实现简单的多页面配置性编译，新增路由可通过在config/router.js中添加页面路由配置即可。