4
头图

Hello, I am very glad that you can click on this blog. This blog is a series of articles on the interpretation of the source code of Vite . After reading it carefully, I believe you can have a simple understanding of the workflow and principles of Vite .

Vite is a new front-end construction tool that can significantly improve the front-end development experience.

I will use the combination of pictures and texts to try to make this article less boring (obviously for source code interpretation articles, this is not a simple thing).

If you haven't used Vite , then you can read my first two articles, I just experienced it for two days. (as follows)

This article mainly interprets vite source code ontology, vite provides development server through connect library, and realizes multiple development server configurations through middleware mechanism. However, rollup did not use packaging tools such as webpack or vite during local development, but realized the translation of files by scheduling internal plugin , so as to achieve a small and fast effect.

Well, without further ado, let's get started!

vite dev

project directory

The Vite source code version read in this article is 2.8.0-beta.3 . If you want to read it with me, you can download the Vite source code at this address.

Let's first take a look at the project directory of the package Vite . (As shown below)

image

This is an integrated management project, the core of which is several packages in packages . Let's take a look at what these packages do. (as follows)

Package nameseffect
viteVite main library, responsible for the local development (plug-in scheduling) and production product construction (Rollup scheduling) of the Vite project
create-viteUsed to create a new Vite project, which stores the initialization templates of multiple frameworks (such as react、vue )
plugin-vueVite Official plugin to provide Vue 3 single-file component support
plugin-vue-jsxVite Official plugin to provide Vue 3 JSX support (via a dedicated Babel transform plugin).
plugin-reactVite official plugin for full React support
plugin-legacyVite official plugin, used to provide traditional browser compatibility support for packaged files
playgroundSome built-in test cases and Demo of Vite

These source code repositories are actually worth reading, but this time we will focus on our main line of this issue - Vite , and let's start with Vite .

Next, we focus on interpreting vite local development service command - vite / vite dev / vite serve .

vite dev

Let's take a look at vite dev command, the internal workflow of the local development service.

vite dev calls the internal createServer method to create a service, which uses middleware (third party) to support multiple capabilities (such as cross-domain, static file server, etc.), and internally created watcher to continuously monitor file changes , with just-in-time compilation and hot reloading.

And createServer does is the core logic we need to pay attention to.

In the createServer method, the collection of the configuration is first performed - resolveConfig .

Configurations supported by vite

We can just look at the configuration supported by the vite project through the source code. You can also directly refer Vite official document . (as follows)

Configuration nameConfiguration instructions
configFileConfiguration file, read the vite.config.js configuration file in the root directory by default
envFileEnvironment variable configuration file, read the .env environment variable configuration file in the root directory by default
rootThe root directory of the project, the default value is the directory where the command is executed - process.cwd()
baseSimilar to webpack in publicPath , which is the public base path of the resource
serverLocal runtime service settings, such as setting host (host address), port (running port)... For detailed configuration, please refer to vite document
buildFor options when building production products, you can refer to vite document
previewPreview option, after using the build command, you can run vite preview to preview the product. For the specific configuration, please refer to vite document
publicDirStatic resource directory, used to place static resources that do not need to be compiled, the default value is the public directory
cacheDirCache folder, used to place some cache dependencies precompiled by vite , to speed up the compilation speed of vite
modeCompile mode, the default value is development when running locally, and production when building production products
defineDefine global variables, where each item of the development environment will be defined globally, and the production environment will be statically replaced
pluginsConfigure the plugin for the vite project
resolveresolve supports many configurations, you can refer to vite document
cssFor the compilation options of the css file, you can refer to vite document
jsonFor the compilation options of the json file, you can refer to vite document
esbuildLooking at the official document is used to convert files, but I don't know what the specific work does. If you have any trouble, please leave a message in the comment area to clarify your doubts.
assetsIncludeSet the file type that needs to be processed independently by the picomatch mode (a file matching mode)
optimizeDepsDepends on optimization options, for details, please refer to vite document
ssrFor related options of ssr , please refer to vite document
logLevelAdjust the level of console output, default is info
customLoggerCustom logger , this option is not exposed, it is an internal option
clearScreenThe default is true . After the configuration is false , each recompile will not clear the previous content.
envDirThe directory used to load the environment variable configuration file .env , the default is the current root directory
envPrefixThe prefix of the environment variable, the environment variable with the prefix will be injected into the project
workerConfigure bundle output type, plugins and Rollup configuration items

