Code前端首页关于Code前端联系我们

Vue-cli迁移vite2实践总结

terry 2年前 (2023-09-08) 阅读数 200 #Vue

两周前(202.02.17)vite2.0 发布。 vite 2.0作为使用浏览器原生ESM的下一代前端工具,比1.0更加成熟。在此之前,笔者就开始关注这类“新”的前端工具。这次基于 vue-cli(-service) + vue2 的现有项目在利用 vita 2.0 版本的同时成功迁移。

搬家比较顺利,只用了不到半天的时间。然而,在整个迁移过程中也出现了一些小问题。这里总结一下,方便面临类似问题的朋友沟通和参考。

项目背景

在介绍具体的迁移工作之前,我先简单介绍一下项目的情况。目前,该项目上线还不到一年,几乎没有与建设相关的历史债务。该项目包含1897个模块文件(包括node_modules中的模块),使用vue2 + vuex + typescript技术栈,构建工具使用vue-cli(webpack)。是一个比较标准的vue技术栈。该项目作为内部系统,兼容性要求不高,用户基本使用较新的Chrome浏览器(少数使用Safari)。

迁移工作

以下是搬家过程中所做的详细说明。

1。配置文件

首先需要安装vita并创建vita配置文件。

npm i -D vite
 

vue-cli-service 使用vue.config.js作为配置文件;而默认情况下,vite 应该创建 vite.config.ts 作为配置文件。基本配置文件非常简单:

import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    // ...
  ],
})
 

创建这个配置文件,之前的vue.config.js将不再使用。

2。输入和 HTML 文件

您还需要为vita指定一个输入文件。但与 webpack 不同的是,vito 没有指定 js/ts 作为入口点,而是指定实际的 HTML 文件作为入口点。

在webpack中,用户通过将input设置为js input(例如src/app.js)来指定打包js的输入文件,并使用HtmlWebpackPlugin将生成的js文件路径插入到HTML中。 Vite直接使用HTML文件,会解析HTML中的script标签来找到输入的js文件。

因此,我们在 HTML 输入中添加对 script 标签的引用到 js/ts 文件:

<!DOCTYPE html>
<html lang="en">

<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">
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>

<body>
  <noscript>
    We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
  </noscript>
  <div id="app"></div>
+ <script type="module" src="/src/main.ts"></script>
</body>

</html>
 

注意上面的<script type="module" src="/src/main.ts"></script>线。它使用浏览器的本机 ESM 来加载脚本。 /src/main.ts是输入js源码的位置。以vita dev模式启动时,实际上启动了一个类似静态服务器的服务器,为源码目录提供服务,因此不需要像webpack那样复杂的模块打包过程。加载模块依赖项将完全取决于浏览器对 import 语法的处理,因此您可以看到一长串脚本加载瀑布:

vue-cli 迁移 vite2 实践小结vue-cli 迁移 vite2 实践小结

这里还需要注意项目根设置。默认是process.cwd(),也会在项目根目录下搜索index.html。为了方便起见,我将 ./public/index.html 移至 ./index.html

3。使用vue插件

vite 2.0 对 Vue 项目提供了良好的支持,但与 Vue 耦合性不强,因此支持通过插件的方式构建 Vue 技术栈项目。目前vite官网推荐的vue插件2.0(2021.2.28)与vue3 SFC配合使用效果会更好。因此,这里使用了一个专门支持vue2的插件,vite-plugin-vue2,它支持JSX。同时最新版本还支持vite2。

也非常容易使用:

import { defineConfig } from 'vite';
+ import { createVuePlugin } from 'vite-plugin-vue2';

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

4。 Typescript路径映射处理

使用vita构建ts项目时,如果使用了typescript路径映射功能,需要特殊处理,否则会出现模块无法解析(找不到)的错误:

vue-cli 迁移 vite2 实践小结vue-cli 迁移 vite2 实践小结

这里需要使用 vite-tsconfig-paths 插件来解析并替换路径映射。原理比较简单,大致就是vita插件的resolveId hook阶段,利用tsconfig-paths库将路径映射解析为实际映射并返回。有兴趣的可以查看插件的实现,比较短。

特殊使用方法如下:

