Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从 VueCLI 迁移到 Rsbuild #115

Open
bosens-China opened this issue Jul 25, 2024 · 0 comments
Open

从 VueCLI 迁移到 Rsbuild #115

bosens-China opened this issue Jul 25, 2024 · 0 comments
Labels
工具相关 工程化相关的东西

Comments

@bosens-China
Copy link
Owner

bosens-China commented Jul 25, 2024

rsbuild

公司有许多项目仍然停留在 Vue 2 上,每次通过 VueCLI 启动都需要等待大约 30 秒(在 M1 芯片上),每次热重载的速度也让人难以忍受,构建速度同样缓慢。虽然可以通过 esbuild 等工具改善,但在某天多次修改需求后,我决定忍无可忍,于是有了这篇文章。

决定迁移的原因主要有两点:

  1. 对 Node 22 支持不友好。由于项目基于 VueCLI 3,Node 版本的诸多变更导致无法启动,必须通过 nvm 切换到 16 版本。
  2. 构建和热更新速度慢。

在决定使用 Rsbuild 之后,我也进行了一番调研,备选方案有四个:

  1. Webpack+SWC 或 Webpack+Esbuild
  2. Vite
  3. Rsbuild
  4. Turbopack

以下是一些考虑:

  • 方案 1:速度虽然有所改善,但仍然基于 Webpack,整体优化空间有限。
  • Vite:虽然非常想迁移,但由于有许多使用 CommonJS 的包,迁移成本太大。
  • Rsbuild:Rust 版本的 Webpack,大部分 Webpack 配置都兼容,是首选。
  • Turbopack:暂时不支持 Vue 等框架。

1. 遵循官方迁移指南

官方提供了一个简短的迁移指南,可以根据文档对项目中涉及的部分进行更改。

查看迁移指南

2. 删除与 @vue/cli 相关的依赖

例如,我删除了以下依赖:

{
  "@vue/cli-plugin-babel": "~4.5.13",
  "@vue/cli-plugin-eslint": "~4.5.13",
  "@vue/cli-plugin-router": "~4.5.13",
  "@vue/cli-plugin-vuex": "~4.5.13",
  "@vue/cli-service": "~4.5.13"
}

3. 修订别名和路径简写

在项目中,通常会使用 @/xxx 的形式来引入模块,这属于 Webpack 的 alias 功能。此外,在 import 导入文件时可以省略 .vue 的扩展名,这也是 Webpack 的 extensions 功能。

在 Rsbuild 中,可以通过以下方式定义:

export default defineConfig({
  tools: {
    rspack: {
      devtool: "source-map",
      resolve: {
        extensions: [".vue", ".js", ".jsx", ".tsx", ".ts", ".json"],
        alias: {
          "@": path.resolve(__dirname, "src"),
        },
      },
    },
  },
});

4. 安装 JSX、Less 和 SCSS 支持

项目中经常会使用 Less,例如引入的 ant-design-vue 就使用了,因此需要支持 Less。

由于之前没有统一标准,项目需要同时支持 Less 和 SCSS。

Less 官方维护了一个插件,可以按照使用方式安装:Less 插件

对于 Sass 和 JSX,按照官方指南安装相应插件:Sass 插件 Vue2 JSX 插件

整理后的配置文件如下:

import { defineConfig } from "@rsbuild/core";
import { pluginLess } from "@rsbuild/plugin-less";
import { pluginSass } from "@rsbuild/plugin-sass";
import { pluginBabel } from "@rsbuild/plugin-babel";
import { pluginVue2 } from "@rsbuild/plugin-vue2";
import { pluginVue2Jsx } from "@rsbuild/plugin-vue2-jsx";

