webpack 系列之多页面应用打包通用方案

3,054 阅读2分钟

多页面应用(MPA)

每次页面跳转的时候,后台服务器都会返回一个新的 HTML 文档,这种类型的网站也就是多页网站,也叫多页应用。多页面跳转时会刷新所有资源,每个公共资源 (js、css 等) 会选择性地重新加载。

为什么要多页面打包

在某些场景下,单页应用的开发模式并不适用。比如电商网站的一些活动页:

https://www.shopping.com/activity/activity1.html
https://www.shopping.com/activity/activity2.html
https://www.shopping.com/activity/activity3.html

如上述的三个页面是完全不相干的活动页,页面之间并没有共享的数据,在这种场景下,就可以使用 webpack 多页面打包的方案

多页面打包的原理

原理:要实现多页面打包,需要配置多个入口、多个 ( 出口依赖的 ) 模板文件

例如在 src 目录下有 detail、index、list、login 目录,在各自的目录下都有 index.js 和 index.html 文件,我们在 webpack.config.js 文件中配置以 index.js 为入口,index.html 为模板文件的多页面打包配置。

多页面打包的方案

底层实现:借助 glob 模块拿到多个入口文件,遍历后得到多个模板文件,最后将入口文件和模板文件都暴露出去,在 webpack.config.js 中使用。

const path = require('path');
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const setMpa = () => {

    // 多页面打包的入口集合
    const entry = {};
    // 多页面打包的模板集合
    const htmlWebpackPlugins = []

    // 借助 glob 获取 src 目录下的所有入口文件
    const entryFiles = glob.sync(path.resolve(__dirname, "./src/*/index.js"));

    // 遍历文件集合,生成所需要的 entry、htmlWebpackPlugins 集合
    entryFiles.map((item, index) => {
        const match = item.match(/src\/(.*)\/index\.js$/);
        const pageName = match?.[1];
        entry[pageName] = item;
        // 多页面所需要的模板集合
        htmlWebpackPlugins.push(
            new HtmlWebpackPlugin({
                title: pageName,
                filename: `${pageName}.html`,
                template: path.join(__dirname, `src/${pageName}/index.html`),
                chunks: [pageName]
            })
        )
    })
    // 对外输出页面打包需要的 入口集合
    return { entry, htmlWebpackPlugins }
}

module.exports = setMpa()

在 webpack.config.js 文件中导入该文件模块,并添加配置:

const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

// 导入多页面应用打包方案
const { entry, htmlwebpackplugins } = setMpa();

module.exports = {

  // 添加 entry 配置
  entry,

  output: {
    path: path.resolve(__dirname, "./mpa"),
    filename: "[name].js",
  },
  mode: "development",
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader",
          "less-loader",
        ],
      },
      {
        test: /\.(png|jpe?g|gif)$/,
        use: {
          loader: "url-loader",
          options: {
            name: "[name].[ext]",
            outputPath: "images/",
            publicPath: "../images",
            limit: 1024 * 3, //阈值 超过阈值的图片会独立文件,没有超过会处理成base64
          },
        },
      },
      {
        test: /\.(eot|woff|woff2)$/,
        use: {
          loader: "file-loader",
          options: {
            name: "[name].[ext]",
            publicPath: "../",
          },
        },
      },
    ],
  },

  plugins: [

    // 添加 htmlwebpackplugins
    ...htmlwebpackplugins,

    new MiniCssExtractPlugin({
      filename: "css/[name].css",
    }),
    new CleanWebpackPlugin(),
  ],
};

在 package.json 中配置命令 "mpa" : "webpack --config ./webpack.mpa.config.js" (命令仅用于测试)

"scripts": {
  "dev": "webpack",
  "serve": "webpack-dev-server",
  "mpa" : "webpack --config ./webpack.mpa.config.js"
},

在终端中执行 npm run mpa 打包,最后打包出来的结果如下:

多入口打包出来的文件如上图,每个文件我们都可以单独访问。