Vue-cli 迁移 Vite (Vue3 + TSX + Antd-Vue)

·  阅读 1345
Vue-cli 迁移 Vite (Vue3 + TSX + Antd-Vue)

背景

上个月入职新东家当天便被拉着开了一个紧急项目启动会,于是下午和晚上便开始了项目的技术选型和基础搭建。最终选择了使用vue-cli作为脚手架,并使用以Vue3+TypeScript+Antd-Vue为核心的技术栈来完成该项目。

随着项目代码的增多,之前vue-cli中使用webpack进行打包构建的方式变得越来越慢,于是在新一阶段的开发任务推进前,决定将vue-cli迁移到vite

本来以为在网上这么多资料的情况下一两个小时就能搞定的事情,结果却花了半天🤭,所以这里把迁移的过程进行记录。

迁移过程

将整个迁移过程详细拆分后,可包含以下多个步骤:

  1. 引入vite
  2. 迁移index.html
  3. 迁移alias
  4. 迁移Antd
  5. 迁移DevServer
  6. 支持jsx
  7. 支持TypeScript
  8. babel配置
  9. 无用文件清理

其中在迁移aliasAntd以及babel配置过程中遇到了一些坑,可重点关注。

引入vite

使用 npm 安装vite到当前项目中,如下所示:

npm install vite -D
复制代码

安装完成后修改package.json中的命令,使用vite进行开发、打包和预览,如下:

"scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
}
复制代码

迁移index.html

现在执行npm run dev命令,命令行中不会报错,因为vite并没有找到项目的入口,只是启动了一个空的server,通过浏览器访问我们的开发地址也会出现404的报错。

vite是根据index.html来确定项目的入口文件的,且该index.html文件的位置默认是需要在项目的根目录下的,具体原因这里不多介绍,可以查看官方文档的说明index.html与项目根目录

于是我们将/public/index.html/public/favicon.ico文件移动到项目的根目录,并修改index.html文件的内容如下:

<!DOCTYPE html>
<html lang="">
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width,initial-scale=1.0" />
        <link rel="icon" href="/favicon.ico" />
        <title><%- title %></title>
    </head>
    <body>
        <noscript>
            <strong
                >We're sorry but <%- title %> doesn't work properly without
                JavaScript enabled. Please enable it to continue.</strong
            >
        </noscript>
        <div id="app"></div>
        <script type="module" src="/src/main.ts"></script>
    </body>
</html>
复制代码

这里主要有以下两个改动点:

  • 在文件底部加入了<script type="module" src="/src/main.ts"></script>用来引入项目js主文件;
  • 将之前webpack-html-plugin的一些变量引用改成了vite-plugin-html插件的引用方式。

vite-plugin-html需要在vite.config.js中进行配置,我们在项目的根目录创建vite.config.js文件,并通过插件去配置index.html中需要的变量,如下所示:

import { defineConfig } from "vite";
import { minifyHtml, injectHtml } from "vite-plugin-html";

export default defineConfig({
    plugins: [
        minifyHtml(),
        injectHtml({
            data: {
                title: "IDG Data Platform",
            },
        }),
    ]
});
复制代码

完成上述必备工作后启动项目就能够看到具体的报错信息了,开发者便可以根据相应的报错信息去依次解决。后面的步骤便是我在项目迁移过程中解决的数个问题。

迁移alias

在将vue.config.js中的alias配置直接复制到新的配置文件中后,启动项目产生错误如下: image.png

在官方的issue列表中我们找到了该问题的解决方案less import no support webpack alias '~',由于vite默认无法识别出~,因此我们需要手动设置该alias,使用以下配置即可:

export default defineConfig({
    ...
    resolve: {
        // 别名配置
        alias: [
            { find: /^~/, replacement: "" },
            {
                find: /^@\//,
                replacement: resolve("./src/"),
            },
        ],
    },
    ...
});
复制代码

迁移Antd

Antd的迁移主要包含两部分,一是自定义主题中less变量的迁移,第二个则是按需加载的迁移。

在原vue.config.js中,我们使用如下配置来定义less变量:

module.exports = {
    ...
    css: {
        extract: true,
        loaderOptions: {
            less: {
                lessOptions: {
                    modifyVars: {
                        "theme-color": "#2b384f",
                        "layout-header-background": "#2b384f",
                        "layout-sider-background": "#354053",
                        "menu-item-color": "#fff",
                        "menu-highlight-color": "#fff",
                        "menu-bg": "#354053",
                        "menu-item-active-bg": "#2b384f",
                        "font-size-base": "12px"
                    },
                    javascriptEnabled: true
                }
            }
        }
    }
    ...
}
复制代码

将该配置复制到vite.config.js中,发现会出现如下错误,相当于配置未生效,导致无法找到对应的样式变量。 image.png

同样的,我们可以在官方issue找到类似的问题cssPreprocessOptions less modifyVars Invalid modification,在vite 2.x中 CSS 预处理器的配置方式与之前不同,因此我们需要将配置修改为以下即可。

export default defineConfig({
    ...
    css: {
        extract: true,
        preprocessorOptions: {
            less: {
                javascriptEnabled: true,
                modifyVars: {
                    "theme-color": "#2b384f",
                    "layout-header-background": "#2b384f",
                    "layout-sider-background": "#354053",
                    "menu-item-color": "#fff",
                    "menu-highlight-color": "#fff",
                    "menu-bg": "#354053",
                    "menu-item-active-bg": "#2b384f",
                    "font-size-base": "12px",
                },
            },
        },
    },
    ...
});
复制代码

按需加载的迁移比较简单,只需要将原先babel配置中按需加载的插件配置进行迁移即可,该功能依赖babel-plugin-import插件,此插件为babel插件,配置如下。