export default defineConfig({
  plugins: [
    pluginBabel({
      include: /\.(?:jsx|tsx)$/,
    }),
    pluginVue2(),
    pluginVue2Jsx(),
    pluginLess({
      lessLoaderOptions: {
        lessOptions: {
          modifyVars: {
            "primary-color": "#5384FE",
            "error-color": "#ED4D37",
            "btn-danger-bg": "#ED4D37",
            "btn-danger-border": "#ED4D37",
            "success-color": "#48BB78",
            "text-color": "#4A5568",
            "text-color-secondary": "#A0AEC0",
            "btn-default-color": "#718096",
            "border-color-base": "#CBD5E0",
            "input-placeholder-color": "#A0AEC0",
            "tooltip-bg": "rgba(26,32,44,.8)",
            "heading-color": "#4A5568",
            "background-color-base": "#F7F8FA",
          },
          javascriptEnabled: true,
          math: "always",
        },
      },
    }),
    pluginSass({
      sassLoaderOptions: {
        additionalData:
          '@import "@/styles/variables.scss";@import "@/styles/mixin.scss";', // @import "@/styles/localIcon.scss";
      },
    }),
  ],
});

需要注意的是,在使用 JSX 时,需要为 <script> 标签指定 lang 属性,下面是一个示例:

<script lang="jsx">
export default {
  render() {
    return (
      <div class="default">
       xxx
      </div>
    );
  },
};

此外,直接在 JS 中编写 JSX 似乎没有什么方法能实现。之前有一个 mixin.js 文件,后来改为 .vue 文件返回 JSX 信息。

5. Node Polyfill

某些包可能使用了 Node 的核心库,例如 node:util,而 Node 核心库默认情况下无法在浏览器环境下运行。

安装 Node Polyfill 插件,它会按需注入所需的库。

import { pluginNodePolyfill } from "@rsbuild/plugin-node-polyfill";

export default defineConfig({
  plugins: [pluginNodePolyfill()],
});

其他注意事项

SCSS 语法升级

在迁移过程中,需要对 SCSS 写法进行升级,具体可以查看警告信息。例如,这里的 SCSS 写法不被支持:

.key-copy {
  position: absolute;
  right: 24px;
  top: 50%;
  // padding-left: 30px;
  @include btn-hover;
  transform: translateY(-50%);
}

需要调整为:

.key-copy {
  @include btn-hover;
  & {
    position: absolute;
    right: 24px;
    top: 50%;
    // padding-left: 30px;
    transform: translateY(-50%);
  }
}

对不规范的 import 语法调整

之前使用的写法是:

<style lang="scss" scoped>
  @import url("~@/layout/style.scss");
</style>

调整为:

@import "@/layout/style.scss";

无效的 ::v-deep 调整

只有在 scoped 标签下 ::v-deep 才是有效的语法,需要删除未添加 scoped 的 style 标签。

按需引入 UI 库

在 VueCLI 中可能通过 babel-import 的形式来引入,在 Rsbuild 中调整为:

export default defineConfig({
  source: {
    transformImport: [
      { libraryName: "ant-design-vue", libraryDirectory: "es", style: true },
    ],
  },
});

具体可以参考文档 transform-import

polyfill 按需注入

在使用 ES6 的语法时,默认情况下不会保证兼容性,可以通过 polyfill 来开启。

export default {
  output: {
    polyfill: "usage",
  },
};

删除 console、debugger

export default {
  performance: {
    removeConsole: true,
    removeMomentLocale: true,
  },
};

更多优化点可以查看 performance

编译 node_modules 下的包

可能某些原因需要对 node_modules 文件夹进行编译,可以使用下面方式:

import path from "node:path";

export default {
  source: {
    include: [
      // 所有包含 `node_modules/query-string/` 的路径都会被匹配到
      /node_modules[\\/]query-string[\\/]/,
    ],
  },
};

详细文档参见:source include

publicPath

在使用 webpack 的时候经常会配置 publicPath 为 ./ ,Rsbuild 同样支持。

import { defineConfig } from "@rsbuild/core";

export default defineConfig({
  output: {
    assetPrefix: "/admin-user/",
  },
});

不过注意,在 assetPrefix 中尽量不要使用 ./ 写法,否则会导致资源查找不到问题。

