We’ve been moving from UmiJS to Vite for over six months now. There were a few issues with the migration, but Vite was good enough to inherit Rollup’s plug-in system and give us a lot of freedom. At present, many people are eager to try Vite, Vite development experience exactly how, today to describe the migration to Vite personal experience.
Conclusion first, Vite is mature enough that it is strongly recommended to migrate from WebPack if necessary.
Why give upUmiJS
At the end of 2019, UmiJS was chosen at a time when Webpack was rampant and various scaffolds were dazzling. It’s sparse, feature-rich, well-documented, and constantly updated. A complete set of solutions, perfect for a team that is mostly not a React technology stack. After constant run-ins, the team quickly adapted to the React development mode, and the development efficiency increased.
There’s always a reason to move. In early 2021, the front-end architecture will need to be adjusted and upgraded to adapt to the company’s development. In the case of growing projects, it takes more than a minute to start a project, and hot updates are almost unusable. Poor machines are configured to start projects for several minutes or run out of memory. This model greatly reduces development efficiency. Customization of internal Webpack plugins, optimization from various perspectives such as multi-core compilation, caching, etc., is still a drop in the bucket. Although UmiJS provided the WebPack5 plug-in, it was not available at the time.
Our main contradiction is:
- Long startup time
- Hot update slow
- Too fat
- Framework BUG fixes are not timely
- Excessive encapsulation and difficulty in customizing plug-ins
- The infinitive is too simple
To meet the requirements of the business, we also need the micro front end. UmiJS also provides a micro front-end plug-in called “Qiankun”. But it still doesn’t solve the fundamental development experience problem. Therefore, in the foundation scaffolding, we seek more control and transparency. (Although UmiJS now supports Module Federation’s package speed solution)
Why is itVite
There are many scaffoldings out there, but few camps, most of which are based on webpack’s top layer packaging. The downside of WebPack is obvious, when cold launching the development server, the packager-based startup must first crawl and build your entire application before it can be served.
In a world where browser ESM support is common, Vite is arguably the next generation of front-end development and build tools. In Vite, HMR is executed on native ESM. When editing a file, HMR keeps it up to date quickly, regardless of the size of the application.
Vite this way in the shadow of our accustomed webpack looks particularly amazing, can say that Vite solves all our pain points perfectly. However, Vite is also relatively new to 2.0, and the number of people who have stepped on it is relatively low. Let’s try Vite.
The early stage of the research
The necessary condition of migration is to find an alternative to the original function, so we used the API and features in UmiJS
UmiJS configuration
- Alias – Configure an alias (for resolve. Alias)
- Base – Sets the route prefix (corresponding to base)
- Define – Used to provide variables available in code (corresponding to define)
- OutputPath – specify the outputPath (corresponding to build.outdir)
- Hash – Configures whether generated files contain the hash suffix (comes with Vite)
- Antd – Integrated ANTD component library (no framework provided, can be referenced in Vite)
- Dva – Integration of DVA data streams (this library has not been updated for a long time and is out of place in the era of hooks. We don’t use a lot, rewriting a file is easy)
- Locale-internationalization plugin for solving i18N problems (you need to implement your own internationalization logic, all based on react-IntL encapsulation, implemented in Vite without pressure)
- FastRefresh – Quick refresh (corresponding to @vitejs/plugin-react-refresh plug-in)
- DynamicImport – Whether load on demand is enabled (load on demand at the routing level, encapsulated with react. lazy in Vite)
- Targets – Minimum compatible browser version required for configuration (for @vitejs/ plugin-Legacy plug-in)
- Theme – configuration less variable (corresponding to the CSS. PreprocessorOptions. Less. ModifyVars configuration)
- LessLoader – Sets the less-loader configuration item (same as the theme configuration)
- IgnoreMomentLocale – ignoreMomentLocale file (alias can be used to set the alias)
- Proxy – Configure the proxy capability (corresponding to server.proxy)
- Externals – set which module can not be packaged (corresponding to the build. RollupOptions. External)
- Copy – Sets the file or folder to copy to the output directory (corresponding rollup-plugin-copy)
- Mock – Configure mock properties (corresponding to viet-plugin-mock)
- ExtraBabelPlugins – Configure additional Babel plugins (corresponding to @rollup/plugin-babel)
Through configuration analysis, virtually all UmiJS configurations can be found in Vite as an alternative. There are some conventions besides configuration
UmiJS @/* path, instead
defineConfig({
resolve: {
alias: {
'@ /': `${path.resolve(process.cwd(), 'src')}/ `,}}});Copy the code
The migration
Review the existing code, identify possible problems and count them. Do your homework. Running is preferred:
Create a project from the official Vite template and install the required dependencies. UmiJS contains the react-Router and antD React-Intl built-in packages. BrowserRouter, ConfigProvider, and LocaleProvider need to be added manually
// App.tsx
export default function App() {
return (
<AppProvider>
<BrowserRouter>
<ConfigProvider locale={currentLocale}>
<LocaleProvider>
<BasicLayout>
<Routes />
</BasicLayout>
</LocaleProvider>
</ConfigProvider>
</BrowserRouter>
</AppProvider>
);
}
Copy the code
Add the corresponding route configuration based on the previous contract route
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'),},// ...
];
Copy the code
The route rendering component implements dynamicImport in UmiJS through react. lazy
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>
);
}
Copy the code
After migrating from the original contract route, the main incompatibility in the project was the members imported from UMI
import { useIntl, history, useLocation, useSelector } from 'umi';
Copy the code
We need to batch change and replace all the variables imported from UMI through the regular replacement of the editor.
- The internationalization of
useIntl
By combining language files andreact-intl
Encapsulate, export a globalformatMessage
methods - Routing related apis
react-router-dom
The export substitution Redux
Relevant, withreact-redux
The export substitution- Find items used in
require
Is replaced by dynamicimport
- Find items used in
process.env.NODE_ENV
And replaced with theimport.meta.env.DEV
Again, becauseVite
There is no longer innode.js
This API
After adding ANTD to the project, it seems that there is a problem with the Vite plug-in corresponding to babel-plugin-import, some styles are missing in dev mode, and it works fine after packaging. It was found that antD was referenced in the component package. In dev mode, the package name was confused with “dependent pre-build”, so the plug-in could not insert antD correctly. To do this, we wrote our own plugin and introduced styles in full in dev mode before prod went plug-in.
With ease, the first page runs successfully.
Because of the need to use a micro front end after the migration, we managed the common configuration through an external plug-in.
export default defineConfig({
server: {
// Configure a different port number for each project
port: 3001,},plugins: [
reactRefresh(),
// Public configuration plug-in
baseConfigPlugin(),
/ / AntD plug-in
antdPlugin(),
],
});
Copy the code
After migration, it is found that Vite needs to configure very little. The extracted common configuration is encapsulated as a Vite plug-in.
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,// Customize the ant prefix
'@ant-prefix': config.antPrefix || 'ant',},plugins: [new LessPluginImportNodeModules()],
javascriptEnabled: true,},},},}; }}; }Copy the code
The whole process of migration is not as complicated as expected, but relatively easy. Almost all commonly used functions Vite has scheme support, which is probably the strength of Vite. In fact, the essence of complexity lies in the business, the complexity of the project is the embodiment of the amount of code, through the IDE search and replacement, quickly completed migration and successful operation.
Now, all of our projects are based on Vite, and we don’t have to wait to catch fish.
Problem/solution
conversionless
file@import '~antd/es/style/themes/default.less'
In the~
The alias error
Configure less plug-in less-plugin-import-node-modules
SyntaxError: The requested module 'xxx' does not provide an export named 'default'
The error we encountered after using the common component as a separate NPM package. The public component package is not compiled by itself, but compiled by the user. So the TS source file is exported. This is normally fine, as Vite will only fail to parse once it encounters CommonJS or UMD packages. Although it is possible to put unparsed packages into optimizedeps.include. But the number of packages can not stand ah, or it TSC translated into JS files and then released.
Packing speed
The first packaging discovery takes more than 70 seconds, so let’s optimize the packaging structure
- through
build.minify
Instead ofesbuild
(the latest versionVite
Is the defaultesbuild
).Esbuild
比terser
20 to 40 times faster, with a compression difference of only 1% to 2%. Reduced to over 30 seconds when turned on babel-plugin-import
In a similarbabel
The plugin is a serious drag, taking 10 seconds out of 40 seconds. We made a plugin through the way of re, perfect solution- Through the analysis of
rollup
对@ant-design/icons
、lodash
The packagetransform
That’s a lot. We added these packages to the plug-in we just made
With a beat, speed up to 16 seconds. Let’s do that.
Why willcacheDir
Put in the root directory
CacheDir serves as the directory for storing cached files. This directory stores pre-packaged dependencies or some cache files generated by Vite, which can be used to improve performance. In some cases, the node_modules package may need to be synchronized, resulting in the modification not taking effect. In this case, use the –force command-line option or manually delete the directory, placing it in the root directory for easy deletion.
Compatibility issues
Vite compatibility can be resolved through the official plugin @vitejs/ plugin-Legacy. We have dropped support for IE 11, unlimited use of ESM in production, envy?
conclusion
If you’re a new project, forget about Webpack, Vite and rollup’s full ecology is enough to support production. If you are an old Webpack project and can’t bear the torture of experience and meet the migration conditions, you might as well try Vite and you will be surprised.
Later, I will share the combination of Vite and my implementation of the micro front end, as well as Vite related plug-ins, please stay tuned.
portal
- Forget useCallback, we have a better way
- How to write more elegant code – JavaScript text
- React Hooks performance optimized for correct posture
Or get your own plug-in that implements the viet-plugin-import regular approach