SoFunction
Updated on 2025-04-05

Learn the principles of Vite

1. Overview

Vite is a lighter and faster web application development tool for modern browsers. The underlying layer is based on the ECMAScript standard native module system ES Module implementation. He appeared to solve the problem that webpack cold start time is too long andWebpack HMRProblems such as slow thermal update reaction speed.

By default, the project created by Vite is an ordinary Vue3 application, which has many fewer configuration files and dependencies than those created based on Vue-cli.

ViteThe development dependencies required for the created project are very small, onlyViteand@vue/compiler-sfc. Vite is a running tool here.compiler-sfcThis is to compile single-file components ending with .vue. When creating a project, you can also support the use of other frameworks such as React. After the project is created, it can be started and packaged through two commands.

# Turn on the servervite serve
# Packvite build

It's precisely becauseViteThe started web service does not require compilation and packaging, so the startup speed is particularly fast. Most of the code run in the debugging stage is the code you write in the editor, which is indeed much faster than the webpack's compile and rendering. Of course, the production environment still needs to be packaged. After all, the latest ES specifications we use are not supported in the browser. Vite's packaging process is similar to webpack and will compile and package all files together. For code cutting, Vite uses native dynamic import to implement it, so the packaging results can only support modern browsers. If you need to be compatible with old browsers, you can import them.Polyfill

In addition to using Webpack to package, the browser environment does not support modularity and new syntax, the module files will generate a large number of http requests. If you develop in a modular way, a page will have more than ten or even dozens of modules, and many times there will be several kb files. It is obviously unreasonable to open a page to load dozens of js resources.

  • Projects created by Vite almost do not require additional configuration. By default, they already support TS, Less, Sass, Stylus, and postcss, but they need to install the corresponding compilers separately. At the same time, they also support jsx and Web Assembly by default.
  • The benefit of Vite is to improve the developer's experience during the development process. The web development server does not need to wait to start immediately. The module hot update is almost real-time. The required files will be compiled on demand to avoid uncompiled files. And use the configuration that avoids loader and plugins out of the box.
  • The core features of Vite include turning on a static web server, being able to compile single file components and providing HMR functions. When starting vite, the current project directory will be used as the root directory of the static server. The static server will intercept some requests, compile it in real time when a single file is requested, and handle modules that other browsers cannot recognize, and implement hmr through websocket.

2. Implement a static test server

First, implement a command line tool that can enable static web servers. Koa is used internally to implement static servers. (PS: The node command line tool can view my previous articles, I won’t introduce them here, just post the code).

npm init
npm install koa koa-send -D

The entry file of the tool bin is set to local

#!/usr/bin/env node

const Koa = require('koa')
const send = require('koa-send')

const app = new Koa()

// Turn on the static file server(async (ctx, next) => {
    // Load static files    await send(ctx, , { root: (), index: ''})
    await next()
})

(5000)

('The server has been started http://localhost:5000')

This way I wrote anodeA tool for static servers.

3. Handle third-party modules

Our approach is to use third-party modules in the code (node_modules) You can modify the path of the third-party module to give it an identifier, and then get this identifier in the server to process the module.

First, you need to modify the path of the third-party module, and a new middleware is needed to implement it. To determine whether the file currently returned to the browser is JavaScript, you only need to look at the content-type in the response header. in the case ofjavascriptYou need to find the module path introduced in this file.It is the content file returned to the browser. The data here is a stream that needs to be converted into a string for processing.

const stream2string = (stream) => {
    return new Promise((resolve, reject) => {
        const chunks = [];
        ('data', chunk => {(chunk)})
        ('end', () => { resolve((chunks).toString('utf-8'))})
        ('error', reject)
    })
}

// Modify the path of the third-party module(async (ctx, next) => {
    if ( === 'application/javascript') {
        const contents = await stream2string();
        // Modify the imported path in the body, reassign the value to the body and return it to the browser        // import vue from 'vue', match from 'modify to from '@modules/         = (/(from\s+['"])(?![\.\/])/g, '$1/@modules/');
    }
})

Then start loading the third-party module. Here you also need a middleware to determine whether the request path is beginning with @module modified. If so, go to node_modules to load the corresponding module and return it to the browser. This middleware should be placed before the static server.

// Load third-party modules(async (ctx, next) => {
    if (('/@modules/')) {
        // Intercept the module name        const moduleName = (10);
    }
})

After getting the module name, you need to obtain the module's entry file. What you want to obtain here is the entry file of the ES Module module. You need to find the module's entry file first.Then get thisThe value of the module field in it is the entry file.

// Find the module pathconst pkgPath = ((), 'node_modules', moduleName, '');
const pkg = require(pkgPath);
// To reassign a value, you need to reset an existing path because the previous path does not exist. = ('/node_modules', moduleName, );
// Execute the next middlewareawiat next();

In this way, although the browser requests to come in, the path path is modified tonode_modulesThe path in this way will go when loadingnode_modulesGet the file in and respond to the loaded content to the browser.

Loading third-party modules:

(async (ctx, next) => {
    if (('/@modules/')) {
        // Intercept the module name        const moduleName = (10);
        // Find the module path        const pkgPath = ((), 'node_modules', moduleName, '');
        const pkg = require(pkgPath);
        // To reassign a value, you need to reset an existing path because the previous path does not exist.         = ('/node_modules', moduleName, );
        // Execute the next middleware        awiat next();
    }
})

4. Single file component processing

I have said before that the browser cannot handle .vue resources. The browser can only recognize commonly used resources such as js and css, so other types of resources need to be processed on the server side. When requesting a single file component, you need to compile the single file component into a js module on the server and return it to the browser.