具体可以查看文档了解更多 outputassetprefix

最后

运行到这里基本就迁移成功了之后删除掉不需要的文件:

  • vue.config.js
  • babel.config.js

迁移下来速度从启动的 30s 到 5s 提升了大概 6 倍,不过更重要的是热重载基本更改后就生效。最后如果有什么错误之处欢迎指出。

下面是一份我最终使用的配置文件

rsbuild.config.ts

import { defineConfig, loadEnv } from "@rsbuild/core";
import path from "path";
import { pluginLess } from "@rsbuild/plugin-less";
import { pluginSass } from "@rsbuild/plugin-sass";
import { pluginBabel } from "@rsbuild/plugin-babel";
import { pluginVue2 } from "@rsbuild/plugin-vue2";
import { pluginVue2Jsx } from "@rsbuild/plugin-vue2-jsx";
import { pluginNodePolyfill } from "@rsbuild/plugin-node-polyfill";

const { publicVars } = loadEnv({ prefixes: ["VUE_APP_"] });

export default defineConfig({
  output: {
    polyfill: "usage",
  },
  performance: {
    chunkSplit: {
      strategy: "split-by-experience",
    },
    removeConsole: true,
    removeMomentLocale: true,
  },
  plugins: [
    pluginBabel({
      include: /\.(?:jsx|tsx)$/,
    }),
    pluginVue2(),
    pluginVue2Jsx(),
    pluginLess({
      lessLoaderOptions: {
        lessOptions: {
          modifyVars: {
            "primary-color": "#5384FE",
            "error-color": "#ED4D37",
            "btn-danger-bg": "#ED4D37",
            "btn-danger-border": "#ED4D37",
            "success-color": "#48BB78",
            "text-color": "#4A5568",
            "text-color-secondary": "#A0AEC0",
            "btn-default-color": "#718096",
            "border-color-base": "#CBD5E0",
            "input-placeholder-color": "#A0AEC0",
            "tooltip-bg": "rgba(26,32,44,.8)",
            "heading-color": "#4A5568",
            "background-color-base": "#F7F8FA",
          },
          javascriptEnabled: true,
          math: "always",
        },
      },
    }),
    pluginSass({
      sassLoaderOptions: {
        additionalData:
          '@import "@/styles/variables.scss";@import "@/styles/mixin.scss";', // @import "@/styles/localIcon.scss";
      },
    }),
    pluginNodePolyfill(),
  ],
  source: {
    entry: {
      index: "./src/main.js", // 指定入口文件
    },
    define: publicVars,
    include: [
      /node_modules\/transpileDependencies/,
      /node_modules\/@antv/,
      /node_modules\/antv\/x6/,
      /node_modules\/vuex-persist/,
      /node_modules\/gm-crypt/,
      /node_modules\/sm-crypto/,
    ],
    transformImport: [
      { libraryName: "ant-design-vue", libraryDirectory: "es", style: true },
    ],
  },
  html: {
    template: "./public/index.html",
  },
  tools: {
    rspack: {
      resolve: {
        extensions: [".vue", ".js", ".jsx", ".tsx", ".ts", ".json"],
        alias: {
          "@": path.resolve(__dirname, "src"),
        },
      },
    },
    bundlerChain: (chain, { CHAIN_ID }) => {
      chain.module.rule(CHAIN_ID.RULE.SVG).oneOfs.clear();
      chain.module
        .rule(CHAIN_ID.RULE.SVG)
        .use("vue-svg-loader")
        .loader("vue-svg-loader");
    },
  },
  server: {
    port: 8888,
    proxy: {
      "/api": {
        target: process.env.VUE_APP_API_BASE_URL,
        ws: false,
        changeOrigin: true,
        pathRewrite: {
          "^/api": "", // 需要rewrite的,
        },
      },
    },
  },
});
@bosens-China bosens-China added the 工具相关 工程化相关的东西 label Jul 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
工具相关 工程化相关的东西
Projects
None yet
Development

No branches or pull requests

1 participant