export default defineConfig({
    ...
    plugins: [
        ...
        babel({
            ...
            plugins: [
                ...
                [
                    "import",
                    {
                        libraryName: "ant-design-vue",
                        libraryDirectory: "es",
                        style: true,
                    },
                ],
            ],
        }),
    ],
    ...
});
复制代码

迁移DevServer

DevServer的迁移相对比较简单,只需要将之前的代理配置复制过来即可。

export default defineConfig({
    ...
    server: {
        proxy: {
            "/api": {
                target: "http://192.168.xxx.xx:8080",
            },
        },
        before(app) {
            app.get("/mock/*", (req, res) => {
                const url =
                    req.url.indexOf("?") > -1 ? req.url.split("?")[0] : req.url;
                const func = require(resolve(`./src/${url}`));
                res.json(func(req, res));
            });
        },
    },
    ...
});
复制代码

支持jsx

由于整个项目是采用了TSX的写法,因此在vite中需要配置支持jsx,否则便会出现Uncaught ReferenceError: React is not defined的报错。

vite官方已经提供了对应的插件,我们只需要安装并引用@vitejs/plugin-vue-jsx插件即可,配置如下:

import vueJsx from "@vitejs/plugin-vue-jsx";

export default defineConfig({
    ...
    plugins: [
        ...
        vueJsx(),
        ...
    ],
    ...
});
复制代码

支持TypeScript

Vite 天然支持引入 .ts 文件。

这是官方文档中关于TypeScript部分的开头语,在vite项目中使用TS语法,修改tsconfig.json配置中compilerOptions.types值为["vite/client"]便可以支持TS开发。

babel配置

并不是每一个项目的迁移都需要进行额外的babel配置,此项目中使用了decorators以及Optional Chaining的语法,同时使用了Antd按需加载的插件,所以需要进行一些基础的babel配置。

vite的内部使用rollup进行打包,所以支持通过@rollup/plugin-babel插件来配置babel

其他依赖及插件可通过以下两个命令分别安装:

npm i @babel/runtime core-js -S
npm i @babel/plugin-proposal-optional-chaining @babel/plugin-transform-runtime @babel/preset-env @rollup/plugin-babel babel-plugin-import -D
复制代码

这里的配置都比较常用,就不一一展开,整体配置如下所示:

import babel from "@rollup/plugin-babel";

export default defineConfig({
    ...
    plugins: [
        ...
        babel({
            babelHelpers: "runtime",
            presets: [
                [
                    "@babel/preset-env",
                    {
                        modules: false,
                    },
                ],
            ],
            extensions: [".ts", ".tsx", ".js"],
            exclude: "node_modules/**",
            plugins: [
                "@babel/plugin-transform-runtime",
                [
                    "import",
                    {
                        libraryName: "ant-design-vue",
                        libraryDirectory: "es",
                        style: true,
                    },
                ],
                ["@babel/plugin-proposal-optional-chaining"],
            ],
        }),
        ...
    ],
    ...
});
复制代码

无用文件清理

在上述配置均完成之后,我们已经可以正常启动并构建我们的项目啦,为了保持工作区干净,删除掉无用的.babelrcvue.config.js,整个项目的迁移过程便完成了🎉🎉🎉!

附录

整个项目迁移完成之后的目录结构和配置文件如下。

目录结构

├── .editorconfig

├── .env.dev

├── .eslintignore

├── .git

├── .gitignore

├── .gitlab-ci.yml

├── .prettierrc

├── README.md

├── favicon.ico

├── index.html

├── node_modules/

├── package-lock.json

├── package.json

├── public/

├── scripts/

├── src/

├── tsconfig.json

└── vite.config.js
复制代码

vite.config.js

import { defineConfig } from "vite";
import { minifyHtml, injectHtml } from "vite-plugin-html";
import babel from "@rollup/plugin-babel";
import vueJsx from "@vitejs/plugin-vue-jsx";

const path = require("path");

function resolve(dir) {
    return path.join(__dirname, dir);
}

export default defineConfig({
    css: {
        extract: true,
        preprocessorOptions: {
            less: {
                javascriptEnabled: true,
                modifyVars: {
                    "theme-color": "#2b384f",
                    "layout-header-background": "#2b384f",
                    "layout-sider-background": "#354053",
                    "menu-item-color": "#fff",
                    "menu-highlight-color": "#fff",
                    "menu-bg": "#354053",
                    "menu-item-active-bg": "#2b384f",
                    "font-size-base": "12px",
                },
            },
        },
    },
    resolve: {
        // 别名配置
        alias: [
            { find: /^~/, replacement: "" },
            {
                find: /^@\//,
                replacement: resolve("./src/"),
            },
        ],
    },
    server: {
        proxy: {
            "/api": {
                target: "http://192.168.xxx.xx:8080",
            },
        },
        before(app) {
            app.get("/mock/*", (req, res) => {
                const url =
                    req.url.indexOf("?") > -1 ? req.url.split("?")[0] : req.url;
                const func = require(resolve(`./src/${url}`));
                res.json(func(req, res));
            });
        },
    },
    plugins: [
        minifyHtml(),
        injectHtml({
            data: {
                title: "Platform",
            },
        }),
        vueJsx(),
        babel({
            babelHelpers: "runtime",
            presets: [
                [
                    "@babel/preset-env",
                    {
                        modules: false,
                    },
                ],
            ],
            extensions: [".ts", ".tsx", ".js"],
            exclude: "node_modules/**",
            plugins: [
                "@babel/plugin-transform-runtime",
                [
                    "import",
                    {
                        libraryName: "ant-design-vue",
                        libraryDirectory: "es",
                        style: true,
                    },
                ],
                ["@babel/plugin-proposal-optional-chaining"],
            ],
        }),
    ],
});

复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改