用 Rspack 替代 Webpack,提升五倍构建速度

5,703 阅读3分钟

前言

通过本文你将理解rspack,并快速实现Vue2、Vue3项目配置,提升5倍多的项目构建性能,快速提高工作效率。

Webpack 构建问题

  • 原有大型后台系统项目 DEV 构建需要 3分钟以上;
  • 自动化部署构建阶段需要10分钟以上;
  • 生产打包构建需要2分钟;

Rspack 优化后

使用 Rspack 替代 Webpack 后:

  • DEV构建 3min -> 20s
  • 自动化部署 6min -> 3min
  • 打包速度 2min -> 20s

比 Webpack 更快 的 Rspack

Rspack 是基于 Rust 重写 Webpack,允许依赖 Webpack 的打包构建工具。

核心优势在于:

  • 快速的 Dev 启动性能。是开发者每天需要运行很多次的命令,但大型项目每次都需要等待 10 分钟,这对于工程师来说非常痛苦,因此优化开发模式下启动的时间至关重要。npm run dev
  • 高效的 Build 性能。经常在 CI/CD 环境中运行,它决定了应用生产交付的效率。有些应用在生产环境中需要 20 到 30 分钟的构建时间,如果能缩短这段时间,对开发流程也将非常有帮助。npm run build
  • 灵活的配置。用户工程的配置非常灵活,不够统一。在之前的尝试中,将 Webpack 配置迁移到其他构建工具时,我们遇到了许多问题,因为其他构建工具的配置不如 Webpack 灵活。
  • 生产环境的优化能力。在启用 Rspack 之前,我们尝试了社区内的各种方案,但它们都面临着一定程度的生产环境负优化,例如拆分包不够精细等。因此,优化生产环境的产物是我们不可放弃的功能。

————摘自Rspack官网

起步

安装依赖:

pnpm add @rspack/cli@0.3.4

配置npm脚本:

// package.json
{
    scripts: {
        dev: "rspack serve",
        build: "rspack build"
    }
}

Vue2 + Rspack 示例

安装好依赖和脚本后,我们接着创建配置文件 rspack.config.js

// rspack.config.js
const { VueLoaderPlugin } = require('vue-loader')
const rspack = require('@rspack/core')
const path = require('path')
const chalk = require('chalk')

const port = {
  production: 9001,
  // ...
}[process.env.VUE_APP_ENV || 'development']

const folderName = {
  development: 'dev_dir',
  // ...
}[process.env.VUE_APP_ENV || 'development']

const isProd = /production/i.test(process.env.NODE_ENV)

const config = {
  context: __dirname,
  entry: {
    main: './src/main.js'
  },
  output: Object.assign({
    path: path.resolve(__dirname, folderName)
  }, isProd
    ? {
        filename: 'static/javascript/[name].[contenthash:8].js',
        cssFilename: 'static/css/[name].[contenthash:8].js',
        assetModuleFilename: 'static/assets/[hash][ext][query]',
        publicPath: './',
        clean: true
      }
    : {}),
  builtins: {
    html: [
      {
        template: 'public/index.html',
        minify: isProd
      }
    ],
    define: {
      'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`,
      'process.env.VUE_APP_ENV': `"${process.env.VUE_APP_ENV}"`
    }
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    },
    extensions: ['.js', '.vue'],
    fallback: {
      url: require.resolve('url/'),
      stream: require.resolve('stream-browserify'),
      buffer: require.resolve('buffer'),
      process: require.resolve('process'),
      module: require.resolve('module')
    }
  },
  devServer: {
    historyApiFallback: true,
    open: true,
    port
  },
  devtool: false,
  plugins: [
    new rspack.ProvidePlugin({
      process: 'process/browser',
      Buffer: ['buffer', 'Buffer']
    }),
    new VueLoaderPlugin()
  ].concat(isProd
    ? [new rspack.ProgressPlugin({
        prefix: chalk.green(`端口${port},文件夹${folderName}, 接口环境${process.env.VUE_APP_ENV}, ${isProd ? '生产' : '开发'}环境${process.env.NODE_ENV} \n`)
      }),
      new rspack.CopyRspackPlugin({
        patterns: [
          {
            from: 'public/static',
            to: 'static'
          }
        ]
      }),
      new rspack.SwcJsMinimizerRspackPlugin({
        dropConsole: isProd
      })]
    : []),
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: 'vue-loader'
      },
      {
        test: /\.scss$/,
        use: ['vue-style-loader', 'css-loader', 'sass-loader'],
        type: 'javascript/auto'
      },
      {
        test: /\.(styl|stylus)$/,
        use: ['vue-style-loader', 'css-loader', 'stylus-loader'],
        type: 'javascript/auto'
      },
      {
        test: /\.css$/,
        use: ['vue-style-loader', 'css-loader'],
        type: 'javascript/auto'
      },
      {
        test: /\.(svg|png|gif|jpe?g)$/,
        type: 'asset/resource'
      },
      {
        test: /^BUILD_ID$/,
        type: 'asset/source'
      }
    ]
  }
}
module.exports = config