Some of the above configurations can be added through command line parameters at startup, for example, set in the form of vite --base / --mode development .

If you want the configuration to be readable through configuration, you can also configure it all through vite.config.js .

Configure breakpoint debugging

After a cursory look at the configurations supported by vite , we return to createServer function, ready to start reading.

Before that, if we can directly run vite dev command and hit a breakpoint, it can better help us read the source code better, so let's configure it first.

We need to enter vite/packages/vite first, install the dependencies, then run scripts in npm run build , and build vite into the dist directory.

Then, we use the debugging capabilities of vscode , create a launch.json (below), and run one of our vite projects.

// launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "pwa-node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": [
        "<node_internals>/**"
      ],
      "program": "packages/vite/bin/vite.js",
      "args": ["/Users/Macxdouble/Desktop/ttt/vite-try"]
    }
  ]
}

image

After the debugging configuration is completed, we can hit a breakpoint in the resolveConfig function to check the effect (the file location is in the dist directory, you need to find the corresponding file according to your own reference).

image

load configuration file

The first step of resolveConfig is to load the configuration file of the project directory. If the location of the configuration file is not specified, it will automatically search for vite.config.js , vite.config.mjs , vite.config.ts , vite.config.cjs in the root directory.

If no configuration file is found, the program will be aborted directly.

When the vite project is initialized, the vite.config.js configuration file will be automatically generated in the project root directory.

image

After reading the configuration file, the configuration file and the initial configuration (higher priority, some configurations come from command line parameters) will be merged, and then a configuration will be obtained. (As shown below)

image

Configuration Collection - resolveConfig

At the beginning of createServer , the resolveConfig function is called for configuration collection.

Let's take a look at what resolveConfig has done.

Handling plugin execution order

First, resolveConfig handles the plugin collation internally, corresponding to the following collation.

image

In the process of subsequent processing, the plug-ins will be executed sequentially according to the corresponding sorting rules, so that the plug-ins can work better in each life cycle node.

Merge plugin configuration

After the plug-in sorting is completed, the vite plug-in of exposes a configuration field of config . By setting this property, the plug-in can add or rewrite some configurations of vite . (As shown below)

image

Handling aliases

Then, resolveConfig internally processes the logic of alias , and replaces the specified alias with the corresponding path.

image

Read environment variable configuration

Next, resolveConfig finds the configuration directory of env (the default is the root directory), and then reads the corresponding env environment variable configuration file in the directory. We can look at the internal read rule priority (as shown below)

image

It can be seen that the read priorities are .env.[mode].local and .env.[mode] respectively. If there is no configuration file corresponding to mode , it will try to find .env.local and .env configuration files. After reading the configuration files, use doteenv to write the environment variables into the project; if these environment variable configuration files do not exist, then will return an empty object.

The environment variable configuration file does not affect the operation of the project, so it has no effect if it is not configured.

export configuration

Next, vite initializes the build configuration, which is the build property in the document. For details, please refer to the build options document

image

Finally, resolveConfig processed some publicDir and cacheDir directories, the following configuration was exported.

