得益于 esbuild 的超高性能,vite 在诞生之初就备受关注,且一直保持着活跃的开发迭代。截至目前,vite 已经迭代到了 2.7.10 版本,各方面也基本具备了在生产使用的条件。这段时间,我在项目中尝试了使用 vite 进行打包构建,本文就是这次构建的过程记录。

基础配置

首先使用vite 官方脚手架生成项目。

yarn create vite vite-demo --template react-ts

上面这行命令使用 react-ts 模板创建了一个叫 vite-demo 的项目。由于我在的团队日常使用 react 和 typescript 开发居多,因此选择了 react-ts 这个模板,vite 官方支持的模板还有很多,可以在 create-vite 中查看。

项目初始化完成以后,目录结构如下:

.
|____index.html
|____.gitignore
|____package.json
|____tsconfig.json
|____vite.config.ts
|____src
| |____App.tsx
| |____main.tsx
| |____App.css
| |____index.css
| |____vite-env.d.ts
| |____logo.svg
| |____favicon.svg

其中 vite.config.ts 内容如下:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' // https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
})

可以看出,vite 官方已经做了比较完善的封装,相较于之前版本,开发体验提升了很多。

按照指示安装完依赖,启动应用以后,速度确实很快。现在我们来做一些基本改造。

我通常使用 less 来写样式,vite 已经做了很好的支持,在安装完依赖以后,只需要直接在代码中引用 xxx.less 即可。对于一个久经考验的开发者来说,样式还是要引入作用域的,通常使用 css modules。

安装 less 预处理器,

yarn add --dev less

然后修改 vite.config.ts 文件,添加 css modules 配置:

export default defineConfig({
...
css: {
modules: {
localsConvention: 'camelCaseOnly', // 我们使用驼峰形式
},
},
...
})

添加完配置以后,只要将原来的 xxx.less 改成 xxx.module.less 即可,这点与 create-react-app 是一样的。

这里推荐一个 vscode 插件 clinyong.vscode-css-modules 可以实现编码时样式类名的智能提示,同时点击样式类名可以跳转到样式定义的地方,非常好用。如果在编写样式时使用的是中划线形式的命名方式,比如 .xxx-container,那么需要额外配置这个 vscode 插件,如下:

{
"cssModules.camelCase": true
}

这样可以实现编写样式时使用中划线形式,在代码中使用的还是驼峰式的。

由于我开发的是一个中后台项目,使用了 antd 和 lodash,大家都知道,这两个是按需加载大户,以前我们使用 babel-plugin-import 来处理,vite 生态里也有很多类似的方案。我选用了 vite-plugin-imp 这个插件,修改 vite.config.ts 如下:

import vitePluginImp from 'vite-plugin-imp';

export default defineConfig({
...
plugins: [
...
vitePluginImp({
libList: [
{
libName: 'lodash',
libDirectory: '',
camel2DashComponentName: false,
},
{
libName: 'antd',
style(name) {
// use less
return `antd/es/${name}/style/index.js`;
},
},
],
}),
],
css: {
...
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
},
});

antd 已经默认支持了 Tree Shaking,上面的配置最终只会处理样式的按需加载。lodash 不支持 Tree Shaking,我们也可以使用 ESM 版本 lodash-es,这样就可以不使用 vite-plugin-imp 了,配置如下:

export default defineConfig({
resolve: {
alias: [{
find: /^lodash$/,
replacement: 'lodash-es',
}],
},
});

通常,我们在开发前端项目时,需要一些代理来调用后端 API 接口,vite 配置如下:

export default defineConfig({
...
server: {
proxy: {
'/api_path/': {
target: 'http://xxx.server.domain.com/',
changeOrigin: true,
},
},
},
});

代理底层都是基于 http-proxy 实现,这里不做过多说明了。

现在可以愉快的开发代码了。

支持微前端构建

因为我们的中后台应用是使用微前端(qiankun)来管理的,上面的配置,打包完成后不能被 qiankun 识别,主要原因可以看看这里,我们需要做一些额外处理。