import { defineConfig } from 'vite';
import { createVuePlugin } from 'vite-plugin-vue2';
+ import tsconfigPaths from 'vite-tsconfig-paths';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    createVuePlugin(),
+   tsconfigPaths(),
  ],
});
 

5。替换 CommonJS

vite采用ESM作为模块化方案,因此不支持使用require导入模块。否则运行时会报Uncaught ReferenceError: require is not defined错误(浏览器不支持CJS,当然不需要方法注入)。

此外,您还可能会遇到ESM和CJS的兼容性问题。当然,这不是vita结构引起的问题,但是应该注意。简单来说,ESM 有默认概念,而 CJS 没有。每个导出的变量都被 CJS 视为 module.exports 对象上的一个属性,而默认的 ESM 导出只是 cjs 上的 module.exports.default 属性。例如,在 typescript 中我们将使用 esModuleInterop 配置来允许 tsc 添加一些兼容性代码来帮助解析导入的模块,而 webpack 中也存在类似的操作。

例如之前的代码:

module.exports = {
    SSO_LOGIN_URL: 'https://xxx.yyy.com',
    SSO_LOGOUT_URL: 'https://xxx.yyy.com/cas/logout',
}
 
const config = require('./config');
 
导出导入时

必须改为ESM,例如:

import config from './config';
 

6。如何使用环境变量

在使用vue-cli(webpack)时,我们经常使用环境变量来在运行时对代码进行评估,例如:

const REPORTER_HOST = process.env.REPORTER_TYPE === 'mock'
  ? 'http://mock-report.xxx.com'
  : 'http://report.xxx.com';
 

vite 仍然支持使用环境变量,但不再提供像process.env这样的访​​问器方法。相反,必须通过 import.meta.env 访问环境变量:

-const REPORTER_HOST = process.env.REPORTER_TYPE === 'mock'
+const REPORTER_HOST = import.meta.env.REPORTER_TYPE === 'mock'
  ? 'http://mock-report.xxx.com'
  : 'http://report.xxx.com';
 

和webpack一样,vite内置了一些可以直接使用的环境变量。

7、import.meta.env

补充:vite提供了它需要的类型定义,可以直接使用vite/client导入,不需要你自己用下面的方法添加。

在打字稿中通过import.meta.env访问环境变量可能会导致错误消息ts:类型“ImportMeta”上不存在属性“env”

vue-cli 迁移 vite2 实践小结vue-cli 迁移 vite2 实践小结

这是因为在当前版本(v4.2.2)中import.meta的定义仍然是一个空接口:

interface ImportMeta {
}
 

或者,我们可以通过接口池功能在项目中进一步定义ImportMeta类型,以扩展对import.meta.env的类型支持。例如,之前通过 vue-cli 创建的 ts 项目会在 src 目录下创建一个文件 vue-shims.d.ts,您可以在这里扩展 env 类型支持:

declare global {
  interface ImportMeta {
    env: Record<string, unknown>;
  }
}
 

这样你就不会收到错误。

vue-cli 迁移 vite2 实践小结vue-cli 迁移 vite2 实践小结

8。 webpack 需要上下文

在webpack中,可以使用require.context方法“动态”解析模块。比较常见的方法是指定一个特定的目录,通过正则匹配加载一些模块,这样以后添加新模块时,就可以达到“动态自动导入”的效果。

比如项目中我们动态链接modules文件夹下的route.ts文件,并在全局vue-router中设置router配置:

const routes = require.context('./modules', true, /([\w\d-]+)\/routes\.ts/)
    .keys()
    .map(id => context(id))
    .map(mod => mod.__esModule ? mod.default : mod)
    .reduce((pre, list) => [...pre, ...list], []);

export default new VueRouter({ routes });
 

文件结构如下:

src/modules
├── admin
│   ├── pages
│   └── routes.ts
├── alert
│   ├── components
│   ├── pages
│   ├── routes.ts
│   ├── store.ts
│   └── utils
├── environment
│   ├── store
│   ├── types
│   └── utils
└── service
    ├── assets
    ├── pages
    ├── routes.ts
    ├── store
    └── types
 

require context 是 webpack 提供的独特模块方法,不是语言标准,所以 require context 不能再在 vita 中使用。但如果完全改成开发者手动导入模块,首先改变现有代码往往会放弃模块导入;其次,放弃这种“灵活”机制,后期的发展模式也会发生一定程度的改变。但幸运的是 vite2.0 允许你以 glob 模式导入模块。该功能实现了上述目标。当然,需要更改一些代码:

const routesModules = import.meta.globEager<{default: unknown[]}>('./modules/**/routes.ts');
const routes = Object
  .keys(routesModules)
  .reduce<any[]>((pre, k) => [...pre, ...routesMod[k].default], []);

export default new VueRouter({ routes });
 

主要是在调整返回值类型的同时将require.context改为import.meta.globEager。当然,你可以在ImportMeta接口中添加一些类型来支持类型:

declare global {
  interface ImportMeta {
    env: Record<string, unknown>;
+   globEager<T = unknown>(globPath: string): Record<string, T>;
  }
}
 

此外,import.meta.globEager 将在构建时执行静态分析,以用静态导入语句替换代码。要支持动态导入,请使用import.meta.glob方法。

9。代理 API

vite2.0 仍然提供本地开发(DEV 模式)的 HTTP 服务器,并且还支持通过 proxy 元素设置代理。后面和webpack一样,使用了http-proxy,所以vue-cli的代理设置可以迁移到vite上:

import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { createVuePlugin } from 'vite-plugin-vue2';
+ import proxy from './src/tangram/proxy-table';

export default defineConfig({
  plugins: [
    tsconfigPaths(),
    createVuePlugin(),
  ],
+ server: {
+   proxy,
+ }
});
 

10。插入 HTML 内容

基于vue-cli,我们可以使用webpack中的HtmlWebpackPlugin来替换HTML中的值,例如<%= htmlWebpackPlugin.options.title %>在编译时将模板变量替换为实际地址值。实现像 vite-plugin-html 这样的功能也很容易。该插件实现了基于ejs的模板变量插入。它通过 transformIndexHtml 钩子接收原始 HTML 字符串,然后通过 ejs 渲染插入的变量并返回它。

以下是迁移后如何使用vite-plugin-html进行配置:

import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { createVuePlugin } from 'vite-plugin-vue2';
+ import { injectHtml } from 'vite-plugin-html';

export default defineConfig({
  plugins: [
    tsconfigPaths(),
    createVuePlugin(),
+   injectHtml({
+     injectData: {
+       title: '用户管理系统',
+     },
    }),
  ],
  server: {
    proxy,
  },
});
 

根据相关需求更改HTML模板变量的编写方式:

<!DOCTYPE html>
<html lang="en">

<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">
- <title><%= htmlWebpackPlugin.options.title %></title>
+ <title><%= title %></title>
</head>

<body>
  <noscript>
-   We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
+   We're sorry but <%= title %> doesn't work properly without JavaScript enabled.
  </noscript>
  <div id="app"></div>
  <script type="module" src="/src/main.ts"></script>
</body>

</html>
 

11。兼容性处理

在项目背景的介绍中,提到了该项目的兼容性要求很低,所以这方面实际上并没有包含在迁移中。

如果你有需要兼容性的项目,当然可以使用@vitejs/plugin-legacy插件。该插件将打包两套代码,一套适用于新浏览器,另一套包含旧浏览器的各种填充和语法兼容性。同时HTML中使用了module/nomodule技术,在新/旧浏览器中进行“条件加载”。

总结

项目包含1897个模块文件(包括node_modules中的模块),迁移前后(不带缓存)的构建时间如下:

vue-cli 邀请2
开发者模式 ~8s ~400ms
制作方法 ~42秒 ~36s

可以看到DEV模式下vite2构建的性能得到了很大的提升。这也是因为在 DEV 模式下它只进行轻型模块文件处理,而不会在生产模式下进行繁重的打包工作。在这种情况下,由于仍然需要构建esbuild和rollup,因此该项目的性能提升并不明显。


以上是作者在迁移vue-cli到vite 2.0时遇到的一些问题。这些都是比较小的点,整体迁移并没有遇到太大的阻碍。整个搬家过程只用了不到半天的时间。当然,这也有赖于近年来JavaScript、HTML等标准化工作,让我们编写的通用代码有一定程度的统一性。这也是这些前端工具给我们“面向未来”编程带来的一大优势。希望这篇文章能给准备转vita 2.0的大家一些参考。

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门