1. 前言
1.1 环境
- 学会 vite 简单原理
2. 开始
2.1 项目结构
2.1.1 monorepo管理方式
从上图可以看出,这是一个monorepo项目,单仓库多包管理
使用 pnpm 作为包管理工具,pnpm天生支持workspae,可以不需要再使用 lerna 来管理仓库了
packages文件夹下面的每一个文件夹都是一个npm包
playground文件夹比较特殊,里面的每个文件夹又是一个独立的npm包2.1.2 scripts run-s
在dependencies找了一圈,没发现有run-s这个包,不过看到了 npm-run-all,之前有看过这个包,知道是执行多脚本的,因此在node_modules下面查看了该包的bin文件夹,发现有run-s,run-p,npm-run-all等三个脚本文件夹,恍然大悟,就是run-s就是 npm-run-all的一个bin执行文件
2.2 packages/vite 执行文件bin/vite.js
```javascript!/usr/bin/env node
// node里面的性能api const { performance } = require(‘perf_hooks’) // 如果没有node_modules if (!dirname.includes(‘node_modules’)) { try { // only available as dev dependency, 使用source-map加载 require(‘source-map-support’).install() } catch (e) {} } // 全局添加参数 global.vite_start_time = performance.now()
// check debug mode first before requiring the CLI. // 找到参数是-d | —debug,另一种写法,没使用minimist来处理参数 (?:pattern) 非获取匹配 const debugIndex = process.argv.findIndex((arg) => /^(?:-d|—debug)$/.test(arg)) const filterIndex = process.argv.findIndex((arg) => /^(?:-f|—filter)$/.test(arg) ) const profileIndex = process.argv.indexOf(‘—profile’)
if (debugIndex > 0) {
// 获取参数的值这里应该是用空格做分割,如 —debug xxx
let value = process.argv[debugIndex + 1]
// 如果不存在,或者以-开头,说明没有参数值
if (!value || value.startsWith(‘-‘)) {
// 默认设置vite:
value = ‘vite:‘
} else {
// support debugging multiple flags with comma-separated list
// 如果有多个包用逗号分隔,批量添加vite:x,vite:z
value = value
.split(‘,’)
.map((v) => vite:${v})
.join(‘,’)
}
// 添加环境变量DEBUG,可以在全局用
process.env.DEBUG = value
if (filterIndex > 0) { // 过滤的值 const filter = process.argv[filterIndex + 1] if (filter && !filter.startsWith(‘-‘)) { // 这里没做处理,直接放入环境变量,全局变量可以放入环境变量中 process.env.VITE_DEBUG_FILTER = filter } } }
function start() { // 执行dist/node/cli require(‘../dist/node/cli’) } // 这个是开启分析 if (profileIndex > 0) { // 从argv中删除参数 process.argv.splice(profileIndex, 1) // 拿到—profile后面一个值 const next = process.argv[profileIndex] if (next && !next.startsWith(‘-‘)) { // 从参数中删除 process.argv.splice(profileIndex, 1) } // node调试api,可以接入到chrome const inspector = require(‘inspector’) // 放入到global全局, const session = (global.__vite_profile_session = new inspector.Session()) session.connect() session.post(‘Profiler.enable’, () => { session.post(‘Profiler.start’, start) }) } else { start() }
<a name="FA8Lf"></a>## 2.3 vite src/node/cli.ts```typescript// cli工具import { cac } from 'cac'// log输出带颜色import chalk from 'chalk'// 性能工具import { performance } from 'perf_hooks'// 打包配置import { BuildOptions } from './build'// 服务配置import { ServerOptions } from './server'// 日志和等级import { createLogger, LogLevel } from './logger'// 解析配置import { resolveConfig } from '.'import { preview } from './preview'const cli = cac('vite')// global options 全局配置interface GlobalCLIOptions {'--'?: string[]c?: boolean | stringconfig?: stringbase?: stringl?: LogLevellogLevel?: LogLevelclearScreen?: booleand?: boolean | stringdebug?: boolean | stringf?: stringfilter?: stringm?: stringmode?: string}/*** removing global flags before passing as command specific sub-configs*/function cleanOptions<Options extends GlobalCLIOptions>(options: Options): Omit<Options, keyof GlobalCLIOptions> {const ret = { ...options }delete ret['--']delete ret.cdelete ret.configdelete ret.basedelete ret.ldelete ret.logLeveldelete ret.clearScreendelete ret.ddelete ret.debugdelete ret.fdelete ret.filterdelete ret.mdelete ret.modereturn ret}// 基础配置说明 cli.option都是用来做help说明的cli.option('-c, --config <file>', `[string] use specified config file`).option('--base <path>', `[string] public base path (default: /)`).option('-l, --logLevel <level>', `[string] info | warn | error | silent`).option('--clearScreen', `[boolean] allow/disable clear screen when logging`).option('-d, --debug [feat]', `[string | boolean] show debug logs`).option('-f, --filter <filter>', `[string] filter debug logs`).option('-m, --mode <mode>', `[string] set env mode`)// dev// 执行cli serve 和 dev就会匹配到执行里面的actioncli.command('[root]') // default command,如果不输入命令,直接执行这个.alias('serve') // the command is called 'serve' in Vite's API.alias('dev') // alias to align with the script name.option('--host [host]', `[string] specify hostname`).option('--port <port>', `[number] specify port`).option('--https', `[boolean] use TLS + HTTP/2`).option('--open [path]', `[boolean | string] open browser on startup`).option('--cors', `[boolean] enable CORS`).option('--strictPort', `[boolean] exit if specified port is already in use`).option('--force',`[boolean] force the optimizer to ignore the cache and re-bundle`).action(async (root: string, options: ServerOptions & GlobalCLIOptions) => {// output structure is preserved even after bundling so require()// is ok here 在代码里面使用importconst { createServer } = await import('./server')try {// 创建server对象const server = await createServer({root,base: options.base,mode: options.mode,configFile: options.config,logLevel: options.logLevel,clearScreen: options.clearScreen,server: cleanOptions(options)})if (!server.httpServer) {throw new Error('HTTP server not available')}await server.listen()const info = server.config.logger.infoinfo(chalk.cyan(`\n vite v${require('vite/package.json').version}`) +chalk.green(` dev server running at:\n`),{clear: !server.config.logger.hasWarned})server.printUrls()// @ts-ignore 性能监控的内容,这个值从bin/vite.js中设置的,用来获取开启服务的事件if (global.__vite_start_time) {// @ts-ignoreconst startupDuration = performance.now() - global.__vite_start_timeinfo(`\n ${chalk.cyan(`ready in ${Math.ceil(startupDuration)}ms.`)}\n`)}} catch (e) {createLogger(options.logLevel).error(chalk.red(`error when starting dev server:\n${e.stack}`),{ error: e })process.exit(1)}})// build// 打包相关的cli.command('build [root]').option('--target <target>', `[string] transpile target (default: 'modules')`).option('--outDir <dir>', `[string] output directory (default: dist)`).option('--assetsDir <dir>',`[string] directory under outDir to place assets in (default: _assets)`).option('--assetsInlineLimit <number>',`[number] static asset base64 inline threshold in bytes (default: 4096)`).option('--ssr [entry]',`[string] build specified entry for server-side rendering`).option('--sourcemap',`[boolean] output source maps for build (default: false)`).option('--minify [minifier]',`[boolean | "terser" | "esbuild"] enable/disable minification, ` +`or specify minifier to use (default: esbuild)`).option('--manifest', `[boolean] emit build manifest json`).option('--ssrManifest', `[boolean] emit ssr manifest json`).option('--emptyOutDir',`[boolean] force empty outDir when it's outside of root`).option('-w, --watch', `[boolean] rebuilds when modules have changed on disk`).action(async (root: string, options: BuildOptions & GlobalCLIOptions) => {// 调用build函数const { build } = await import('./build')const buildOptions: BuildOptions = cleanOptions(options)try {await build({root,base: options.base,mode: options.mode,configFile: options.config,logLevel: options.logLevel,clearScreen: options.clearScreen,build: buildOptions})} catch (e) {createLogger(options.logLevel).error(chalk.red(`error during build:\n${e.stack}`),{ error: e })process.exit(1)}})// optimize 优化cli.command('optimize [root]').option('--force',`[boolean] force the optimizer to ignore the cache and re-bundle`).action(async (root: string, options: { force?: boolean } & GlobalCLIOptions) => {const { optimizeDeps } = await import('./optimizer')try {const config = await resolveConfig({root,base: options.base,configFile: options.config,logLevel: options.logLevel},'build','development')await optimizeDeps(config, options.force, true)} catch (e) {createLogger(options.logLevel).error(chalk.red(`error when optimizing deps:\n${e.stack}`),{ error: e })process.exit(1)}})cli.command('preview [root]').option('--host [host]', `[string] specify hostname`).option('--port <port>', `[number] specify port`).option('--strictPort', `[boolean] exit if specified port is already in use`).option('--https', `[boolean] use TLS + HTTP/2`).option('--open [path]', `[boolean | string] open browser on startup`).action(async (root: string,options: {host?: string | booleanport?: numberhttps?: booleanopen?: boolean | stringstrictPort?: boolean} & GlobalCLIOptions) => {try {const server = await preview({root,base: options.base,configFile: options.config,logLevel: options.logLevel,preview: {port: options.port,strictPort: options.strictPort,host: options.host,https: options.https,open: options.open}})server.printUrls()} catch (e) {createLogger(options.logLevel).error(chalk.red(`error when starting preview server:\n${e.stack}`),{ error: e })process.exit(1)}})// cli.js --help 可以查看上面配置的optioncli.help()// cli.js --version 可以查看当前版本cli.version(require('../../package.json').version)// 获取命令的值cli.parse()
cac cli工具
2.4 vite src/node/server/index.ts
2.4.1 createServer 创建server对象
import fs from 'fs'import path from 'path'import * as net from 'net'import * as http from 'http'import connect from 'connect'import corsMiddleware from 'cors'import chalk from 'chalk'// 获取ip信息import { AddressInfo } from 'net'// 监听文件变化import chokidar from 'chokidar'import {resolveHttpsConfig,resolveHttpServer,httpServerStart,CommonServerOptions} from '../http'import { resolveConfig, InlineConfig, ResolvedConfig } from '../config'import { createPluginContainer, PluginContainer } from './pluginContainer'import { FSWatcher, WatchOptions } from 'types/chokidar'import { createWebSocketServer, WebSocketServer } from './ws'import { baseMiddleware } from './middlewares/base'import { proxyMiddleware } from './middlewares/proxy'import { spaFallbackMiddleware } from './middlewares/spaFallback'import { transformMiddleware } from './middlewares/transform'import {createDevHtmlTransformFn,indexHtmlMiddleware} from './middlewares/indexHtml'import {serveRawFsMiddleware,servePublicMiddleware,serveStaticMiddleware} from './middlewares/static'import { timeMiddleware } from './middlewares/time'import { ModuleGraph, ModuleNode } from './moduleGraph'import { Connect } from 'types/connect'import { ensureLeadingSlash, normalizePath } from '../utils'import { errorMiddleware, prepareError } from './middlewares/error'import { handleHMRUpdate, HmrOptions, handleFileAddUnlink } from './hmr'import { openBrowser } from './openBrowser'import launchEditorMiddleware from 'launch-editor-middleware'import {TransformOptions,TransformResult,transformRequest} from './transformRequest'import {transformWithEsbuild,ESBuildTransformResult} from '../plugins/esbuild'import { TransformOptions as EsbuildTransformOptions } from 'esbuild'import { DepOptimizationMetadata, optimizeDeps } from '../optimizer'import { ssrLoadModule } from '../ssr/ssrModuleLoader'import { resolveSSRExternal } from '../ssr/ssrExternal'import {rebindErrorStacktrace,ssrRewriteStacktrace} from '../ssr/ssrStacktrace'import { ssrTransform } from '../ssr/ssrTransform'import { createMissingImporterRegisterFn } from '../optimizer/registerMissing'import { resolveHostname } from '../utils'import { searchForWorkspaceRoot } from './searchRoot'import { CLIENT_DIR } from '../constants'import { printCommonServerUrls } from '../logger'import { performance } from 'perf_hooks'import { invalidatePackageData } from '../packages'import { SourceMap } from 'rollup'export { searchForWorkspaceRoot } from './searchRoot'// cli里面调用的createServerexport async function createServer(// 配置项inlineConfig: InlineConfig = {}): Promise<ViteDevServer> {// 解析配置项const config = await resolveConfig(inlineConfig, 'serve', 'development')const root = config.rootconst serverConfig = config.server// 解析http配置const httpsOptions = await resolveHttpsConfig(config.server.https)let { middlewareMode } = serverConfigif (middlewareMode === true) {middlewareMode = 'ssr'}const middlewares = connect() as Connect.Server// 创建http服务const httpServer = middlewareMode? null: await resolveHttpServer(serverConfig, middlewares, httpsOptions)// 创建一个webSocket服务,用来做hrm做的const ws = createWebSocketServer(httpServer, config, httpsOptions)// 监听服务const { ignored = [], ...watchOptions } = serverConfig.watch || {}const watcher = chokidar.watch(path.resolve(root), {ignored: ['**/node_modules/**','**/.git/**',...(Array.isArray(ignored) ? ignored : [ignored])],ignoreInitial: true,ignorePermissionErrors: true,disableGlobbing: true,...watchOptions}) as FSWatcherconst moduleGraph: ModuleGraph = new ModuleGraph((url) =>container.resolveId(url))// 插件容器const container = await createPluginContainer(config, moduleGraph, watcher)// 关闭http函数const closeHttpServer = createServerCloseFn(httpServer)// eslint-disable-next-line prefer-constlet exitProcess: () => void// server对象,包含的一些属性和方法const server: ViteDevServer = {config,middlewares,get app() {config.logger.warn(`ViteDevServer.app is deprecated. Use ViteDevServer.middlewares instead.`)return middlewares},httpServer,watcher,pluginContainer: container,ws,moduleGraph,ssrTransform,transformWithEsbuild,transformRequest(url, options) {return transformRequest(url, server, options)},transformIndexHtml: null!, // to be immediately setssrLoadModule(url) {server._ssrExternals ||= resolveSSRExternal(config,server._optimizeDepsMetadata? Object.keys(server._optimizeDepsMetadata.optimized): [])return ssrLoadModule(url, server)},ssrFixStacktrace(e) {if (e.stack) {const stacktrace = ssrRewriteStacktrace(e.stack, moduleGraph)rebindErrorStacktrace(e, stacktrace)}},// 这里做监听,开启serverlisten(port?: number, isRestart?: boolean) {return startServer(server, port, isRestart)},// 关闭服务async close() {// 执行关闭信号量process.off('SIGTERM', exitProcess)if (!middlewareMode && process.env.CI !== 'true') {process.stdin.off('end', exitProcess)}// 关闭所有的服务await Promise.all([// 监听器watcher.close(),// hrm服务ws.close(),// 插件容器container.close(),// 关闭http服务closeHttpServer()])},printUrls() {if (httpServer) {printCommonServerUrls(httpServer, config.server, config)} else {throw new Error('cannot print server URLs in middleware mode.')}},// 重启async restart(forceOptimize: boolean) {if (!server._restartPromise) {server._forceOptimizeOnRestart = !!forceOptimizeserver._restartPromise = restartServer(server).finally(() => {server._restartPromise = nullserver._forceOptimizeOnRestart = false})}return server._restartPromise},_optimizeDepsMetadata: null,_ssrExternals: null,_globImporters: Object.create(null),_restartPromise: null,_forceOptimizeOnRestart: false,_isRunningOptimizer: false,_registerMissingImport: null,_pendingReload: null,_pendingRequests: new Map()}// html转换函数server.transformIndexHtml = createDevHtmlTransformFn(server)exitProcess = async () => {try {await server.close()} finally {process.exit(0)}}// 一旦接受到信号,执行exitProcessprocess.once('SIGTERM', exitProcess)if (!middlewareMode && process.env.CI !== 'true') {process.stdin.on('end', exitProcess)}// 缓存包数据const { packageCache } = configconst setPackageData = packageCache.set.bind(packageCache)packageCache.set = (id, pkg) => {if (id.endsWith('.json')) {// json文件放入watcher监听watcher.add(id)}return setPackageData(id, pkg)}// 监听文件变化watcher.on('change', async (file) => {file = normalizePath(file)if (file.endsWith('/package.json')) {return invalidatePackageData(packageCache, file)}// invalidate module graph cache on file changemoduleGraph.onFileChange(file)if (serverConfig.hmr !== false) {try {await handleHMRUpdate(file, server)} catch (err) {ws.send({type: 'error',err: prepareError(err)})}}})watcher.on('add', (file) => {handleFileAddUnlink(normalizePath(file), server)})watcher.on('unlink', (file) => {handleFileAddUnlink(normalizePath(file), server, true)})if (!middlewareMode && httpServer) {httpServer.once('listening', () => {// update actual port since this may be different from initial valueserverConfig.port = (httpServer.address() as AddressInfo).port})}// apply server configuration hooks from plugins// 将插件放入postHooks队列中,执行plugin.configureServer将server实例注入到插件中const postHooks: ((() => void) | void)[] = []for (const plugin of config.plugins) {if (plugin.configureServer) {postHooks.push(await plugin.configureServer(server))}}// Internal middlewares ------------------------------------------------------// request timerif (process.env.DEBUG) {// 时间中间件middlewares.use(timeMiddleware(root))}// cors (enabled by default)const { cors } = serverConfigif (cors !== false) {// 跨域中间件middlewares.use(corsMiddleware(typeof cors === 'boolean' ? {} : cors))}// proxyconst { proxy } = serverConfigif (proxy) {// 代理中间件middlewares.use(proxyMiddleware(httpServer, config))}// baseif (config.base !== '/') {//base不是/,添加新的basemiddlewares.use(baseMiddleware(server))}// open in editor support,监听下面的路由,如果监听到会打开编辑器中的文件,如vscode打开一个vue文件middlewares.use('/__open-in-editor', launchEditorMiddleware())// hmr reconnect ping// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`middlewares.use('/__vite_ping', function viteHMRPingMiddleware(_, res) {res.end('pong')})// serve static files under /public// this applies before the transform middleware so that these files are served// as-is without transforms.if (config.publicDir) {// 公共文件middlewares.use(servePublicMiddleware(config.publicDir))}// main transform middleware 转换中间件,插件的加载也在这里load,如plugin-vuemiddlewares.use(transformMiddleware(server))// serve static filesmiddlewares.use(serveRawFsMiddleware(server))middlewares.use(serveStaticMiddleware(root, server))// spa fallbackif (!middlewareMode || middlewareMode === 'html') {middlewares.use(spaFallbackMiddleware(root))}// run post config hooks// This is applied before the html middleware so that user middleware can// serve custom content instead of index.html.// 执行插件postHooks.forEach((fn) => fn && fn())if (!middlewareMode || middlewareMode === 'html') {// transform index.htmlmiddlewares.use(indexHtmlMiddleware(server))// handle 404s// 添加404// Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...`middlewares.use(function vite404Middleware(_, res) {res.statusCode = 404res.end()})}// error handler 异常处理middlewares.use(errorMiddleware(server, !!middlewareMode))const runOptimize = async () => {if (config.cacheDir) {server._isRunningOptimizer = truetry {server._optimizeDepsMetadata = await optimizeDeps(config,config.server.force || server._forceOptimizeOnRestart)} finally {server._isRunningOptimizer = false}server._registerMissingImport = createMissingImporterRegisterFn(server)}}if (!middlewareMode && httpServer) {let isOptimized = false// overwrite listen to run optimizer before server startconst listen = httpServer.listen.bind(httpServer)httpServer.listen = (async (port: number, ...args: any[]) => {if (!isOptimized) {try {await container.buildStart({})// 运行优化await runOptimize()isOptimized = true} catch (e) {httpServer.emit('error', e)return}}return listen(port, ...args)}) as any} else {await container.buildStart({})await runOptimize()}return server}
2.4.2 startServer 启动http服务
async function startServer(server: ViteDevServer,inlinePort?: number,isRestart: boolean = false): Promise<ViteDevServer> {const httpServer = server.httpServerif (!httpServer) {throw new Error('Cannot call server.listen in middleware mode.')}const options = server.config.serverconst port = inlinePort || options.port || 3000const hostname = resolveHostname(options.host)const protocol = options.https ? 'https' : 'http'const info = server.config.logger.infoconst base = server.config.baseconst serverPort = await httpServerStart(httpServer, {port,strictPort: options.strictPort,host: hostname.host,logger: server.config.logger})// @ts-ignore 分析工具, 参数来源于cli里面配置的const profileSession = global.__vite_profile_sessionif (profileSession) {profileSession.post('Profiler.stop', (err: any, { profile }: any) => {// Write profile to disk, upload, etc.if (!err) {const outPath = path.resolve('./vite-profile.cpuprofile')fs.writeFileSync(outPath, JSON.stringify(profile))info(chalk.yellow(` CPU profile written to ${chalk.white.dim(outPath)}\n`))} else {throw err}})}// 打开浏览器if (options.open && !isRestart) {const path = typeof options.open === 'string' ? options.open : baseopenBrowser(path.startsWith('http')? path: `${protocol}://${hostname.name}:${serverPort}${path}`,true,server.config.logger)}return server}
2.5 plugin-vue插件
2.5.1 vue-project中的vite.config.js配置
import { fileURLToPath } from 'url'import { defineConfig } from 'vite'import vue from '@vitejs/plugin-vue'// https://vitejs.dev/config/export default defineConfig({plugins: [vue()],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}})
上述配置可以看出使用了vue插件,通过server服务监听路由,请求了vue文件,然后会调用插件进行处理
2.5.2 plugin-vue/src/index.tx
export default function vuePlugin(rawOptions: Options = {}): Plugin {// 插件配置,可以传空const {include = /\.vue$/,exclude,customElement = /\.ce\.vue$/,refTransform = false} = rawOptionsconst filter = createFilter(include, exclude)const customElementFilter =typeof customElement === 'boolean'? () => customElement: createFilter(customElement)const refTransformFilter =refTransform === false? () => false: refTransform === true? createFilter(/\.(j|t)sx?$/, /node_modules/): createFilter(refTransform)// compat for older versionsconst canUseRefTransform = typeof compiler.shouldTransformRef === 'function'let options: ResolvedOptions = {isProduction: process.env.NODE_ENV === 'production',...rawOptions,include,exclude,customElement,refTransform,root: process.cwd(),sourceMap: true}// Temporal handling for 2.7 breaking changeconst isSSR = (opt: { ssr?: boolean } | boolean | undefined) =>opt === undefined? !!options.ssr: typeof opt === 'boolean'? opt: opt?.ssr === true// 返回插件配置return {name: 'vite:vue',handleHotUpdate(ctx) {if (!filter(ctx.file)) {return}return handleHotUpdate(ctx, options)},config(config) {return {define: {__VUE_OPTIONS_API__: true,__VUE_PROD_DEVTOOLS__: false},ssr: {external: ['vue', '@vue/server-renderer']}}},configResolved(config) {options = {...options,root: config.root,sourceMap: config.command === 'build' ? !!config.build.sourcemap : true,isProduction: config.isProduction}},configureServer(server) {options.devServer = server},async resolveId(id) {// component export helperif (id === EXPORT_HELPER_ID) {return id}// serve sub-part requests (*?vue) as virtual modulesif (parseVueRequest(id).query.vue) {return id}},// 加载vue文件load(id, opt) {const ssr = isSSR(opt)if (id === EXPORT_HELPER_ID) {return helperCode}const { filename, query } = parseVueRequest(id)// select corresponding block for sub-part virtual modulesif (query.vue) {if (query.src) {return fs.readFileSync(filename, 'utf-8')}const descriptor = getDescriptor(filename, options)!let block: SFCBlock | null | undefinedif (query.type === 'script') {// handle <scrip> + <script setup> merge via compileScript()block = getResolvedScript(descriptor, ssr)} else if (query.type === 'template') {block = descriptor.template!} else if (query.type === 'style') {block = descriptor.styles[query.index!]} else if (query.index != null) {block = descriptor.customBlocks[query.index]}if (block) {return {code: block.content,map: block.map as any}}}},// 转换文件transform(code, id, opt) {const ssr = isSSR(opt)const { filename, query } = parseVueRequest(id)if (query.raw) {return}if (!filter(filename) && !query.vue) {if (!query.vue && refTransformFilter(filename)) {if (!canUseRefTransform) {this.warn('refTransform requires @vue/compiler-sfc@^3.2.5.')} else if (compiler.shouldTransformRef(code)) {return compiler.transformRef(code, {filename,sourceMap: true})}}return}if (!query.vue) {// main requestreturn transformMain(code,filename,options,this,ssr,customElementFilter(filename))} else {// sub block requestconst descriptor = getDescriptor(filename, options)!if (query.type === 'template') {return transformTemplateAsModule(code, descriptor, options, this, ssr)} else if (query.type === 'style') {return transformStyle(code,descriptor,Number(query.index),options,this)}}}}}
2.6 整体server流程
- 运行vite server命令
- 会启动server服务,hrm websocket服务,监听文件变化
- 注册一些中间件,插件初始化
- 当用户请求一个路由,会在http中请求一个.vue文件
会触发transformMiddleware中间件,里面做了对vue文件的处理
3. 总结
perf_hooks和inspector分别是node性能工具和调试工具,后面可以在深入了解
- 通过正则方式去匹配命令行参数/^(?:-d|—debug)$/.test(arg),这种写法可以不借助minimist等参数处理库,减少了包体积
-
4. 参考文档
- github.com/vitejs/vite
- http://nodejs.cn/api/perf_hooks.html
- https://nodejs.org/api/inspector.html