const resolved: ResolvedConfig = {
    ...config,
    configFile: configFile ? normalizePath(configFile) : undefined,
    configFileDependencies,
    inlineConfig,
    root: resolvedRoot,
    base: BASE_URL,
    resolve: resolveOptions,
    publicDir: resolvedPublicDir,
    cacheDir,
    command,
    mode,
    isProduction,
    plugins: userPlugins,
    server,
    build: resolvedBuildOptions,
    preview: resolvePreviewOptions(config.preview, server),
    env: {
      ...userEnv,
      BASE_URL,
      MODE: mode,
      DEV: !isProduction,
      PROD: isProduction
    },
    assetsInclude(file: string) {
      return DEFAULT_ASSETS_RE.test(file) || assetsFilter(file)
    },
    logger,
    packageCache: new Map(),
    createResolver,
    optimizeDeps: {
      ...config.optimizeDeps,
      esbuildOptions: {
        keepNames: config.optimizeDeps?.keepNames,
        preserveSymlinks: config.resolve?.preserveSymlinks,
        ...config.optimizeDeps?.esbuildOptions
      }
    },
    worker: resolvedWorkerOptions
  }

image

resolveConfig has some additional work processing inside, mainly collecting the internal plug-in collection (as shown in the figure below), and configuring some deprecated option warning messages.

image

Local Development Services - createServer

Going back to the createServer method, this method processes the logic of ssr (server-side rendering) for the first time after getting the configuration through resolveConfig .

If server-side rendering is used, local development and debugging will be performed in other ways.

If it is not server-side rendering, a http server will be created for local development and debugging, and a websocket service will be created for hot reloading. (As shown below)

image

File monitoring + hot reload

Then, vite creates a FSWatcher object to monitor changes in local project files. (The chokidar library is used here)

  const watcher = chokidar.watch(path.resolve(root), {
    ignored: [
      // 忽略 node_modules 目录的文件变更
      '**/node_modules/**',
      // 忽略 .git 目录的文件变更
      '**/.git/**',
      // 忽略用户传入的 `ignore` 目录文件的变更
      ...(Array.isArray(ignored) ? ignored : [ignored])
    ],
    ignoreInitial: true,
    ignorePermissionErrors: true,
    disableGlobbing: true,
    ...watchOptions
  }) as FSWatcher

Then, vite organizes multiple properties and methods into a server object, which is responsible for starting the local development service and also responsible for the subsequent development hot reloading of the service.

Next, let's take a look at how watcher does page hot reloading. The principle is to re-trigger the plugin compilation after monitoring the file change, and then send the update message to the client. (As shown below)

image

plugin container

Next, vite creates the plugin container ( pluginContainer ) for calling the plugin's hooks at various stages of the build. (As shown below)

image

In fact, the plugin container is created before hot reloading. For the convenience of reading, the article puts the hot reloading content together.

middleware mechanism

Next is some internal middleware processing. When configuring development server options, vite internally provides support through the middleware capabilities of the connect framework. (As shown below)

image

Among them, many configurations such as public directory and public path are realized through connect + middleware, making full use of the capabilities of third-party libraries without reinventing the wheel.

prebuilt dependencies

Next, vite pre-builds the dependencies used in the project, one is to be compatible with different ES module specifications, and the other is to improve the loading performance. (As shown below)

image

When the preparations are ready, vite internally calls startServer start the local development server. (as follows)

// ...
httpServer.listen(port, host, () => {
  httpServer.removeListener('error', onError)
  resolve(port)
})

summary

So far, the source code part of vite itself has been parsed.

It can be seen that during local development, vite mainly relies on plug-in + middleware system to provide capability support. Because only a small amount of compilation work is involved in local development, it is very fast. vite rollup building the production product.

Let's use a flowchart to finally sort out the internal workflow of the vite local development service.

image

Then this article ends here. In the next article, I will select 1-2 typical plug-ins or build (production product construction) for source code analysis.

one last thing

If you have seen this, I hope you will give a like and go~

Your likes are the greatest encouragement to the author, and can also allow more people to see this article!

If you think this article is helpful to you, please help to light up star on github to encourage it!


晒兜斯
1.8k 声望535 粉丝