“This article has participated in the call for good writing activities, click to view: the back end, the big front end double track submission, 20,000 yuan prize pool waiting for you to challenge!”

Introduction to the

Vite there are dev | build | optimize | preview four commands, the article did combed the whole process of vite, for dev | build main steps did Jane answer split, facilitate everybody understanding of vite source to form a whole, You are welcome to correct any mistakes.

Start the

The two default commands of the Vite project, NPM run serve NPM run build, are started with the vite command. Open the /node_modules/. Bin directory, find the vite file, and the main process is to run the start function to load the node/ CLI file

// This is not the case. function start() { require('.. /dist/node/cli') } ... start() ...Copy the code

Then go to the cli file vite/ SRC /node/cli.ts

Vite scaffolding/src/node/cli.ts

Can see vite scaffolding a total of four orders, including dev | build | optimize | preview, this paper mainly dev | build

Dev dev environment build Preview Vite preview optimizeCopy the code

vite dev

cli.ts

The dev command in cli.ts references createServer of./server and triggers Listen () to listen

/node/server/index.ts

CreateServer creates and returns a server. Specifically, it does the following things:

  1. Integrate the configuration file vite.config.js and the configuration from the command line into config
const config = await resolveConfig(inlineConfig, "serve", "development");
Copy the code
  1. Start an HTTP (s)server and upgrade it to a Websocket.
const httpsOptions = await resolveHttpsConfig(config);

  const middlewares = connect() as Connect.Server;
  const httpServer = middlewareMode
    ? null
    : await resolveHttpServer(serverConfig, middlewares, httpsOptions);
  const ws = createWebSocketServer(httpServer, config, httpsOptions);
Copy the code
  1. Use Chokidar to listen for file changes (this is the basis for hot updates)
const watcher = chokidar.watch(path.resolve(root), { ignored: ["**/node_modules/**", "**/.git/**", ...ignored], ignoreInitial: true, ignorePermissionErrors: true, disableGlobbing: true, ... watchOptions, }) as FSWatcher;Copy the code
  1. All plugins are processed and stored in a container
const container = await createPluginContainer(config, watcher);
Copy the code
  1. ModuleGraph is used to document import relationships, URL-to-file mapping, and hot update relationships
    • And HMR state `)
const moduleGraph = new ModuleGraph(container); /** * Module graph that tracks the import relationships, URL to file mapping * and HMR State. */Copy the code
  1. Initialize the vite-dev-server to be returned later, binding some properties and methods
const server: ViteDevServer = {
...
}
Copy the code
  1. When watcher changes, the corresponding hot update process is performed
watcher.on('change', fn)
watcher.on('add', fn)
watcher.on('unlink', fn)
Copy the code
  1. Execute the Vite hookconfigureServerPostHooks only collects plugins with configureServer
const postHooks: ((() => void) | void)[] = []; For (const plugin of plugins) {if (plugin.configureServer) {// WK executes plugin posthooks.push (await) configured with configureServer plugin.configureServer(server)); }}Copy the code
  1. Use of internal middleware
. middlewares.use(corsMiddleware(typeof cors === "boolean" ? {} : cors)); middlewares.use(proxyMiddleware(httpServer, config)); .Copy the code
  1. Execute plugins in the posHooks
postHooks.forEach((fn) => fn && fn());
Copy the code
  1. Convert index. HTML
middlewares.use(indexHtmlMiddleware(server));
Copy the code
  1. Before the listen ()
    1. Execute vite hook buildStart
    2. Execute runOptimize() for pre-boot optimization
if (! middlewareMode && httpServer) { // overwrite listen to run optimizer before server start const listen = httpServer.listen.bind(httpServer); httpServer.listen = (async (port: number, ... args: any[]) => { try { await container.buildStart({}); await runOptimize(); } catch (e) { httpServer.emit("error", e); return; } return listen(port, ... args); }) as any; httpServer.once("listening", () => { // update actual port since this may be different from initial value serverConfig.port = (httpServer.address() as AddressInfo).port; }); } else { await container.buildStart({}); await runOptimize(); }Copy the code
  1. Back to the server

vite build

cli.ts

The build command imports the./build file and executes build().

A rollup packaging

Vite is packaged using rollup, so before reading about the methods, it’s worth understanding rollup in general. Below is the code description of rollup’s packaging process.rollup javascript API

You can see the rollup general packaging needs

  1. Packaged configuration parametersinputOptions
  2. Package the configuration parameters of the build fileoutputOptions
  3. callrollup.rollup()Return a bundle object
  4. callbundle.generate()orbundle.write()To complete the packaging

Then the Vite build should follow this process

build.ts

In build.ts, the build method mainly refers to the dobuild method, and dobuild does the following things (SSR related ones are not considered) :

  1. Modify configuration parameters => Config
const config = await resolveConfig(inlineConfig, "build", "production");
Copy the code
  1. Rollup packages input parameters => RollupOptions, before processing input parameters and external, which are important to the RollupOptions object
const RollupOptions: RollupOptions = { input, preserveEntrySignatures: ssr ? "allow-extension" : libOptions ? "strict" : false, ... options.rollupOptions, plugins, external, onwarn(warning, warn) { onRollupWarning(warning, warn, config); }};Copy the code
  1. Outputs are usually an obJ for project development, but when building libraries we may need to generate packages in different formats, so outputs can also be an array.
const outputs = resolveBuildOutputs( options.rollupOptions? .output, libOptions, config.logger );Copy the code
  1. Rollup also provides a watch function, which is implemented in Vite directly to rollup-watch
if (config.build.watch) { config.logger.info(chalk.cyanBright(`\nwatching for file changes... `)); const output: OutputOptions[] = []; if (Array.isArray(outputs)) { for (const resolvedOutput of outputs) { output.push(buildOuputOptions(resolvedOutput)); } } else { output.push(buildOuputOptions(outputs)); } const watcherOptions = config.build.watch; const watcher = rollup.watch({ ... rollupOptions, output, watch: { ... watcherOptions, chokidar: { ignored: [ "**/node_modules/**", "**/.git/**", ...(watcherOptions?.chokidar?.ignored || []), ], ignoreInitial: true, ignorePermissionErrors: true, ... watcherOptions.chokidar, }, }, }); watcher.on("event", (event) => { if (event.code === "BUNDLE_START") { config.logger.info(chalk.cyanBright(`\nbuild started... `)); if (options.write) { prepareOutDir(outDir, options.emptyOutDir, config); } } else if (event.code === "BUNDLE_END") { event.result.close(); config.logger.info(chalk.cyanBright(`built in ${event.duration}ms.`)); } else if (event.code === "ERROR") { outputBuildError(event.error); }}); // stop watching watcher.close(); return watcher; }Copy the code
  1. Generating bundle objects
const bundle = await rollup.rollup(rollupOptions);
Copy the code
  1. Call the bundle.write method, write it to the file, and you’re done. I called it before thatprepareOutDirMethod (Verifies the existence of the package directory and cleans it)
if (options.write) {
  prepareOutDir(outDir, options.emptyOutDir, config);
}

if (Array.isArray(outputs)) {
  const res = [];
  for (const output of outputs) {
    res.push(await generate(output));
  }
  return res;
} else {
  return await generate(outputs);
}
Copy the code

The follow-up plan

Read through vite to see the implementation in some detail, such as the handling of import {createApp} from ‘vue’

other

This reading related comments in vite source read, can install VScode plug-in todo tree, prefix ‘WK’ is I look at the process of adding comments