Location>code7788 >text

Vite Local Builds: Handwriting Core Principles

Popularity:530 ℃/2024-07-28 17:05:30

preamble

Picking up from the previous post, we learn thatviteThe principle of local construction is mainly:Start a connect server to intercept the ESM request from the browser. Finds the corresponding file in the requested path and compiles it to return it to the browser in the ESM format.

Based on this core idea, we can try to get our hands dirty to realize it.

Build a static server

on the basis ofkoaBuild a project:

The project is structured as above, with services usingkoaBuild.binSpecify the location of the cli executable

#!/usr/bin/env node
// means that the script uses thenodefulfillment

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



const App = new koa()

(3000, () => {
    ('Server is running at http://localhost:3000');
});

This way a service is built, and for debugging purposes, we execute in that working directorynpm linkThis will link the project to the global npm, which is equivalent to installing the npm package globally.

Then we run themy-viteYou will be able to start the service!

Processing root directory html files

Since we didn't process any routes for the above service, when accessing thehttp://localhost:3000You will find nothing, we first need to put the project's template fileReturn to Browser

const root = (); // Get the current working directory
('Current working directory: ', ()); // Get the current working directory.

// Static file serving area
(async (ctx, next) => {
    // Process the root path and return
    await send(ctx, , { root: () ,index: ''}); }
    await next(); { // Process the root path and return.
}).

The template file is below:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue + TS</title>
  </head>
  <body>
    <div id="app"></div>
    <script>
       = { env: { NODE_ENV: 'development' } };
    </script>
    <script type="module" src="/src/"></script>
  </body>
</html>

that isESMis loaded by means of thevueentry file for the

After adding this code, we add thevue3Let's do it under the projectmy-vite

Come to the browser to see what's going on at this point:

At this point the browser loads theThe file is as follows: it passes theimportTwo modules were introduced

import { createApp } from 'vue'
import App from './'


createApp(App).mount('#app')

Logically, the browser should have proceeded to initiate a request to fetch both modules at this point, but it doesn't now 🤔

At this point there is an error on the console:

It means that to load a module, it must be loaded with a relative path (/, . /, . /)

So we now need to deal with the loading paths of these modules

Processing module load path

Since the tripartite modules are loaded directly by module name, here we need to convert the reference paths of these modules to relative paths.

// Handle the module import
const importAction = (content) => {
    return (/(from\s+['"])(?! \. \/)/g, '$1/@modules/')
}

// Modify the path to the third-party module
(async (ctx, next) => {
    // ('', , ); }
    // Handle ts or js files
    if (('.ts') || ('.js')){
        const content = await fileToString(); // Get the content of the file
         = 'application/javascript'; // set the response type to js
         = importAction(content); // Handle import loading paths
    }
    await next(); }
}).

In this middleware, we use regular expressions to replace the module's reference path with the path of the/@modulesbeginning, so that it conforms to the browser's quoting rules.

Then go back to the browser to see what happens at this point:

At this point the browser can already make two other requests to load thevuemodules as well asComponent up.

can be seenvueThe load path of the module has changed to/@modulesIt's open, although the path is still404, but at the very least it's a step forward from where we were before.

indeed404It's also well understood that since our service doesn't handle these kinds of paths at all yet, it should handle them next/@modulesthis kind (of)pathand load the contents of the module

Loading third-party modules

Here we just need to go to the intercept just/@modulesand find the real location of the module under that path, and finally return it to the browser.

// Loading third-party modules
(async (ctx, next) => {
    if (('/@modules/')) {
        const moduleName = (10); // Get module name
        const modulePath = (root, 'node_modules', moduleName); // Get module path
        const package = require(modulePath + '/'); // Get the module's
        // ('modulePath', modulePath);
         = ('/node_modules', moduleName, ); // Rewrite Path
    }
    await next();
    
});

We can do this by reading thein the filemodulefield to find the entry file for the third-party module.

This middleware needs to be executed before the middleware that handles module loading paths

At this point come back to the browser to view it:

It can be seen that at this point in time thevueThe module has been able to be reloaded, but there are four more modules loaded below, where did they come from?

can be seenvueThe module introduces anotherruntime-dommodules, and their load paths have been converted to the/@modulesTo begin with, this is the above mentionedThe middleware that loads the module needs to be executed before the middleware that handles the module load pathsAfter the module is loaded back, it passes through the middleware that handles the loading path, so it is equivalent to recursively converting all the paths of the module into relative paths.

runtime-domThe module in turn introduces theruntime-coretogether withsharedmodule, whereasruntime-coreThe module in turn introduces thereactivitymodule, so you'll see a loading order like the one in the image above.

The modules are loaded and introduced correctly, but the page still doesn't show any rendered content.

This is because at this point in timeIt hasn't been compiled in any way, and browsers don't recognize and execute the file directly.

So the next point is that you need to putfile into a browser-executablejavascriptContent (render function)

Handling Vue single-file components

Here we need to use theVuecompilation module@vue/compiler-sfctogether with@vue/compiler-dom(coll.) speak tovueThe file is compiled and processed.

Handling scripts

const content = await fileToString(); // get the file content
const { descriptor } = (content); // Parsing a single file component
const compileScript = importAction(
   (
     descriptor, { { descriptor } = (content); // Parsing a single-file component.
     {
       id.
     }
   ).content); // compile script

Handling templates

const compileRender =importAction((,
                // compilingtemplate, renderThe variables in the function are removed from thesetupGetting
            { mode: 'module',
                sourceMap: true,
                filename: (),
                __isScriptSetup: true, // Whether or not the marker issetup
                compatConfig: { MODE: 3 }, // compatibilityvue3
            }).code); // compilingtemplate

Handling style

let styles = '';
if(){
  ('', );
  // Handle styles
  styles = ((style,index) => {
    return `
             import '${}?type=style&index=${index}';
    `
  }).join('\n');

} // Handling styles

Here's a way to make an additional request to thestyleThis is handled so that the segregation logic can be clearer

Handling requests for styles

In the middleware by intercepting thetypebecause ofstylerequest to be processed by the

if ( === 'style') {
  // Handling styles
  const styleBlock = [];
  ('styleBlock', styleBlock);
   = 'application/javascript';
   = `
            const _style = (css) => {
                const __style = ('style');
                __style.type = 'text/css';
                __style.innerHTML = css;
                (__style);
                }
                _style(${()});
                export default _style;
            `;
}

final verification

summarize

After an in-depth exploration of theviteAfter the workflow, you may realize that while it may seem simple conceptually, theviteThe implementation behind it is yet quite complex and subtle. We've just gone through a walk-through of its core process for theviteThere is an initial understanding of how to load modules, parse and compile files. However, this is just the tip of the iceberg.

Overall.viteWhile the workings of the program can be understood with a simplified example, its true power and complexity extends far beyond that. It would be nice to have a better understanding of theviteIf you are interested in the in-depth workings of this program, you can read its source code in depth, where we can learn a lot more.