create-react-app创建的项目打包优化

982 阅读3分钟

项目打包慢,包的体积大这个问题一直是我的心头大事。

碎碎念

在项目测试阶段,发现问题修改后,打包就需要1小时,无奈只能等着把包打完,经常性的我们下班回去打包。而对于后台来说也是一个问题,每次把包解压到服务器上又是一个漫长的过程,也会因为前端包的版本过多,服务器的内存不够,需要定期的去清理前端包。

对于这件事情我也尝试了很多次,但是都以失败告终,总会因为时间不够用,优化到一半被拉去开会,或者是上线任务紧急,不得以暂时放下这件事。

今年把这件事情提上了日程,终于可以花全部的精力去做这件事情啦!

准备工作

对于react脚手架打包优化的问题,我也在网上翻阅了资料,试过了路由懒加载、Gzip、图片体积,这些优化后,并没有任何的效果。又通过安装webpack-bundle-analyzer插件来分析哪些包或者文件过大,从而进行优化。

分析过后,发现react、antd等等依赖包的体积都比较大,想到这些依赖的版本很少去变动,就决定才去DllPlugin把这些依赖包进行单独构建

DllPlugin原理

DllPlugin 可以将特定的类库提前打包成动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据,把基础模块独立出来打包到单独的动态连接库里,当需要导入的模块在动态连接库里的时候,模块不用再次被打包,而是去动态连接库里获取。

这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。

代码实现

  1. 在根目录下新建文件webpack.dll.config.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', 'antd', 'ali-oss', 'react-dom', 'react-router-dom', 'axios', 'less-loader'],
  },
  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: "'production'",
      },
    }),
    // manifest.json 描述动态链接库包含了哪些内容
    new webpack.DllPlugin({

      path: path.join(__dirname, dllPath, '[name]-manifest.json'),
      // 保持与 output.library 中名称一致
      name: '[name]_[hash]',
      context: process.cwd(),
    }),
  ],
}

  1. package.json文件
...
"scripts": {
    "start": " react-app-rewired start",
    "build": " react-app-rewired --max_old_space_size=15000 build",
    "dll": "webpack --mode production --progress --config ./webpack.dll.config.js"
  },
  1. 执行命令:npm run dll在public文件夹下面生成vendor文件即可

  2. 在config-overrides.js文件加入

/* eslint-disable */
const path = require('path')
const {
  override,
  fixBabelImports,
  addLessLoader,
  addWebpackPlugin,
  addWebpackAlias,
} = require('customize-cra')
const rewireReactHotLoader = require('react-app-rewire-hot-loader')
const ProgressBarPlugin = require("progress-bar-webpack-plugin")
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const chalk = require("chalk")
const webpack = require("webpack")
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')

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

module.exports = override(
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: true,
  }),
  addLessLoader({
    lessOptions: {
      javascriptEnabled: true,
      modifyVars: { '@primary-color': '#40A9FF' },
    },
  }),
  addWebpackPlugin(
    // 打包进度条:含内容、进度条、进度百分比、消耗时间
    new ProgressBarPlugin({
      format: `  :msg [:bar] ${chalk.green.bold(":percent")} (:elapsed s)`,
    }),
    //  为模块提供中间缓存步骤,对二次编译有很大的提升
    new HardSourceWebpackPlugin(),
    new BundleAnalyzerPlugin()
  ),
  addWebpackAlias({
    '@': path.resolve(__dirname, 'src'),
  }),
  addCustomize(),
  (config, env) => {
    if (env === 'production') {
      config.devtool = false
    }
    config = rewireReactHotLoader(config, env)
    return config
  },
)

5.npm run build就成功啦!!!

最终实现了之前打包1小时,现在10分钟。而且包的体积也从380M到67M

总结

实现DllPlugin的过程是艰辛的,因为参考别人的文章时,没有和自己的项目是一模一样的配置,要先理解大致原理,再去结合自己的项目进行修改、配置。要坚信别人可以做到的,你也可以!!!