So here, when the browser requests for the first time, the server will compile the single-file component into an object, load the component first, and then create an object.

import Hello from './src/components/'
const __script = {
    name: "App",
    components: {
        Hello
    }
}

Then load the entry file. This time I will tell the server to compile the template of this single file component and return a render function. Then mount the render function to the component option object you just created, and finally export the option object.

import { render as __render } from '/src/?type=template'
__script.render = __render
__script.__hmrId = '/src/'
export default __script

That is to sayviteTwo requests will be sent. The first request will compile a single file file. The second request will be compiled with a single file template.renderfunction.

Compile single file options:

First, let’s implement the situation of requesting a single file for the first time. The single file component needs to be compiled into an option, which is also implemented with a middleware. This function needs to be processed after processing the static server and before processing the third-party module path.

First, you need to compile a single file component and need helpcompiler-sfc

// Handle single file components(async (ctx, next) => {
    if (('.vue')) {
        // Get the response file content and convert it into a string        const contents = await streamToString();
        // Compile the file contents        const { descriptor } = (contents);
        // Define status code        let code;
        // The first request is not requested if type does not exist        if (!) {
            code = ;
            // The code format here is, it needs to be modified to the appearance of the vite we posted earlier            // import Hello from './components/'
            // export default {
            //      name: 'App',
            //      components: {
            //          Hello
            //      }
            //  }
            // Reform the code format and replace export default with const __script =            code = (/export\s+default\s+/g, 'const __script = ')
            code += `
                import { render as __render } from '${}?type=template'
                __script.rener = __render
                export default __script
            `
        }
        // Set the browser response header to js         = 'application/javascript'
        // Convert the string to data and pass it to the next middleware.         = stringToStream(code);
    }
    await next()
})

const stringToStream = text => {
    const stream = new Readable();
    (text);
    (null);
    return stream;
}
npm install @vue/compiler-sfc -D

Then we will process the second request of the single file component, and the second request url will be brought with you.type=templateParameters, the single file component template needs to be compiled into the render function.

First, you need to determine whether there is any in the current requesttype=template

if (!) {
    ...
} else if ( === 'template') {
    // Get the compiled object code is the render function    const templateRender = ({ source:  })
    // Assign the render function to the code to return to the browser    code = 
}

Here we need to deal with the tools, because these codes will be returned to the browser to run, if not processed, the default will be node, causing the run to fail. You can modify it in the middleware that modify the path of the third-party module, and add a modification after modifying the path.

// Modify the path of the third-party module(async (ctx, next) => {
    if ( === 'application/javascript') {
        const contents = await stream2string();
        // Modify the imported path in the body, reassign the value to the body and return it to the browser        // import vue from 'vue', match from 'modify to from '@modules/         = (/(from\s+['"])(?![\.\/])/g, '$1/@modules/').replace(/process\.env\.NODE_ENV/g, '"development"');
    }
})

At this point, a simple version of vite was implemented. Of course, we only demonstrated the .vue file here, and it has not processed other resources such as css, less, etc., but the methods are all similar. Interested students can implement them themselves.

#!/usr/bin/env node

const path = require('path')
const { Readable } = require('stream)
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc')

const app = new Koa()

const stream2string = (stream) => {
    return new Promise((resolve, reject) => {
        const chunks = [];
        ('data', chunk => {(chunk)})
        ('end', () => { resolve((chunks).toString('utf-8'))})
        ('error', reject)
    })
}

const stringToStream = text => {
    const stream = new Readable();
    (text);
    (null);
    return stream;
}

// Load third-party modules(async (ctx, next) => {
    if (('/@modules/')) {
        // Intercept the module name        const moduleName = (10);
        // Find the module path        const pkgPath = ((), 'node_modules', moduleName, '');
        const pkg = require(pkgPath);
        // To reassign a value, you need to reset an existing path because the previous path does not exist.         = ('/node_modules', moduleName, );
        // Execute the next middleware        awiat next();
    }
})

// Turn on the static file server(async (ctx, next) => {
    // Load static files    await send(ctx, , { root: (), index: ''})
    await next()
})

// Handle single file components(async (ctx, next) => {
    if (('.vue')) {
        // Get the response file content and convert it into a string        const contents = await streamToString();
        // Compile the file contents        const { descriptor } = (contents);
        // Define status code        let code;
        // The first request is not requested if type does not exist        if (!) {
            code = ;
            // The code format here is, it needs to be modified to the appearance of the vite we posted earlier            // import Hello from './components/'
            // export default {
            //      name: 'App',
            //      components: {
            //          Hello
            //      }
            //  }
            // Reform the code format and replace export default with const __script =            code = (/export\s+default\s+/g, 'const __script = ')
            code += `
                import { render as __render } from '${}?type=template'
                __script.rener = __render
                export default __script
            `
        } else if ( === 'template') {
            // Get the compiled object code is the render function            const templateRender = ({ source:  })
            // Assign the render function to the code to return to the browser            code = 
        }
        // Set the browser response header to js         = 'application/javascript'
        // Convert the string to data and pass it to the next middleware.         = stringToStream(code);
    }
    await next()
})

// Modify the path of the third-party module(async (ctx, next) => {
    if ( === 'application/javascript') {
        const contents = await stream2string();
        // Modify the imported path in the body, reassign the value to the body and return it to the browser        // import vue from 'vue', match from 'modify to from '@modules/         = (/(from\s+['"])(?![\.\/])/g, '$1/@modules/').replace(/process\.env\.NODE_ENV/g, '"development"');
    }
})

(5000)

('The server has been started http://localhost:5000')

This is the end of this article about learning the principles of Vite. For more related content on Vite principles, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!