- Introduction
- Significant Reduction in package.json Size
- Adjustments to the Entry index.html File
- Support for React and TypeScript
- Support for Less and Module-CSS
- Basic Server and Proxy Configuration for the Project
- Configuration of Absolute Path Alias
- Importing Static Files
- Configuration of Environment Variables
- Manual Chunk Packaging Optimization Logic
- Strange Pitfalls
- Complete Vite Configuration
- Conclusion
Introduction#
Vite is a brand new front-end build tool. Adding the word meta
before build tool in this metaverse wave seems to endow it with some mysterious power.
With React introducing features like Server-Side Streaming and React Server Components in version 18, the creator of the Vue framework, Evan You, who is also the author of Vite, jokingly referred to React as the Meta Framework.
Of course, calling Vite a meta build tool also has its legitimate meaning.
- The word meta comes from Greek, meaning
beyond
, and the emergence of Vite signifiessurpassing the current situation where all previous projects are built on webpack
. - In terms like "neuron" in Chinese, meta also means
the smallest functional unit
, and one of Vite's biggest features compared to webpack's build solution is that during the development phase, it does not need to bundle all source code into a single bundle for the browser to run. Instead, it directly hands over the code to modern browsers based on esm, where anesm
can be understood as that smallest functional unit.
To get to the point, the idea of transforming the project to be driven by Vite arose because with an increasing amount of project code and files, it became evident that the speed of cold starts and hot reloads in the webpack dev environment was slowing down. Additionally, I had heard about Vite's smooth and rapid development experience
, which made me eager to try it out.
Next, we will mainly discuss the process of transforming an existing webpack-based react + antd project into one driven by Vite
.
Significant Reduction in package.json Size#
Adjustments to the Entry index.html File#
index.html is at the outermost layer of the project and not in the public folder
webpack
<!DOCTYPE html>
<html>
<head>
<link rel="icon" href="%PUBLIC_URL%/logo.ico" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" style="height: 100%"></div>
</body>
</html>
vite
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="icon" href="/src/assets/logo.ico" />
</head>
<body>
<div id="root"></div>
<!-- Vite-specific esm entry file -->
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Support for React and TypeScript#
The official plugin @vitejs/plugin-react
provides complete support for React.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://cn.vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
Vite has built-in esbuild for compiling TypeScript files, and you only need to provide your own tsconfig file in the project.
Support for Less and Module-CSS#
Vite has built-in support for Less and Module-CSS. You just need to install the Less preprocessor dependency. Since antd's Less uses Less's function features, you need to enable the javascriptEnabled
configuration in preprocessorOptions
.
export default defineConfig({
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
},
})
Basic Server and Proxy Configuration for the Project#
export default defineConfig({
server: {
port: 3000,
host: 'localhost',
open: true,
https: true,
hmr: {
host: 'localhost',
},
proxy: {
'/api': "https://tartget-server",
'/special/api': {
target: "https://special-tartget-server",
changeOrigin: true,
rewrite: path => path.replace(/^\/special/, ''),
},
},
},
});
Configuration of Absolute Path Alias#
- In the project, we generally use absolute paths to import modules, using
@
to replace the path of thesrc
folder under the root directory. - Since some dependencies in
node_modules
contain@
, such imports should not be matched. - Some older Less imports use
~
to indicate absolute imports, which need special handling.
export default defineConfig({
resolve: {
alias: [
{
find: /^@\//,
replacement: `${path.resolve(__dirname, 'src')}${path.sep}`,
},
{ find: /^~/, replacement: '' },
],
},
})
Importing Static Files#
Vite supports various methods for importing static assets. During the migration process, we mainly handled one case where static resources needed to be referenced based on variable concatenation. Since we cannot use the require
method outside of the webpack environment, we need to use Vite's native esm feature import.meta.url.
// Dynamically concatenate the path to the flag resource based on short
const getFlag = (short: string) => {
return new URL(`./flags/${short.toLowerCase()}.png`, import.meta.url).href
}
Configuration of Environment Variables#
Introduce dotenv-related dependencies and add dotenv to the startup command.
{
"scripts": {
"dev": "dotenv -e .env.dev vite",
"build": "tsc && dotenv -e .env.dev vite build",
"serve": "vite preview"
},
"devDependencies": {
"dotenv": "^8.2.0",
"dotenv-cli": "^4.0.0",
"dotenv-expand": "^5.1.0"
}
}
In Vite, you need to follow the convention of prefixing with VITE_
.
VITE_VAR=SOME_VALUE
Define a TypeScript file for static verification in src
called env.d.ts
.
interface ImportMetaEnv extends Readonly<Record<string, string>> {
readonly VITE_VAR: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
Manual Chunk Packaging Optimization Logic#
Using lazy loading with React-Router routes in the project generally allows for automatic code splitting, which can solve most scenarios. If code splitting is still unreasonable, you can add manual chunk generation logic in the configuration.
import path from 'path'
import { dependencies } from './package.json'
function renderChunks(deps) {
let chunks = {}
Object.keys(deps).forEach((key) => {
if (key.includes('@types')) return
if (['react', 'react-router-dom', 'react-dom'].includes(key)) return
chunks[key] = [key]
})
return chunks
}
export default defineConfig({
// The logic under build will only run during production packaging
build: {
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-router-dom', 'react-dom'],
...renderChunks(dependencies),
},
},
},
},
})
Strange Pitfalls#
- The moment internationalization package needs to be imported in a different way to take effect.
// webpack
import 'moment/locale/zh-cn'
// vite
import 'moment/dist/locale/zh-cn'
- Some dependency packages that work fine in the webpack production environment may throw errors in Vite. The
react-response
package will throw an error aboutglobal
not being defined in production, so you need to add fault tolerance logic in the script tag.
<script>
if (global === undefined) {
var global = window
}
</script>
Complete Vite Configuration#
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { dependencies } from './package.json';
function renderChunks(deps) {
let chunks = {};
Object.keys(deps).forEach(key => {
if (key.includes('@types')) return;
if (['react', 'react-router-dom', 'react-dom'].includes(key)) return;
chunks[key] = [key];
});
return chunks;
}
export default defineConfig({
plugins: [
react(),
],
css: {
preprocessorOptions: {
less: {
javascriptEnabled: true,
},
},
},
server: {
port: 3000,
host: 'localhost',
open: true,
https: true,
hmr: {
host: 'localhost',
},
proxy: {
'/api': "https://tartget-server",
'/special/api': {
target: "https://special-tartget-server",
changeOrigin: true,
rewrite: path => path.replace(/^\/special/, ''),
},
},
},
resolve: {
alias: [
{
find: /^@\//,
replacement: `${path.resolve(__dirname, 'src')}${path.sep}`,
},
{ find: /^~/, replacement: '' },
],
},
build: {
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-router-dom', 'react-dom'],
...renderChunks(dependencies),
},
},
},
},
});
Conclusion#
-
The migrated project runs without issues on the latest Chrome, but support for some older browsers still needs to be verified.
(Vite itself provides support for older browsers through plugins) -
Due to the differences in the construction methods between the development and production environments in Vite, there may be some confusion regarding the final online deployment.
-
Additionally, for the migrated project, the way styles are referenced from antd involves fully importing the source code of Less and adding custom Less theme variable overrides.
Since browsers do not support running Less files, Vite still needs to fully compile Less files before the project can start. When there are many Less files, the compilation speed can be slow.
In the future, we can configure on-demand imports for Less and inject plugins for Less theme variables.