directory
- Vite vs. Webpack
- Complete migration combat
Vite vs. Webpack
Index comparison
After the actual operation, in the same project, using almost the same Settings, the results are as follows:
indicators \ tool | Vite | Vite(legecy) | Vue-cli + Webpack |
---|---|---|---|
NPM run debug to page available (MS) | 2405 | 4351 | 21418 |
NPM Run Build time (ms) | 19727 | 82277 | 61000 |
The number of packed JS files | 22 | 45 | 46 |
Average JS file size (KB) | 175 | 174 | 88 |
Total JS file size (KB) | 3864 | 7832 | 4080 |
Development differentiation
webpack:
- Translate the package first, then start dev Server
- During hot update, all the dependent modules of the modified module are compiled once
vite:
- For third-party dependencies that do not change, esBuild is pre-built with go, which compiles faster
- For js/ JSX/CSS source code, translation into native ES Module(ESM)
- Take advantage of modern browsers that support ESM and automatically make requests to dependent modules
- Start dev Server directly (no packaging required) and compile the requested modules in real time on demand
- When hot updates are made, only the modified modules are rerequested by the browser
The history of ESM and earlier Javascript modularity is not covered here, but those who are interested can refer to this article; In short, there is nothing new under the sun. The work of setting up local services, static resource packaging and dynamic update done by Webpack or Vite goes back at least ten years and there are various solutions
Build a link
- It is still inefficient to publish an unpackaged ESM in a production environment, considering loading, caching, etc
- Vite leverages mature rollups for tree-shaking, lazy loading, and chunk splitting
Source analyses
After running the vite command:
- > start () / / packages/vite/bin/vite, js - > use cac tool build can handle dev/build/preview command instances of cli - > cli. The parse () / / packages/vite/src/node/cli.tsCopy the code
1. Vite (Dev mode)
- > createServer () / / packages/vite/SRC/node/server/index. The ts - resolveHttpServer () based on HTTP / / create a service - native module CreateWebSocketServer () // Use WebSocket to send a hot update message like the following - chokidar.watch(path.resolve(root)...) / / to monitor source code changes - > handleHMRUpdate () / / processing hot update packages/vite/SRC/node/server/HMR. Ts - updateModules () ` ` ` ` ` ` ws. Send ({type: 'update', updates}) [browser ws://localhost:8080/my-report/] {"type": "update", "updates": [{"type": "js-update", "timestamp": 1646797458716, "path": "/src/app.vue", "acceptedPath": "/src/app.vue?vue&type=template&lang.js" } ] } ``````Copy the code
The part of the browser that responds to HMR:
-> handleMessage() // packages/vite/src/client/client.ts `````` if (update.type === 'js-update') { QueueUpdate (fetchUpdate(update))} else {`````` -> fetchUpdate() `````` // takes advantage of the browser's dynamic import https://github.com/tc39/proposal-dynamic-import / / visible request such as http://... /src/app.vue? import&t=1646797458716&vue&type=template&lang.js const newMod = await import( /* @vite-ignore */ base + path.slice(1) + `? import&t=${timestamp}${query ? `&${query}` : ''}` ) ``````Copy the code
2. vite build
- > build () / / packages/vite/SRC/node/cli. Ts - > doBuild () / / packages/vite/SRC/node/build ts - resolveConfig () / / processing PrepareOutDir () // Clear the package directory, etc. - rollup.rollup()['write']() // Use rollup to complete the actual package and write workCopy the code
The migration practice
Business background and migration principles
Migration Background:
- The speed of webpack development, debugging and packaging for existing projects has been slow
- Looking at background statistics, the browser coverage of the project can support the removal of historical baggage
- The project is representative and already contains components and modules written in TS/JSX/FC, etc
- A gradual move towards the VUE3 technology stack is required
Upgrade Principles:
- The original packaging process of development is painless, and the structure of delivered products is basically unchanged
- Ensure online product safety, set the observation period and compatibility with webpack process rather than direct replacement
- Overwrite the main browser in the background access record and publish the test product and other development links
Main documents involved:
/index.html
— New entrance, oldsrc/index.html
keep/vite.config.js
— Configuration file of the vite tool
Vite version:
- Vite v2.8.2
The node version:
- The node v14.19.0
- Practice shows that V14 can accommodate both the new Vite and the existing WebPack processes
- If Jenkins deployment is involved, you may need to upgrade the node software package
package.json
Rely on
"devDependencies": {
"vite": "^ 2.8.2"."vite-plugin-vue2": "^ 1.9.3." "."vite-plugin-html": "^ 3.0.4"."vite-plugin-time-reporter": "^ 1.0.0"."sass": "^ 1.49.7"."rollup-plugin-copy": "^ 3.4.0"."@vue/compiler-sfc": "^ 3.2.31",},Copy the code
npm scripts
"debug": "vite --mode dev",
"build": "vite build --mode production",
"preview": "vite preview --port 8082",
Copy the code
The previous webpack command is prefixed (e.g., “webpack:build”) and still available
node-sass
The updated version also meets the packaging requirements of WebPack/Vite
- "node - sass" : "^ 4.9.2", + "node - sass" : "^ 6.0.0", - "sass - loader" : "^ 7.0.3", + "sass - loader" : "^ 10.0.0"Copy the code
index.html
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="shortcut icon" href="/src/assets/imgs/report.ico" />
<link rel="stylesheet" href="<%- htmlWebpackPlugin.options.navCss %>" />
<title><%- htmlWebpackPlugin.options.title %></title>
<script
type="text/javascript"
src="<%- htmlWebpackPlugin.options.navJs %>"
></script>
</head>
<body>
<div id="nav"></div>
<div id="app"></div>
<script type="module" src="/src/index.js"></script>
</body>
</html>
Copy the code
- Located in the root directory, the default entry to Vite
- join
type="module"
The script element of the entry file < % = = >
Grammar intoThe < % -- -- >
Basic configuration
Reuse and improve previous packaging and development profiles:
// build/config.js
module.exports = {
title: 'statements'.// Package folder name
base: 'my-report'.// Debug the configuration
debug: {
pubDir: 'dist'.assetsDir: 'assets'.host: 'localhost'.port: 8080.navCss: '/ SRC/assets/common2.0 / SCSS/nav - common. CSS'.navJs: '/ SRC/assets/common2.0 / js/nav - common. Js'.proxy: {
target: 'http://api.foo.com'}},// Production configuration
prod: {
navJs: '/public/v3/js/nav-common.js'.navCss: '/public/v3/css/nav-common.css',}};Copy the code
The basic structure of viet.config.js
import {createVuePlugin} from 'vite-plugin-vue2';
export default ({mode}) => {
const isProduction = mode === 'production';
return defineConfig({
base: ` /${config.base}/ `.logLevel: 'info'.// plugin, compatible with rollup
plugins: [
/ / vue2 and JSX
createVuePlugin({
jsx: true.jsxOptions: {
compositionAPI: true}}),// Package statistics
timeReporter()
],
/ / devServer Settings
server: {},
// Dependency parsing rules etc
resolve: {
alias: {}},// Package directories, footage directories, rollup native options, etc
build: {}}); };Copy the code
The resolve of the migration
Previous configuration in Webpack:
resolve: {
extensions: ['.ts'.'.tsx'.'.vue'.'.js'.'.jsx'.'.json'.'.css'.'.scss'].alias: {
The '@': path.resolve(__dirname, '.. /src'),
assets: path.resolve(__dirname, '.. /src/assets'),
vue$: path.resolve(__dirname, '.. /node_modules'.'vue/dist/vue.esm.js')},symlinks: false
},
Copy the code
Vite:
resolve: {
extensions: ['.ts'.'.tsx'.'.vue'.'.js'.'.jsx'.'.json'.'.css'.'.scss'].alias: [{find: The '@'.replacement: path.resolve(__dirname, 'src')}, {find: 'assets'.replacement: path.resolve(__dirname, 'src'.'assets')}, {find: 'vue$'.replacement: path.resolve(__dirname, 'node_modules'.'vue/dist/vue.esm.js')}, {find: '~@foo/src/styles/common/publicVar'.replacement: 'node_modules/@foo/src/styles/common/_publicVar.scss'
},
{
find: '~@foo/src/styles/mixins/all'.replacement: 'node_modules/@foo/src/styles/mixins/_all.scss'}},Copy the code
The last two configurations are incorrect paths that vite cannot skip and will cause packaging failure. References need to be corrected or special handled here
The build of the migration
Previous configuration in Webpack:
context: path.resolve(__dirname, '.. / '),
mode: isProduction ? 'production' : 'development'.entry: {
index: './src/index.js'
},
output: {
path: path.resolve(__dirname, '.. /dist', config.base),
publicPath,
filename: isProduction ? 'assets/js/[name].[contenthash:8].js' : 'assets/js/[name].[hash:8].js'.chunkFilename: isProduction
? 'assets/js/[name].[contenthash:8].chunk.js'
: 'assets/js/[name].[hash:8].chunk.js'
},
performance: {
maxEntrypointSize: 2000000.maxAssetSize: 1000000
}
Copy the code
Vite:
build: {
outDir: `${pubDir}/${config.base}`,
assetsDir,
rollupOptions: {},chunkSizeWarningLimit: 1000000.cssCodeSplit: true
}
Copy the code
Material copied directly
- The business has part of the dynamic path of the story map reference
<img :src="path">
, path may beassets/imgs/noData.png
Relative path like this - Webpack uses the ‘copy-webpack-plugin’ plugin to copy images to the release directory, which is accessible during debugging
- Vite can also copy the dist directory successfully with the ‘rollup-plugin-copy’ plugin, but the debug process cannot access the dist directory
import copy from 'rollup-plugin-copy'; .// Copy only when packing
plugins: [
isProduction
? copy({
targets: [{src: path.resolve(__dirname, 'src/assets/imgs'),
dest: `${pubDir}/${config.base}/${assetsDir}`}].hook: 'writeBundle'
})
: void 0,].// Special overwrite during debugging
server: {
proxy: {
'/my-report/assets/imgs/': {
target: `http://${host}:${port}/ `.rewrite: path= > path.replace('assets'.'src/assets')}}}Copy the code
Special external references
- Vite requires the ‘viet-plugin-html’ plugin to achieve the same HTML injection effect as the ‘html-webpack-plugin’
- Special references such as ‘/public/v3/ CSS /nav-common.css’ do not comply with vite’s internal retention policy and will be removed
<link>
Tag and convert to JS import, which will cause the page to be inaccessible - Combine custom plug-in to implement hack during packaging and recovery after packaging
import {createHtmlPlugin} from 'vite-plugin-html'; .const indexReplaceHolder = '//fakePrefix'; .plugins: [
createHtmlPlugin({
template: 'index.html'.minify: true.inject: {
data: {
htmlWebpackPlugin: {
options: {
title: config.title,
navCss: isProduction ? indexReplaceHolder + config.prod.navCss : config.debug.navCss,
navJs: isProduction ? indexReplaceHolder + config.prod.navJs : config.debug.navJs
}
}
}
}
}),
(function() {
let viteConfig;
return {
name: 'vite-plugin-fix-index'.configResolved(resolvedConfig) {
viteConfig = resolvedConfig;
},
transformIndexHtml(code) {
if (viteConfig.command === 'build' && isProduction) {
const re = new RegExp(indexReplaceHolder, 'g');
code = code.replace(re, ' ');
}
returncode; }}; }) (),,Copy the code
Compatibility with traditional browsers
- Vite with
@vitejs/plugin-legacy
Plug-ins provide traditional browser compatibility support for packaged files - Legacy has a great impact on the build speed
plugins: [
legacy({
targets: ['> 1%', 'last 2 versions', 'not ie <= 10']
}),
]
Copy the code
The global CSS becomes invalid after Legecy
- Vue 2,
build.cssCodeSplit: false
Adding legecy causes problems such as global style loss (Github.com/vitejs/vite…)
The environment variable
process.env
In vite, change toimport.meta
, and there are differences in use
// src/utils/env.js
export const getEnvMode = () => {
try {
// eslint-disable-next-line
if (typeof process !== 'undefined' && process.env) {
// eslint-disable-next-line
return process.env.NODE_ENV;
}
// eslint-disable-next-line
if (import.meta && import.meta.env) {
return import.meta.env.MODE;
}
} catch (e) {
console.log(e);
}
};
Copy the code
// package.json "devDependencies": {"@open-wc/webpack-import-meta-loader": "^0.4.7",}Copy the code
// webpack -> module -> rules { test: /\.jsx? $/, -loader: 'babel-loader', +loaders: ['babel-loader', {loader: require.resolve('@open-wc/webpack-import-meta-loader')}], include: [path.resolve(__dirname, '../src')] }Copy the code
// jest.config.js -> collectCoverageFrom
[
'!<rootDir>/src/utils/env.js'
]
Copy the code
// __tests__/setup.js jest.mock('.. /src/utils/env.js', () => { return { getEnvMode: () => 'production' }; });Copy the code
require.ensure
- There is no good compatible writing method for the time being, so avoid it as far as possible
new Set()
- If you use ES6 types such as Map/Set and do not use polyfill, you should be aware of its behavior
- For example, the value of a Set may be automatically changed to an array in a webpack/ Babel transliteration, whereas the new process requires manual manipulation
Array.from()
To deal with
conclusion
- Webpack workflows can be almost completely copied by Vite for smooth online upgrades
- Based on the browser visit history evaluation, most projects can enjoy the Vite extreme package benefit
- For special cases that require compatibility with IE 11, consider using Legecy migration mode after full testing
- Note that the rollup package in the production environment is inconsistent with the code in the development environment, which is best verified with Preview
The resources
- cn.vitejs.dev/
- Blog. Bitsrc. IO/vite – is – bet…
- www.cxyzjd.com/article/wei…
- Patak. Dev/vite/build….
- Hacks.mozilla.org/2018/03/es-…
- Mp.weixin.qq.com/s/l849t9xsS…