webpack多入口代码分割问题

项目中有多个html文件,互相功能独立,但是有些会共同引用同一个module,比如多个页面都会使用lodash。

webpack中通过遍历项目中的pages目录中的html文件,将html文件同级的js/index.js作为入口,动态配置HTMLWebpackPlugin。

现在想使用webpack的optimization.splitChunks将共同引用的模块单独打包,
比如a.html和b.html中的index.js都会引入lodash,正常打包后,a和b都会生成a.js和b.js,a.js和b.js中都会包含lodash(假如lodash是100kb),那么a.js和b.js都会是101kb,我想让a和b自身的业务逻辑单独打包,lodash单独打包,然后a.html和b.html共同引入lodash.js
那么应该怎么配置HTMLWebpackPlugin的chunk呢?

目录结构

.
├── assets
│   └── img
│       ├── loading-icon.gif
│       ├── logo.png
├── pages
│   ├── page-a
│   │   ├── index.html
│   │   ├── js
│   │   │   └── index.js
│   │   └── styles
│   │       └── index.scss
│   ├── page-b
│   │   ├── index.html
│   │   └── js
│   │       └── index.js
├── static
└── styles
    ├── common.scss
    ├── reset.css
    └── variables.scss

下面是我的webpack配置

webpack.base.config.js:

// 基础配置文件,包含了不同环境通用配置
const path = require('path')
const fs = require('fs')
const config = require('./config.js')
const { getHTMLFileList } = require('./utils')

const HTMLWebpackPlugin = require('html-webpack-plugin') // html-webpack-plugin  用于生成html
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const devMode = process.env.NODE_ENV !== 'production'

const htmlPath = path.join(__dirname, '..', '/src/pages/')
const htmlFIleList = getHTMLFileList(htmlPath)

// 根据每个html文件来生成HTMLWebpackPlugin实例 和 入口列表
const HTMLPlugins = [] // 保存HTMLWebpackPlugin实例
const Entries = {} // 保存入口列表

htmlFIleList.forEach(page => {
  // 生成HTMLWebpackPlugin实例和入口列表
  const htmlConfig = {
    filename: `${page.name}.html`, // 生成的html文件名
    template: page.path, // 原文件位置
  }

  const found = config.ignoreJs.findIndex(val => {
    return val === page.name
  }) // 筛选没有入口js的名

  const jsFilePath = path.join(page.dirPath, 'js/index.js')

  if (found === -1 && fs.existsSync(jsFilePath)) {
    // 有入口js文件的html,添加本页的入口js和公用js,并将入口js写入Entries中
    htmlConfig.chunks = [page.name] // html文件绑定入口JS和公用JS
    Entries[page.name] = jsFilePath // 每个HTML文件添加一个入口,除非设置不用
  } else {
    // 没有入口js文件,chunk为空
    htmlConfig.chunks = []
  }
  // console.log('====htmlConfig====', htmlConfig)
  HTMLPlugins.push(new HTMLWebpackPlugin(htmlConfig))
})

