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.htmlkeep
  • /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
  • jointype="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.pngRelative 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-legacyPlug-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: falseAdding legecy causes problems such as global style loss (Github.com/vitejs/vite…)

The environment variable

  • process.envIn 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 manipulationArray.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…