Webpack如何使用DllPlugin打包公共模块,并且能自动找寻入口模块下的子模块。

技术栈:create-ract-app、webpack

  • dll.conf.js
const path = require('path')
const webpack = require('webpack')
const {
  CleanWebpackPlugin
} = require('clean-webpack-plugin');


// dll文件存放的目录
const dllPath = 'public/vendor'

module.exports = {
  entry: {
    // 需要提取的库文件
    vendor: ['react', 'react-dom', 'react-native-storage', 'react-router-dom', 'antd', 'mobx', 'mobx-react',
      'react-css-modules', 'history', 'lodash', 'babel-polyfill'],
    common: ['mime', 'mime-db', 'stream-http', 'ajv', 'psl', 'elliptic', 'sshpk', 'bluebird', 'pako', 'react-error-overlay']
  },
  output: {
    path: path.join(__dirname, dllPath),
    filename: '[name].dll.js',
    // vendor.dll.js中暴露出的全局变量名
    // 保持与 webpack.DllPlugin 中名称一致
    library: '[name]_[hash]'
  },
  plugins: [
    // 清除之前的dll文件
    new CleanWebpackPlugin(),
    // 设置环境变量
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify('production')
      }
    }),
    // manifest.json 描述动态链接库包含了哪些内容
    new webpack.DllPlugin({
      path: path.join(__dirname, dllPath, '[name]-manifest.json'),
      // 保持与 output.library 中名称一致
      name: '[name]_[hash]',
      context: process.cwd()
    })

  ],
  // optimization:{
  //   splitChunks: {
  //     cacheGroups: {
  //       vendor: {
  //         name: 'vendor',
  //         chunks: 'initial',
  //         minChunks: 10
  //       }
  //     }
  //   }
  // }
}

image.png

  • config-overrides.js


const addCustomize = () => config => {
    if (process.env.NODE_ENV === 'production') {
        config.devtool = false; //去掉map文件
        config.plugins.push(
            new webpack.DefinePlugin({
                'process.env': {
                    NODE_ENV: JSON.stringify('production')
                }
            }),
            new webpack.DllReferencePlugin({
                context: process.cwd(),
                manifest: require('./public/vendor/vendor-manifest.json')
            }),
            new webpack.DllReferencePlugin({
                context: process.cwd(),
                manifest: require('./public/vendor/common-manifest.json')
            }),
            // 将 dll 注入到 生成的 html 模板中
            new AddAssetHtmlPlugin({
                // dll文件位置
                filepath: path.resolve(__dirname, './public/vendor/*.js'),
                // dll 引用路径 一般设置为 CDN
                publicPath: 'http://10.20.26.19/ttt/vendor',
                // dll最终输出的目录
                outputPath: './vendor'
            }),
            // 可以解析项目
            new BundleAnalyzerPlugin({
                analyzerMode: 'static'
            })
        )
    }
    return config;
}



module.exports = override(
    
    addCustomize(),

)

这样打包出来dll文件有两个,一个是vendor.js,另一个是common.js,目前这样的配置能够很好的使用。

1、但我的问题是,我已经写了entry入口一些第三方包,为何打包的时候不能自动找寻入口包下面的一些引用包,需要我在common中一一列举出来,大家看看是否有其他简单的方式?
2、为何单个的dll文件为何如此大,要如何分割?
阅读 3k
1 个回答

我的思路是通过fs读取目录下有哪些manifest.json,然后循环添加对应个数的DllReferencePlugin

// vue.config.js

const webpack = require('webpack')
const path = require('path')
const fs = require('fs')
const isProd = process.env.NODE_ENV === 'production'
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

function resolve() {
  return path.resolve(__dirname, ...arguments)
}

module.exports = {
  chainWebpack(config) {
    config.when(
      isProd,
      () => {
        config.plugins.delete('preload').delete('prefetch')

        // 我写的demo里,dllplugin生成的若干json及若干js放在了public/js/dll目录下
        fs.readdirSync(resolve('./public/js/dll'))
          .forEach(file => {
            // 只关心manifest.json的文件
            if (!file.endsWith('manifest.json')) {
              return
            }
            // 每有一个manifest.json,就向plugins里增加一个DllReferencePlugin
            config
              // 有多个DllReferencePlugin时不能都叫同一个名,因此拼接manifest.json文件全名做键
              .plugin('dll-reference-' + file)
              .use(webpack.DllReferencePlugin, [{
                // 拼接manifest.json文件完整路径,引入json
                manifest: resolve('./public/js/dll', file)
              }])
          })

        config
          .plugin('add-asset')
            .after('html')
            .use(AddAssetHtmlWebpackPlugin)
              .tap(options => {
                const outputPath = 'js/dll'
                const publicPath = config.output.get('publicPath') + outputPath
                options.push(
                  fs.readdirSync('./public/js/dll')
                    .reduce((op, curr) => {
                      // 读取public/js/dll下的所有文件,只处理js文件
                      if (!curr.endsWith('.js')) {
                        return op
                      }
                      op.push({
                        filepath: require.resolve(resolve(__dirname, './public/js/dll', curr)),
                        outputPath, publicPath
                      })
                      return op
                    }, [])
                )
                /*
                最后生成的options结构是:
                [
                  [
                      [{
                        filepath: 'public/js/dll/element.js',
                        outputPath, publicPath
                      }],
                      [{
                        filepath: 'public/js/dll/lodash.js',
                        outputPath, publicPath
                      }]
                      ,
                      ...........等等等等
                  ]
                ]
                */
                return options
              })
              .end()
      }
    )
  }
}

打包出来的js体积大的原因可能有两点:

  1. 依赖本身就大;
  2. 构建dll时没有配置生产模式;
  3. 多配置几个entry,entry内每项的数组长度小些,多生成几个js。你需要的js代码量就那么多了,js多点,每个js体积不就小了么。除数越大,值就越小。

配置生产模式:

module.exports = {
  mode: 'production',
  plugins: [
    new DefinePlugin({
      'process.env.NODE_ENV': "'production'"
    })
  ]
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