const ESLintRule = () => ({
  test: /\.js$/,
  loader: 'eslint-loader',
  enforce: 'pre', // loader种类,pre / post
  include: [config.srcPath], // 检测的目录
  options: {
    formatter: require('eslint-friendly-formatter'), // 错误信息显示在终端上
    // 如果option设置为true,Loader将始终返回警告。如果您正在使用热模块更换,您可能希望在开发中启用此功能,否则在出现夹板错误时将跳过更新。
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

module.exports = {
  context: config.projectPath, // 入口、插件路径会基于context查找
  entry: Entries,
  resolve: {
    extensions: ['.js', '.scss', '.css', '.json'], // 自动补全的扩展名
    alias: {
      // 省略路径
      '@': path.join(__dirname, '..', 'src')
    }
  },
  module: {
    // 用了noParse的模块将不会被loaders解析,所以当我们使用的库如果太大,并且其中不包含import require、define的调用,我们就可以使用这项配置来提升性能, 让 Webpack 忽略对部分没采用模块化的文件的递归解析处理。
    noParse: function (content) {
      return /jquery|lodash/.test(content)
    },
    // 处理字体,生成图片,JS babel
    rules: [
      ...(config.dev.useEslint ? [ESLintRule()] : []),
      {
        // html loader,主要用于html内图片等src路径解析
        test: /\.html$/,
        use: {
          loader: 'html-loader',
          options: {
            minimize: false,
            removeComments: false,
            collapseWhitespace: false
          }
        }
      },
      {
        test: /\.js$/,
        include: [config.srcPath], // 在源文件目录查询
        exclude: [config.assetsSubDirectory, /node_modules/],
        use: [{ loader: 'babel-loader' }]
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        include: [config.srcPath], // 在源文件目录查询
        exclude: [config.assetsSubDirectory], // 忽略第三方的任何代码
        use: [
          {
            // 导入字体文件,并最打包到output.path+ options.name对应的路径中
            loader: 'url-loader',
            options: {
              limit: 8192,
              name: 'fonts/[name].[hash:7].[ext]',
              fallback: 'file-loader'
            }
          }
        ]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        include: [config.srcPath], // 在源文件目录查询
        // exclude: [config.assetsSubDirectory],    // 忽略第三方的任何代码
        use: [
          {
            // 图片文件小于8k时编译成dataUrl直接嵌入页面,超过8k回退使用file-loader
            loader: 'url-loader',
            options: {
              limit: 8192, // 8k
              name: 'img/[name].[hash:7].[ext]', // 回退使用file-loader时的名称
              fallback: 'file-loader' // 当超过8192byte时,会回退使用file-loader
            }
          }
        ]
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 8192,
          name: 'media/[name].[hash:7].[ext]',
          fallback: 'file-loader'
        }
      }
    ]
  },
  plugins: [
    // 生成HTML文件
    ...HTMLPlugins // 扩展运算符生成所有HTMLPlugins
  ]
}

webpack.prod.config.js

// 生产环境配置文件

const webpackBase = require('./webpack.config.base.js') // 引入基础配置
const config = require('./config.js') // 引入配置

const webpack = require('webpack') // 用于引用官方插件
const webpackMerge = require('webpack-merge') // 用于合并配置文件
const CleanWebpackPlugin = require('clean-webpack-plugin') // 用于清除文件夹
const UglifyJSPlugin = require('uglifyjs-webpack-plugin') // 用于压缩js文件
const CopyWebpackPlugin = require('copy-webpack-plugin') // 用于拷贝文件
const TerserJSPlugin = require('terser-webpack-plugin') // This plugin uses terser to minify your JavaScript.
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // webpack v4开始,处理css
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 压缩提取出的css,并解决ExtractTextPlugin分离出的js重复问题(多个文件引入同一css文件)
const devMode = process.env.NODE_ENV !== 'production'
console.log('====process.env====', process.env.NODE_ENV)
console.log('====devMode====', devMode)

const webpackProd = {
  // 生产配置文件
  output: {
    publicPath: config.build.assetsPublicPath, // 相对于服务器根目录的路径,用于加载资源。
    filename: 'js/[name].[contenthash].bundle.js', // 构建文件名
    chunkFilename: 'js/[id].[contenthash].js' // 按需加载的文件名
  },
  // 是否开启 sourceMap
  devtool: config.build.prodSourceMap ? config.build.devtool : false,
  mode: 'production',
  optimization: {
    splitChunks: {
      chunks: 'all', // 哪些块进行优化,"initial"|"all"|"async"(默认) (string function)
      minSize: 30000, // 要生成的块的最小大小,默认30000(30k)
      minChunks: 1, // 分割前必须共享模块的最小块数,默认1
      maxAsyncRequests: 5, // 最大异步并行请求数,默认5
      maxInitialRequests: 3, // 最大初始化并行请求书,默认3
      automaticNameDelimiter: '~', // 生成的名称分隔符,默认~  (string)
      name: true, // 拆分快的名称,默认true(function true string)
      cacheGroups: {
        // 缓存组,可以继承和/或覆盖任何选项
        // priority: 0,                   // 缓存组的优先级,默认0
        // test: null,                    // 控制此缓存组选择的模块,默认空(function RegExp string)
        // reuseExistingChunk: true,      // 如果当前块包含已从主束拆分的模块,是否重用它。
        vendors: {
          name: 'vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    minimizer: [
      new TerserJSPlugin({}),
      // 压缩提取出的css,并解决ExtractTextPlugin分离出的js重复问题(多个文件引入同一css文件)
      new OptimizeCSSAssetsPlugin({
        assetNameRegExp: /\.css$/g,
        cssProcessor: require('cssnano'),
        canPrint: true
      })
    ]
  },
  plugins: [
    // 提取CSS
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: devMode ? '[name].css' : 'css/[name].[contenthash].css',
      chunkFilename: devMode ? '[id].css' : 'css/[id].[contenthash].css'
    }),

    // 根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境
    // 当供应商模块不变时,保持module.id稳定
    new webpack.HashedModuleIdsPlugin(),

    // 删除dist文件夹
    new CleanWebpackPlugin(),

    // 复制静态资源
    new CopyWebpackPlugin([
      {
        from: config.assetsSubDirectory,
        to: '../dist/static',
        ignore: ['.*']
      }
    ])
  ]
}

module.exports = webpackMerge(webpackBase, webpackProd)
阅读 2.7k
1 个回答

还少一个 HtmlWebpackPlugin 的配置

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进