我们从 UmiJS 迁移到了 Vite

大家好,我是若川。最近组织了源码共读活动,感兴趣的可以点此加我微信ruochuan12 进群参与,每周大家一起学习200行左右的源码,共同进步。已进行三个月了,很多小伙伴表示收获颇丰。

我们从 UmiJS迁移到 Vite 已经上线半年多了。迁移过程中也遇到了不少问题,好在 Vite 足够优秀,继承自 Rollup 的插件系统,使我们有了自由发挥空间。目前很多人对 Vite 跃跃欲试,Vite 开发体验到底怎么样,今天来叙叙迁移到 Vite 的亲身经历。

先说结论,Vite 已经很成熟,强烈建议有条件的可以从 webpack 迁移过来。

为什么要放弃 UmiJS

2019 年底,在 Webpack 横行霸道,各种脚手架琳琅满目的时代选择了 UmiJS。它配置少、功能多、文档齐全、持续更新。一整套的解决方案,非常适合一个大部分非 React 技术栈的团队。经过不断地磨合,团队很快适应了这种 React 开发模式,开发效率也是水涨船高。

凡事总有个原因,为什么要迁移。2021 年初,为适应公司的发展,前端架构也需要做调整与升级。在项目日益增长的情况下,一次项目启动需要耗费一分多钟,热更新也慢得基本无法使用。差点的机器配置启动项目要么好几分钟、要么内存溢出。这种模式极大地降低了开发效率。无论是自定义修改内部 webpack 插件、从各种角度如多核编译、缓存等方式优化,依然是杯水车薪。虽然 UmiJS 提供了 webpack5 插件,不过在当时处于不可用的状态。

我们主要的矛盾是:

  1. 启动时间长

  2. 热更新慢

  3. 太臃肿

  4. 框架 BUG 修复不及时

  5. 过度封装,自定义插件难度大

  6. 约定式功能太单一

适应业务的要求,我们也需要上微前端。UmiJS 也提供了微前端插件 “乾坤”。但依然解决不了根本开发体验问题。因此,在基础脚手架上,我们寻求更多的是可控性及透明性。(尽管UmiJS 现在已经支持Module Federation 的打包提速方案)

为什么是 Vite

市面上的脚手架很多,阵营却很少,大部分是基于 webpack 的上层封装。webpack 的缺点很明显,当冷启动开发服务器时,基于打包器的方式启动必须优先抓取并构建你的整个应用,然后才能提供服务。

025db80adc63551efc8ae9162a42fb45.png

在浏览器 ESM 支持得很普遍得今天,Vite 这种可以称得上是下一代前端开发与构建工具。在 Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,无论应用大小如何,HMR 始终能保持快速更新。

329770296b08f7f5e78440b85be3efd3.png

Vite 这种方式在我们习惯 webpack 的阴影下显得尤为惊艳,可以说 Vite 完美地解决了我们所有的痛点。不过 Vite 也是刚发布 2.0 不久,踩过坑的人也是相当少。我们便试试 Vite

前期调研

迁移的必要条件是在原有的功能下找到替代方案,我们便统计用到了 UmiJS 中的 API 及特性

UmiJS 配置

  • alias - 配置别名(对应 resolve.alias)

  • base - 设置路由前缀(对应 base)

  • define - 用于提供给代码中可用的变量(对应 define)

  • outputPath - 指定输出路径(对应 build.outDir)

  • hash - 配置是否让生成的文件包含 hash 后缀 (Vite 自带)

  • antd - 整合 antd 组件库 (无需框架提供,Vite 中可自己引用)

  • dva - 整合 dva 数据流(此库已经很久没有更新了,在 hooks 时代使用显得格格不入。我们没有大量使用,重写一个文件很轻松)

  • locale - 国际化插件,用于解决 i18n 问题(需要自己实现国际化逻辑,都是基于 react-intl 封装,在 Vite 中实现无压力)

  • fastRefresh - 快速刷新(对应 @vitejs/plugin-react-refresh 插件)

  • dynamicImport - 是否启用按需加载(路由级的按需加载,在 Vite 中用 React.lazy 封装)

  • targets - 配置需要兼容的浏览器最低版本(对应 @vitejs/plugin-legacy 插件)

  • theme - 配置 less 变量(对应 css.preprocessorOptions.less.modifyVars 配置)

  • lessLoader - 设置 less-loader 配置项(与 theme 配置相同)

  • ignoreMomentLocale - 忽略 moment 的 locale 文件(可以通过 alias 设置别名方式解决)

  • proxy - 配置代理能力(对应 server.proxy)

  • externals - 设置哪些模块可以不被打包(对应 build.rollupOptions.external)

  • copy - 设置要复制到输出目录的文件或文件夹(对应 rollup-plugin-copy)

  • mock - 配置 mock 属性(对应 vite-plugin-mock)

  • extraBabelPlugins - 配置额外的 babel 插件(对应 @rollup/plugin-babel)