我们知道,使用 webpack 构建微前端是,需要添加如下三个配置项:

{
output: {
libraryTarget: 'umd',
library: `${APP_NAME}-[name]`,
jsonpFunction: `webpackJsonp_${APP_NAME}`,
}
}

在 vite 中,可以直接通过设置 build.rollupOptions.formatumd 来设置 UMD 规范,但是实际构建结果却不能被 qiankun 识别,猜想是可能跟 vite 使用 html entry 有关系。

换一个思路,我们把当前整个应用当做一个 library 来构建,输出为 UMD 规范,然后手动写入一个 html 文件,加载这个输出的 JS。

修改配置如下:

export default defineConfig({
...
build: {
lib: {
name,
entry: path.resolve(__dirname, 'src/index.tsx'),
formats: ['umd'],
},
},
...
})

配置完成之后,执行 yarn build 提示如下错误:

UMD and IIFE output formats are not supported for code-splitting builds.

因为我们的应用中有路由,使用了按需加载。我们将 rollup 的 inlineDynamicImports 配置打开:

export default defineConfig({
...
build: {
rollupOptions: {
output: {
inlineDynamicImports: true,
},
},
},
...
})

这样,构建完成之后,dist 目录下有两个文件 style.cssxxx.umd.js

现在我们要生成 index.html 了。

因为 vite 在开发态直接使用 ES Modules,是不打包的,因此生成开发态的 index.html 和生产的 index.html 是不同的。

我们修改项目根目录下的 index.html 为:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<!-- style placeholder -->
</head>
<body>
<div id="root"></div>
<!-- script placeholder -->
</body>
</html>

注意当中的两行注释,我们会在开发态和生产构建做不同的处理。

vite 插件 API 中有一个 transformindexhtml 可以定制开发态的 html 内容,因此,我们开发态的配置如下:

// https://vitejs.dev/config/
export default defineConfig({
...
plugins: [
...
{
name: 'dev html',
apply: 'serve',
transformIndexHtml(indexHtml: string) {
return indexHtml
.replace('<!-- style placeholder -->', '')
.replace('<!-- script placeholder -->', '<script type="module" src="/src/index.tsx"></script>');
},
},
...
],
});

生产构建需要借助于 @rollup/plugin-html 这个插件来实现定制 html 内容。

import html from '@rollup/plugin-html';
import fs from 'fs'; const entryHtml = fs.readFileSync('./index.html', { encoding: 'utf-8' }); export default defineConfig({
...
plugins: [
...
{
name: 'build html',
apply: 'build',
...html({
template: () => {
return entryHtml
.replace(
'<!-- style placeholder -->',
'<link rel="stylesheet" type="text/css" href="style.css" />',
)
.replace(
'<!-- script placeholder -->',
`<script type="text/javascript" src="${name}.umd.js"></script>`,
);
},
}),
},
...
],
});

通过上面的配置,再次构建,qiankun 可以加载这个子应用了。

其他说明

1. 老旧浏览器的支持

由于我这次的项目是中后台项目,对老旧浏览器的支持诉求不强烈,就没有在项目中做处理。其实 vite 官方也是给了解决方案的,就是 @vitejs/plugin-legacy 这个插件。

原理也非常简单,就是通过 <script nomodule> 来实现在不支持 ES Modules 的浏览器执行相关脚本,同时使用 SystemJS 来加载模块。

2. 关于 TypeScript 的说明

脚手架初始化完成以后就可以用 TypeScript 开发,这里格外说明一点,就是需要开启编译器选项 isolatedModules:true,因为 vite 使用 esbuild 处理 ts 文件,只将 ts转换成 js 而不做类型检查(依赖编辑器处理类型检查,比如 vscode)。因此,当遇到一些纯类型的导入导出时,会出错,需要开启 isolatedModules:true 来避免这个问题。如果因为一些原因无法开启这个选项,则可以使用 rollup-plugin-friendly-type-imports 这个包来处理,这个包的 README 里也说明了为什么会有这样的问题。

3. 对接 CDN