Dev 执行结果:

image.png

image.png

Vue3 + TS + Rspack 示例

const { VueLoaderPlugin } = require('vue-loader')
const rspack = require('@rspack/core')
const path = require('path')
const chalk = require('chalk')
const packageName = require('./package.json').name
const isProd = /production/i.test(process.env.NODE_ENV || '')
const folderName = process.env.OUT_DIR
const port = 666666

/** @type {import('@rspack/cli').Configuration} */
const config = {
  context: __dirname,
  entry: {
    main: './src/main.ts'
  },
  output: Object.assign({
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    chunkLoadingGlobal: `webpackJsonp_${packageName}`
  }, isProd
    ? {
      path: path.resolve(__dirname, folderName),
      filename: 'static/javascript/[name].[contenthash:8].js',
      cssFilename: 'static/css/[name].[contenthash:8].css',
      assetModuleFilename: 'static/assets/[hash][ext][query]',
      publicPath: './',
      clean: true
    }
    : {}),
  builtins: {
    html: [
      {
        template: 'public/index.html',
        minify: isProd,
        title: 'finder-dms'
      }
    ],
    define: {
      'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`
    }
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    },
    extensions: ['.vue', '.js', '.ts']
  },
  devServer: {
    historyApiFallback: true,
    open: false,
    port,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  },
  devtool: false,
  plugins: [
    new VueLoaderPlugin()
  ].concat(isProd
    ? [new rspack.ProgressPlugin({
      prefix: chalk.green(`端口${port},输出目录${folderName}, 接口环境由主应用决定, ${isProd ? '生产' : '开发'}环境${process.env.NODE_ENV} \n`)
    }),
    new rspack.SwcJsMinimizerRspackPlugin({
      dropConsole: isProd
    })]
    : []),
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          experimentalInlineMatchResource: true
        }
      },
      {
        test: /\.ts$/,
        loader: 'builtin:swc-loader',
        options: {
          sourceMap: true,
          jsc: {
            parser: {
              syntax: 'typescript'
            }
          }
        },
        type: 'javascript/auto'
      },
      {
        test: /.(styl|stylus)$/,
        loader: 'stylus-loader',
        type: 'css'
      },
      {
        test: /.scss$/,
        loader: 'sass-loader',
        type: 'css'
      },
      {
        test: /\.(svg|png|gif|jpe?g)$/,
        type: 'asset/resource'
      },
      {
        test: /^BUILD_ID$/,
        type: 'asset/source'
      }
    ]
  }
}
module.exports = config

总结

本文介绍了rspack以及如何在Vue2和Vue3项目中使用rspack进行配置,以提高项目构建性能和工作效率。使用rspack替代webpack后,构建时间大幅缩短的优势。

如果你对巨型应用构建问题很头疼的话,不妨试试接入Rspack。