通过配置分析,基本上所有的 UmiJS 配置都可以在 Vite 中找到替代方案。除了配置还有一些约定

UmiJS 中 @/* 路径,代替方式

defineConfig({resolve: {alias: {'@/': `${path.resolve(process.cwd(), 'src')}/`,},},
});

迁移

Review 现有的代码,找出可能出问题的点并统计。做前期准备。跑起来优先:

从头 Vite 官方模板中创建一个项目,安装所需依赖包。UmiJS 内置封装了 react-routerantd react-intl,这里我们需要手动加上 BrowserRouterConfigProviderLocaleProvider

// App.tsx
export default function App() {return (<AppProvider><BrowserRouter><ConfigProvider locale={currentLocale}><LocaleProvider><BasicLayout><Routes /></BasicLayout></LocaleProvider></ConfigProvider></BrowserRouter></AppProvider>);
}

根据之前约定式路由,添加相应的路由配置

export const basicRoutes = [{path: '/',exact: true,trunk: () => import('@/pages/index'),},{path: '/login',exact: true,trunk: () => import('@/pages/login'),},{path: '/my-app',trunk: () => import('@/pages/my-app'),},// ...
];

路由渲染组件,通过 React.lazy 实现 UmiJS 中的 dynamicImport

const routes = basicRoutes.map(({ trunk, ...config }) => {const Trunk = React.lazy(() => trunk());return {...config,component: (<React.Suspense fallback={<Spinner />}><Trunk /></React.Suspense>),};
});export default function Routes() {return (<Switch>{routes.map((route) => (<Route key={route.key || route.path} path={route.path} exact={route.exact} render={() => route.component} />))}</Switch>);
}

从原先的约定式路由迁移完成,项目中主要不兼容的地方就是从 umi 导入的成员

import { useIntl, history, useLocation, useSelector } from 'umi';

我们需要将所有 umi 中导入的变量,通过编辑器的正则替换批量修改替换。

  • 国际化的 useIntl 通过将语言文件和 react-intl 封装,导出一个全局的 formatMessage 方法

  • 路由相关的 API 用 react-router-dom 导出替换

  • Redux 相关的,用 react-redux 导出替换

  • 查找项目中使用 require 的地方,替换为动态 import

  • 查找项目中使用 process.env.NODE_ENV,替换为 import.meta.env.DEV,因为再 Vite 中不再有 node.js 相关的 API

antd 添加进项目后,发现 babel-plugin-import 对应的 Vite 插件似乎有问题,某些样式在 dev 模式下缺失,打包后正常。排查发现是组件包里面引用了 antd,在 dev 模式下包名被“依赖预构建” 混淆,导致插件无法正确插入 antd 的样式。为此,我们自己写了个插件,在 dev 模式下全量引入样式,prod 才走插件。

很轻松,第一个页面成功运行。

由于迁移之后需要使用微前端,因此我们将公共配置通过外置插件统一管理。

export default defineConfig({server: {// 每个项目配置不同的端口号port: 3001,},plugins: [reactRefresh(),// 公共配置插件baseConfigPlugin(),// AntD 插件antdPlugin(),],
});

迁移后发现 Vite 需要配置的其实很少,抽取的公共配置,封装成 Vite 插件。

import path from 'path';
import LessPluginImportNodeModules from 'less-plugin-import-node-modules';export default function vitePluginBaseConfig(config: CustomConfig): Plugin {return {enforce: 'post',name: 'base-config',config() {return {cacheDir: '.vite',resolve: {alias: {'@/': `${path.resolve(process.cwd(), 'src')}/`,lodash: 'lodash-es','lodash.debounce': 'lodash-es/debounce','lodash.throttle': 'lodash-es/throttle',},},server: {host: '0.0.0.0',},css: {preprocessorOptions: {less: {modifyVars: {'@primary-color': '#f99b0b',...config.theme,// 自定义 ant 前缀'@ant-prefix': config.antPrefix || 'ant',},plugins: [new LessPluginImportNodeModules()],javascriptEnabled: true,},},},};},};
}

迁移的整个过程没有想象中那么繁杂,反而相对容易。几乎常用的功能 Vite 都有方案支持,这也许是 Vite 的厉害之处吧。其实本质上的复杂度在于业务,项目的复杂度就是代码量的体现,通过 IDE 的搜索替换,很快便完成了迁移并成功的运行。

现在,我们所有的项目都基于 Vite,完全没有了等待而摸鱼的烦恼。

问题/解决

转换 less 文件 @import '~antd/es/style/themes/default.less' 中的 ~ 别名报错

配置 less 插件less-plugin-import-node-modules

SyntaxError: The requested module 'xxx' does not provide an export named 'default'

我们将公共组件作为独立的 npm 包之后使用时遇到的错误。本想着公共组件包自己不编译,统一交给使用方编译。所以导出了 TS 源文件。而这种情况常规下没有问题,Vite 一旦遇到 CommonJSUMD 的包才导致无法解析。虽然可以将无法解析的包放入 optimizeDeps.include 。但是架不住包的数量多啊,还是将它 tsc 转译为 JS 文件再发布。

打包提速

首次打包发现需要 70 多秒,我们来优化打包结构

  • 通过 build.minify 改为 esbuild(最新版 Vite 已经默认 esbuild) 。Esbuildterser 快 20-40 倍,压缩率只差 1%-2%。开启后降低到 30 多秒

  • babel-plugin-import 的类似 babel 插件严重拖后腿,总共不到 40 秒的时间,它就要占 10 秒。我们通过正则的方式做了个插件,完美解决

  • 通过分析 rollup@ant-design/iconslodash 包的 transform 数量非常多。我们将这些包也加入到刚刚做的插件中

通过一顿操作下来,提速到 16 秒,先这样吧。

为什么将 cacheDir 放在根目录

cacheDir 作为存储缓存文件的目录。此目录下会存储预打包的依赖项或 vite 生成的某些缓存文件,使用缓存可以提高性能。在某些情况下需要联调 node_modules 里包,从而导致修改后未生效。这时需要使用 --force 命令行选项或手动删除目录,放在根目录便于删除。

兼容性问题

Vite 的兼容性可以通过官方的插件 @vitejs/plugin-legacy 解决。我们已经放弃支持 IE 11,无限制在生产使用 ESM,羡慕吗?

结语

如果你是新的项目,完全不必考虑 Webpack 了,Viterollup 的完全生态足够支撑上生产。如果你是 Webpack 生态老项目,不忍体验上的折磨,满足迁移条件的话,不妨试试 Vite,肯定会带给你惊喜。

后面我会分享 Vite 和自己实现的微前端搭配组合,以及Vite 相关的插件,请持续关注。


最近组建了一个湖南人的前端交流群,如果你是湖南人可以加我微信 ruochuan12 私信 湖南 拉你进群。

推荐阅读

1个月,200+人,一起读了4周源码
我历时3年才写了10余篇源码文章,但收获了100w+阅读

老姚浅谈:怎么学JavaScript?

我在阿里招前端,该怎么帮你(可进面试群)

c6018231e99dddb535d8b6f33bff6ace.gif

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》10余篇,在知乎、掘金收获超百万阅读。
从2014年起,每年都会写一篇年度总结,已经写了7篇,点击查看年度总结。
同时,最近组织了源码共读活动,帮助1000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

f452ebba7ed3a85aeaf1a476f2340dd5.png

识别方二维码加我微信、拉你进源码共读

今日话题

略。分享、收藏、点赞、在看我的文章就是对我最大的支持~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将DataTable的内容以EXCEl的形式导出到本地

1.在搞项目的时候一般会遇到&#xff0c;将GridView或者Repeater的内容以Excel的形式保存到本地&#xff0c;即导出功能。我总结了两个方法。 方法一&#xff1a; 1 DataTable dt query.GetItems().GetDataTable();2 if (dt ! null)3 {4 …

智能家居数据库设计_设计更智能的数据表

智能家居数据库设计重点 (Top highlight)Data tables are hard. There are many different ways to think about them. So, naturally, the first step would be to figure out what your users need.数据表很难。 有许多不同的方式来考虑它们。 因此&#xff0c;自然地&#x…

可能是全网首个前端源码共读活动,诚邀你加入一起学习

大家好&#xff0c;我是若川。众所周知&#xff0c;从8月份开始&#xff0c;我组织了源码共读活动&#xff0c;每周学习200行左右的源码&#xff0c;到现在持续了3个多月&#xff0c;坚持答疑解惑。帮助了不少人&#xff0c;还是挺开心的。另外&#xff0c;涌现了很多优秀的读者…

线段树专辑——pku 3667 Hotel

http://poj.org/problem?id3667 哈哈&#xff0c;经典中的经典题啊。利用线段树求最大连续空闲区间&#xff0c;并返回空闲区间的起点坐标。 View Code 1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 using namespace std; 5 6 …

houseparty不流畅_重新设计Houseparty –用户体验案例研究

houseparty不流畅Houseparty has become very popular during the COVID-19 period because it helps you connect with others in a fun way. The concept is simple, you open the app and jump on a video call with your friends. You can even play online games with the…

你不知道的 Node.js 工具函数

从类型判断说起在 JavaScript 中&#xff0c;进行变量的类型校验是一个非常令人头疼的事&#xff0c;如果只是简单的使用 typeof 会到各种各样的问题。举几个简单的&#x1f330;&#xff1a;console.log(typeof null) // object console.log(typeof new Array) // object cons…

Java应用集群下的定时任务处理方案(mysql)

今天来说一个Java多机部署下定时任务的处理方案。 需求: 有两台服务器同时部署了同一套代码&#xff0c; 代码中写有spring自带的定时任务&#xff0c;但是每次执行定时任务时只需要一台机器去执行。 当拿到这个需求时我脑子中立马出现了两个简单的解决方案&#xff1a; 利用ip…

概念验证_设置成功的UX概念验证

概念验证用户体验/概念证明/第1部分 (USER EXPERIENCE / PROOF OF CONCEPT / PART 1) This is the first article of a four-part series. Please read Part 2 and Part 3.这是由四个部分组成的系列文章的第一篇。 请阅读 第2 部分 和 第3部分 。 How do today’s top UX desi…

从 vue3 和 vite 源码中,我学到了一行代码统一规范团队包管理器的神器

1. 前言大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。已进行四个月了&#xff0c;很多小伙伴表示收获颇丰。想学源码&#xff0c;极力推荐之前我写…

6个高效办公的Excel小技巧,学会让你高效办公

很多人在做Excel表格的时候&#xff0c;会出现下面这种情况&#xff1a;好不容易把内容都输入好了&#xff0c;才发现文字或是数字的排列组合需要重新调整&#xff0c;这个时候头就大了&#xff0c;到底是要一个个复制黏贴&#xff0c;还是要删除后再添加&#xff1f;不管哪种方…

unity 完美像素_像素完美

unity 完美像素从Kidpix到设计系统 (From Kidpix to design systems) Did you ever create stamps in KidPix? Kidpix is bitmap drawing software that’s been around since the nineties, and I remember many happy — more like maddening — hours creating tiny pixela…

整整4个月了,尽全力组织了源码共读活动~

大家好&#xff0c;我是若川。从8月份到现在11月结束了。每周一期&#xff0c;一起读200行左右的源码&#xff0c;撰写辅助文章&#xff0c;截止到现在整整4个月了。由写有《学习源码整体架构系列》20余篇的若川【若川视野公众号号主】倾力组织&#xff0c;召集了各大厂对于源码…

字节内部前端开发手册(完整版)开放下载!

备战2022&#xff0c;准备好了吗&#xff1f;据字节HR部门发布的最新信息&#xff0c;2019年以来字节连续3年扩招&#xff0c;而即将到来的2022年春招前端岗位数不低于3000&#xff0c;虽连年扩招&#xff0c;但是报录比却从2019年的3%下降到今年的1%。BAT等一线大厂同样有类似…

EBS中Java并发程序笔记(1)

在Oracle EBS中的Java并发程序&#xff08;Java Concurrent Program&#xff09;是系统功能中的一个亮点&#xff0c;它的出现使得用户可以在ERP系统中运行自己定义的Java程序。本文为学习笔记&#xff0c;所以不会介绍太多背景知识。 使用Java并发程序的好处&#xff1a; 当遇…

figma设计_5位来自杂乱无章的设计师的Figma技巧

figma设计When starting a design project, a fast pace and multiple design iterations can easily lead to a cluttered mess. Taking the time in the beginning to build good organizational habits will save you time later. You’ll thank your past self when you do…

设计和实现一个 Chrome 插件提升登录效率

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信ruochuan12 进群参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。已进行4个月了&#xff0c;很多小伙伴表示收获颇丰。前言在我们的工作过程中&#xff0c;每当…

qq空间网页设计_网页设计中的负空间

qq空间网页设计重点 (Top highlight)Because screens are limited, web design is also limited. It can be said that in the small box of the screen, each pixel is a piece of real estate.由于屏幕有限&#xff0c;因此网页设计也受到限制。 可以说&#xff0c;在屏幕的小…

时隔一年半,我,一个卑微的前端菜鸡,又来写面经了

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信ruochuan12 进群参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。已进行4个月了&#xff0c;很多小伙伴表示收获颇丰。作者&#xff1a;刮涂层_赢大奖原文地址…

用户体验与可用性测试_可用性作为用户体验的原则

用户体验与可用性测试Every UX Designer has his views and best practices. We all have a guide book created through time and experience. I want to share mine with you.每个UX设计器都有他的观点和最佳实践。 我们都有一本通过时间和经验编写的指南。 我想和你分享我的…

Jenkins插件之Deploy

deploy插件&#xff1a; Deploy Plugindeploy插件支持将War/Jar部署到远程的应用服务器上&#xff0c;例如Tomcat,JBoss,Glassfish。正在寻找或开发.NET web 应用的自动发布插件。如何回滚或重新部署先前的build&#xff1a;0&#xff09; 需要被deploy的job的结果要存档&#…