基于上面的配置构建出来的结果,浏览器在加载资源的时候,都是使用的根路径(/)加载,如果使用 CDN 的话会出现资源加载 404 的问题。

我们可以配置 base 来设置基础路径,类似于 webpack 的 PUBLIC_PATH

export default defineConfig({
base: '/some/public/path',
})

4. 构建出错

4.1 找不到包

报错信息为:

[plugin: vite:dep-scan] Failed to resolve entry for package "xxx"

通常是依赖包未在 package.json 正确配置 main、module 等字段,导致 vite 无法找到包的入口。

可以设置通过设置别名的方式,将其映射到正确的文件上。

export default defineConfig({
resolve: {
alias: [{
find: /^SOME_PACKAGE_NAME$/,
replacement: 'SOME_PACKAGE_NAME/dist/xxx.es.js',
}],
},
});

4.2 请求超时

报错信息为:

net::ERR_ABORTED 408 (Request Timeout)

启动开发服务器后,浏览器出现请求超时错误。是因为 vite 检测到对依赖包的请求,且该依赖尚未被 vite 处理过,这时候会会触发预构建,导致请求超时以及页面重载。

我们可以多刷新几次等 vite 完成预构建,也可以将依赖加入 optimizeDeps.include 来提前处理。

4.3 导入模块出错

报错信息为:

Internal server error: Failed to resolve import "./chunk-7L3SPMWF.js" from "node_modules/.vite/antd.js?v=7bec0e27". Does the file exist?

可能是因为一些依赖包输出的格式 vite 还不支持,可以看看这个 issue

这个错误只在开发服务器运行处理过程中存在,待页面正常展示后就不出现了,忽略这个错误之后,目前看也没产生什么影响。

小结

总体来说,vite 已经基本具备了生产使用的条件。如果是常规的应用开发,vite 的配置非常简单,可以说是开箱即用。如果需要添加额外的配置也非常方便。

目前比较大的问题是周边生态还不是特别成熟,很多已经成熟的包对于 vite(ES Modules)的支持比较弱。同时,如果团队内基建氛围比较浓厚的话,自己开发的工具包也要考虑这方面的问题。

常见面试知识点、技术方案分析、教程,都可以扫码关注公众号“众里千寻”获取,或者来这里 https://everfind.github.io/posts/

最新文章

  1. 读《单页web应用》-回顾作用域
  2. Java批处理ExecutorService/CompletionService
  3. Des加解密算法
  4. 25个最佳的 WordPress Gallery 画廊插件
  5. XML学习笔记(二)-- DTD格式规范
  6. latex figure \label 放在\caption 后
  7. IronPython调用C# DLL函数方法
  8. Hadoop命令摘录
  9. Nginx教程(三) Nginx日志管理
  10. MySQL数据库优化的八种方式
  11. matlab练习程序(水波特效)
  12. html实现调用jar包
  13. Lucene.Net如何实现搜索结果分类统计功能
  14. (4.24)sql server变量中set与select的区别
  15. duilib踩坑记录
  16. Chunk
  17. nextcloud私有云盘的部署
  18. ubuntu16.04-caffe安装过程详解-草稿
  19. Ubuntu环境下安装CUDA9.0
  20. [转帖]学习一下centos7 新地方

热门文章

  1. WPF将窗口置于桌面下方(可用于动态桌面)
  2. [BSidesCF 2019]Runit(细心)
  3. 尚硅谷SSM-CRUD实战Demo
  4. python开发环境软件包安装相关 failed with error code 1 in /tmp/pip-build-vn_f_e1n/psutil/
  5. 【剑指Offer】顺时针打印矩阵 解题报告(Python)
  6. C++多线程并发---异步编程
  7. 3 - 基于ELK的ElasticSearch 7.8.x技术整理 - 高级篇( 偏理论 )
  8. 「物流跟踪管理系统」 · Java Swing + MySQL JDBC开发,美和易思结业考试机试试题
  9. 使用 Eclipse 可视化插件 windowbuilder 进行Java GUI开发(插件安装的两种方法)
  10. Java中两个或多个byte数组合并及int类型转数组