得益于 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/

用 vite 构建项目,同时支持微前端的更多相关文章

  1. vue3.0入门(五):vite构建vue项目

    使用vite构建项目步骤 安装node,cmd输入:node -v验证是否安装成功:一般node安装后会自动安装npm,cmd输入:npm -v验证是否安装成功 选择一个文件夹作为项目文件夹,搜索框输 ...

  2. 极致简洁的微前端框架-京东MicroApp开源了

    前言 MicroApp是一款基于类WebComponent进行渲染的微前端框架,不同于目前流行的开源框架,它从组件化的思维实现微前端,旨在降低上手难度.提升工作效率.它是目前市面上接入微前端成本最低的 ...

  3. 基于 iframe 的微前端框架 —— 擎天

    vivo 互联网前端团队- Jiang Zuohan 一.背景 VAPD是一款专为团队协作办公场景设计的项目管理工具,实践敏捷开发与持续交付,以「项目」为核心,融合需求.任务.缺陷等应用,使用敏捷迭代 ...

  4. 让现有vue前端项目快速支持多语言 - 用.net core程序快速替换中文为资源Key,咱不干体力活

    前言 最近应公司上层要求,需要将现有项目尽快支持多语言,而中文内容可以找专业人员翻译.那么咱们说干就干,首先我们项目的前端是用vue写的spa程序且组件方面用的element ui,那么自然而然想到用 ...

  5. 「微前端实践」使用Vue+qiankun微前端方案重构老项目的本地验证

    10月份换了新的工作,参与完一个月的需求迭代后,接到了项目重构的任务.简单来说,需要在短时间内提出方案设想,同时进行本地验证,最终需要拿出一套技术替换方案来.于是,埋头苦干了一个月,总算干了点成绩出来 ...

  6. 「实践篇」解决微前端 single-spa 项目中 Vue 和 React 路由跳转问题

    前言 本文介绍的是在做微前端 single-spa 项目过程中,遇到的 Vue 子应用和 React 子应用互相跳转路由时遇到的问题. 项目情况:single-spa 项目,基座用的是 React,目 ...

  7. 在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

    在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务 https://procodeguide.com/programming/polly-in-aspnet-core ...

  8. 微前端框架 之 qiankun 从入门到源码分析

    封面 简介 从 single-spa 的缺陷讲起 -> qiankun 是如何从框架层面解决 single-spa 存在的问题 -> qiankun 源码解读,带你全方位刨析 qianku ...

  9. 【前端构建】WebPack实例与前端性能优化

    计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给前端优化工作带来的便利. 壹 | Fisrt 曾几 ...

随机推荐

  1. CF1099A Snowball 题解

    Content 有一个重量为 \(w\) 的雪球从高度为 \(h\) 的位置开始滚落,每秒它的高度会减少 \(1\),同时在高度为 \(i\) 的位置它的重量会增加 \(i\).在雪球滚动的路线上还有 ...

  2. CF102B Sum of Digits 题解

    Content 给定一个数 \(n\),每次操作可以将 \(n\) 变成 \(n\) 各位数之和.问你几次操作之后可以将 \(n\) 变为一位数. 数据范围:\(1\leqslant n\leqsla ...

  3. 如何把myeclipse的工程导入到svn服务器上

    如何把myeclipse的工程导入到svn服务器上,按照如下步骤便可

  4. 【进阶】uniapp复现微信相册功能之【图视频编辑 + 压缩】

    基于uniapp + vue实现微信相册,在实现了微信相册的基础上增加以下功能 1: 图片编辑 2: 视频编辑 3: 文件压缩 技术实现 开发环境:HbuilderX + nodejs 技术框架:un ...

  5. echarts map 地图在react项目中的使用

    需求 展示海南省地图,点击市高亮展示,并在右侧展示对应市的相关数据. 准备工作 Echarts 海南地图json 效果图 代码 index.tsx import React, { useRef, us ...

  6. AcWing03. 完全背包问题

    有\(N\)种物品和一个容量是\(V\)的背包,每种物品都有无限件可用. 第\(i\)种物品的体积是\(v_i\),价值是\(w_i\). 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量, ...

  7. Mybatis-Plus中使用max、sum聚合函数、只查询指定字段、查询语句多个OR处理

    聚合函数查询 可以使用以下方法 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.select(" I ...

  8. 【LeetCode】672. Bulb Switcher II 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...

  9. 【LeetCode】450. Delete Node in a BST 解题报告 (Python&C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 迭代 日期 题目地址:https://leetcode ...

  10. Fence(poj1821)

    Fence Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 4705   Accepted: 1489 Description ...