第 16 卷
• 配置灵活强大:支持深度自定义构建流程,可精准控制每一个环节(如模块解析、代码分割、缓存
• 兼容性广泛:支持旧浏览器(如 IE 11),可通过 Loader 转译各类非标准模块,适配传统项目迁移
需求。
• 多场景适配:对多页面应用、Monorepo、大型企业级项目的支持更成熟,案例丰富,问题解决方
案易查找。
二十、Vite的核心优势是什么,它是如何实现快速启动的?
Vite 的核心优势集中在“极致的开发体验”,具体表现为秒级开发服务器启动、毫秒级热更新(HMR)和零配置开箱即用。其中,“快速启动”是 Vite 最直观的优势,其实现核心是颠覆了
Webpack 等传统工具“先打包再服务”的理念,采用“原生 ES 模块(ESM)+ 按需编译”的架构,彻底规避了传统工具全量打包的性能损耗。
- Vite 的核心优势详解
• 秒级开发服务器启动:无论项目大小(即使是千级模块的大型项目),开发服务器均可秒级启动,
无需等待全量打包。
• 毫秒级热更新:代码修改后,仅需编译变化的模块,无需处理依赖链,更新速度恒定,不受项目规
模影响,调试体验极佳。
• 零配置开箱即用:默认支持 React、Vue、TypeScript、Sass/Less、CSS 模块、静态资源等主流场
景,无需手动配置构建规则,新手友好。
• 高效的生产构建:基于 Rollup 打包,默认开启 Tree Shaking、代码分割、资源压缩、懒加载等优
化,产物体积更小、加载更快。
- 原生 ESM 支持:顺应前端模块化标准,直接利用浏览器原生 ESM 能力,减少构建层的转换开销。
• 丰富的内置功能:内置开发服务器、热更新、PostCSS、CSS 预处理器支持、静态资源处理等功
能,无需额外安装大量依赖。
- Vite 实现快速启动的核心原理
Vite 快速启动的核心是“不打包开发环境”,通过“原生 ESM 服务 + 按需编译”替代传统工具的“全量打包 + 服务”,具体实现步骤和原理如下:
(1)核心前提:浏览器原生 ESM 支持
现代浏览器(Chrome 61+、Firefox 60+、Safari 11.1+)已原生支持 ES 模块,可通过 <script type="module" src="xxx.js"></script> 直接加载 ESM 模块,并自动解析模块中的
import 语句,发起依赖请求。Vite 正是基于这一浏览器特性,彻底规避了开发环境的打包步骤。
(2)开发环境:按需编译 + 原生 ESM 服务
- 入口文件处理:浏览器请求项目入口文件(如 index.html)时,Vite 会修改 HTML 中的脚本
标签,添加 type="module" 属性(如 <script type="module"
src="/src/main.js"></script>),让浏览器以 ESM 方式加载入口模块。
- 依赖模块按需编译与转发:
- 当浏览器解析入口模块中的 import 语句(如 import Vue from 'vue')时,会向开发
服务器发起依赖请求(如 /node_modules/vue/dist/vue.esm-browser.js);
- Vite 拦截该请求后,会对模块进行必要的编译(如将 CommonJS 模块转为 ESM、处理 CSS 预处
理器、转译 TypeScript),但仅编译当前请求的模块,不处理其他未被请求的模块;
- 编译完成后,Vite 将模块以原生 ESM 格式返回给浏览器,浏览器继续解析该模块的依赖,重
复“请求 → 编译 → 返回”的流程。
关键优势:启动时无需解析所有模块和构建依赖图,仅处理当前请求的模块,因此启动时间与项目大小无关,即使是大型项目也能秒级启动。
(3)依赖预构建:优化第三方依赖加载
虽然 Vite 开发环境不打包项目代码,但会对第三方依赖(如 node_modules 中的 Vue、React)进行“预构建”,进一步提升启动和加载速度:
• 预构建的原因:第三方依赖通常存在以下问题,影响加载效率:
- 多数第三方依赖是 CommonJS 格式(如 lodash),浏览器无法直接解析,需实时转换为 ESM,
性能损耗大;
- 第三方依赖可能包含大量小模块(如 React 生态的众多子包),会导致浏览器发起大量并发请
求,产生“网络瀑布”问题。
• 预构建的流程:
- Vite 启动时,扫描项目的第三方依赖,将 CommonJS 格式的依赖转换为 ESM 格式;
- 将多个小模块合并为一个“预构建包”(如将 React 及其依赖合并为一个文件),减少浏览器请
求数量;
- 预构建结果缓存到 node_modules/.vite 目录,后续启动时直接复用,仅当依赖版本变化
或配置修改时重新预构建。
(4)对比传统工具:规避全量打包的性能损耗
传统工具(如 Webpack)开发环境的流程是“全量解析所有模块 → 构建完整依赖图 → 打包为 bundle → 启动服务器提供 bundle”。对于大型项目,这一过程需要解析千级以上模块,构建时间长
达数十秒;而 Vite 无需打包,仅按需处理当前请求的模块,启动时间不受项目大小影响,因此实现了快速启动。
(5)补充:生产环境的打包优化
- 快速启动原理总结
Vite 快速启动的核心是“借助浏览器原生 ESM 能力,将开发环境的打包过程从‘提前全量’改为‘按需实时’”,配合第三方依赖预构建和缓存优化,彻底规避了传统工具全量打包的性能损耗,最终实
现“启动时间与项目大小无关”的秒级启动体验。
二十一、解释Vite中的插件系统,它如何工作?
Vite 的插件系统是其核心扩展机制,基于 Rollup 插件接口设计(同时扩展了适配 Vite 特有场景的能力),用于解决开发和构建过程中的各类需求(如资源处理、功能增强、流程优化等)。与 Webpack
插件基于生命周期钩子不同,Vite 插件更轻量化,可同时作用于开发环境(Dev Server)和生产环境(Rollup 打包),实现“一套插件,双环境复用”。
- 插件系统的核心特点
• 兼容 Rollup 插件:Vite 生产环境基于 Rollup 打包,因此大部分 Rollup 插件可直接在 Vite 中使用
(无需额外适配),大幅扩展了生态范围。
• 扩展 Vite 特有钩子:在 Rollup 插件接口基础上,新增了针对 Vite 开发环境的特有钩子(如处理
Dev Server 相关逻辑),满足开发环境的特殊需求。
• 插件优先级明确:通过 enforce 选项指定插件执行顺序( pre 优先执行、默认普通优先级、
post 最后执行),避免插件间冲突。
• 轻量化设计:插件逻辑专注于“拦截-处理-返回”的管道式流程,无需关注复杂的生命周期管理,
开发成本低。
- 插件的核心类型与工作流程
Vite 插件本质是一个包含特定钩子的对象,核心工作流程是“拦截模块请求 → 处理模块内容/路径 → 返回处理结果”,可分为两类核心场景:
(1)通用场景(兼容 Rollup)
借助 Rollup 插件的核心钩子(如 resolveId、 load、 transform),处理模块的解析、加载和转换,同时作用于开发和生产环境:
- resolveId:拦截模块路径,自定义模块解析规则(如别名映射、虚拟模块创建)。
- load:根据解析后的模块 ID,加载模块内容(如读取本地文件、生成虚拟内容)。
- transform:转换已加载的模块内容(如代码转译、替换字符串、注入逻辑)。
全站分卷索引 · 26 卷
- 1第 1 卷
- 深入解析:npm 安装包及四大包管理工具对比
- 一、npm 安装包的完整过程
- ◦ 先检查项目根目录的 package.json 文件,读取目标包的版本约束(如 ^1.2.3、
- ◦ 结合 package-lock.json(npm 5+ 新增)或 npm-shrinkwrap.json,优先锁定已
- ◦ 更新 package-lock.json:记录本次安装的所有包的精确版本、依赖关系、下载地址等信
- ◦ 执行生命周期脚本:若包自身或项目中配置了 preinstall、 install、
- • 性能排序(安装速度+磁盘占用):pnpm > yarn > cnpm > npm(最新版 npm 已优化,差距缩
- • 网络优化:cnpm 依赖国内镜像,npm/yarn/pnpm 可手动配置镜像,pnpm 缓存跨项目复用更
- 三、pnpm 存储结构减少磁盘占用+提高安装速度的原理
- • 全局共享存储目录:pnpm 会在系统中创建一个全局共享存储目录(默认路径:Windows
- • 硬链接复用包内容:硬链接是文件系统层面的特性,它不复制文件本身,仅创建一个指向原文件数
- • 符号链接组织依赖结构:pnpm 不采用扁平依赖结构,而是通过符号链接在项目
- 2第 2 卷
- 3第 3 卷
- 4第 4 卷
- • 使用 ESBuild 替代 Babel/Terser(可选):
- 总结
- 六、loader和plugin的区别及其在Webpack中的作用
- • 定位不同:loader 专注于“模块转换”,解决非 JS/JSON 模块的加载与转译问题;plugin 专注
- • 工作时机不同:loader 仅在“编译阶段”(解析模块时)执行,针对单个模块进行处理;plugin
- • 使用方式不同:loader 配置在 module.rules 中,通过 test 匹配目标文件,指定对应的
- • loader的作用:将非 JS/JSON 模块转换为 Webpack 可识别的模块,以便纳入依赖图谱进行打
- • plugin的作用:扩展 Webpack 的构建能力,覆盖从初始化配置到输出文件的全流程,实现
- 七、如何使用Webpack来处理CSS和Sass
- • 步骤1:安装依赖:
- • 步骤2:Webpack配置:
- • 生产环境(提取为单独 CSS 文件):
- 5第 5 卷
- 6第 6 卷
- 7第 7 卷
- 8第 8 卷
- • 排除无需处理的文件:通过 exclude 配置排除 node_modules、 dist 等目录,避免
- • 使用 thread-loader 开启多线程构建:将耗时的 loader 任务(如 Babel 转译、Sass 转译)分配到
- • 禁用生产环境相关优化(开发环境):开发环境无需启用代码压缩( TerserPlugin)、CSS 提
- 补充总结
- • 依赖静态分析:ES 模块的导入导出是静态的(导入导出语句只能在模块顶层,且不能动态判
- • 删除死代码:在代码压缩阶段(如 TerserPlugin),将标记的死代码删除,最终生成精简的
- • 代码必须使用 ES 模块( import/export):避免使用 CommonJS 模块( require 是动态
- • 指定 mode: 'production':Webpack 在生产环境下会自动启用 Tree Shaking(内置
- • 优化 usedExports(标记未使用导出):显式标记未被使用的导出,增强 TerserPlugin 的删除效
- • 通过 package.json 配置 sideEffects(排除有副作用的模块):
- 9第 9 卷
- 10第 10 卷
- • HMR 体验:在 React 项目中修改某个组件的样式或逻辑,页面无刷新,组件样式/逻辑实时更新,
- • environment:Webpack 环境准备完成后触发(如 Node.js 环境变量配置),插件可在此阶段获
- • entryOption:在 Webpack 解析入口配置( entry)后触发,可用于修改入口配置(如动态添
- • afterResolvers:解析器(resolver,用于查找模块路径)初始化完成后触发,可用于自定义模块
- • beforeRun:在 Compiler 开始执行编译前触发(仅在首次构建和 watch 模式下重新构建时触
- • watchRun:仅在 watch 模式下(文件变化触发重新构建)触发,在编译开始前执行,可用于监听
- • compilation:当一个新的 Compilation 实例(负责模块解析、依赖收集、生成 chunk)被创建时
- • normalModuleFactory:普通模块工厂(用于创建普通模块,如 JS 模块)初始化时触发,可用
- • contextModuleFactory:上下文模块工厂(用于创建上下文模块,如
- • moduleAsset:当一个模块生成对应的资源(如图片、CSS 文件)时触发,可用于跟踪模块与资
- • shouldEmit:在输出文件前触发,返回 boolean 值,决定是否输出文件(返回 false 可终止
- • emit:在输出文件到磁盘前触发,是插件修改输出资源的关键钩子(如 TerserPlugin 在此阶
- 11第 11 卷
- 12第 12 卷
- 13第 13 卷
- 14第 14 卷
- • 重复编译未变化模块:开发环境中未启用缓存,每次代码修改后都全量重新编译,而非仅编译变化
- • 资源处理低效:
- • 配置不合理:
- • 项目本身复杂度高:模块数量庞大(千级以上)、依赖链过深、存在大量动态依赖,导致
- • Webpack 内置缓存(Webpack 5+):配置 cache 选项,将模块缓存到内存或磁盘,重启开发
- • Loader 缓存:为耗时 Loader(如 babel-loader、 sass-loader)启用缓存,避免重复转
- • 预编译第三方依赖(DllPlugin):第三方依赖(如 React、Vue)极少变化,通过 DllPlugin
- • 排除无需处理的文件:通过 exclude 排除 node_modules、 dist 等目录,减少
- • 开启多线程构建:使用 thread-loader 将耗时的 Loader 任务分配到多个线程并行执行,充分
- 15第 15 卷
- • 优化静态资源处理:
- • 开发环境:禁用生产优化:开发环境核心目标是“快速构建”,无需启用代码压缩、CSS 提取等耗
- • 生产环境:精准优化:生产环境核心目标是“产物优化”,但需避免冗余配置:
- • 移除无用插件:检查插件列表,移除开发环境中不需要的生产插件(如 TerserPlugin、
- • 使用高效插件替代:
- • 拆分大型项目(Monorepo):对于大型项目,使用 Webpack 工作区(Workspaces)或
- • 减少动态依赖:尽量避免 require(`./${filename}`) 等动态依赖,改用静态导入,减少
- • 按需加载第三方依赖:对于体积大的第三方库(如 lodash),使用按需导入(如 import {
- • 开发体验极致:秒级启动开发服务器,毫秒级热更新,彻底解决大型项目开发时“启动慢、更新
- • 零配置开箱即用:默认支持 React、Vue、TypeScript、CSS 预处理器(Sass/Less)、静态资源等
- • 生产环境产物优化:基于 Rollup 打包,默认开启 Tree Shaking、代码分割、资源压缩等优化,产
- • 原生 ESM 支持:顺应前端模块化发展趋势,直接使用浏览器原生 ESM 能力,减少打包层的性能损
- 16第 16 卷
- • 配置灵活强大:支持深度自定义构建流程,可精准控制每一个环节(如模块解析、代码分割、缓存
- • 兼容性广泛:支持旧浏览器(如 IE 11),可通过 Loader 转译各类非标准模块,适配传统项目迁移
- • 多场景适配:对多页面应用、Monorepo、大型企业级项目的支持更成熟,案例丰富,问题解决方
- • 秒级开发服务器启动:无论项目大小(即使是千级模块的大型项目),开发服务器均可秒级启动,
- • 毫秒级热更新:代码修改后,仅需编译变化的模块,无需处理依赖链,更新速度恒定,不受项目规
- • 零配置开箱即用:默认支持 React、Vue、TypeScript、Sass/Less、CSS 模块、静态资源等主流场
- • 高效的生产构建:基于 Rollup 打包,默认开启 Tree Shaking、代码分割、资源压缩、懒加载等优
- • 丰富的内置功能:内置开发服务器、热更新、PostCSS、CSS 预处理器支持、静态资源处理等功
- • 预构建的原因:第三方依赖通常存在以下问题,影响加载效率:
- • 预构建的流程:
- • 兼容 Rollup 插件:Vite 生产环境基于 Rollup 打包,因此大部分 Rollup 插件可直接在 Vite 中使用
- • 扩展 Vite 特有钩子:在 Rollup 插件接口基础上,新增了针对 Vite 开发环境的特有钩子(如处理
- 17第 17 卷
- 18第 18 卷
- • changeOrigin:可选,默认 false。开启后,代理会修改请求头中的 Origin 字段为
- • rewrite:可选,路径重写函数。用于修改请求路径前缀(如前端用 /api 前缀标识接口请
- • secure:可选,默认 true。若目标服务器是 HTTPS 协议,且证书不被信任(如本地开发环
- • ws:可选,默认 false。开启后支持 WebSocket 代理(如后端有 WebSocket 服务时启
- • 代理仅作用于开发环境:Vite 的 server.proxy 是 Dev Server 的配置,生产环境打包后不生
- • 路径匹配规则:代理规则的键是请求路径的前缀,若多个规则匹配,优先匹配更具体的前缀(如
- • WebSocket 代理:若后端有 WebSocket 服务(如实时消息),需开启 ws: true,示
- 19第 19 卷
- • 模块格式不兼容:多数第三方依赖是 CommonJS 格式(如 lodash、moment),而 Vite 开发环
- • 大量小模块导致网络瀑布:部分依赖(如 React 生态、lodash-es)包含大量小模块(单个功能一
- • 依赖路径复杂:第三方依赖内部的路径引用可能包含别名、条件导入等,直接加载会导致解析失
- • 缓存存储位置:预构建结果缓存到项目根目录的 node_modules/.vite 文件夹中,包含转换
- • 缓存失效条件:仅当以下条件发生变化时,才会重新预构建,否则直接复用缓存:依赖版本变化
- • 必要依赖识别:Vite 会扫描项目的入口模块(如 src/main.js)及其直接/间接依赖,识别出需
- • 自定义预构建范围:通过 vite.config.js 的 optimizeDeps.include 和
- • Vite 内部使用 esbuild(基于 Go 语言的构建工具)进行依赖预构建, esbuild 本身支持并
- • 对于多个独立的第三方依赖(如 vue 和 axios),Vite 会并行触发 esbuild 处理,避免串
- • 例如: lodash-es 包含数百个小模块(如 lodash-es/map.js、 lodash-
- • 合并策略:Vite 会根据依赖的内部依赖关系,智能合并关联紧密的模块,避免合并后的包体积过
- • 多格式支持:原生支持 CommonJS → ESM 的转换、JSX 转译、TypeScript 转译等,无需额外配
- 20第 20 卷
- • Tree Shaking:自动移除未被使用的代码(死代码),仅保留被引用的模块和代码片段(基于
- • 代码分割(Code Splitting):自动拆分第三方依赖(如 vue、 axios)为独立 chunk(如
- • 对动态导入的模块(如 import('./pages/About.vue')),自动拆分为异步 chunk,实现
- • 开发环境:以“原生 ESM 支持”为核心,直接将静态资源作为模块加载,无需打包,确保开发体
- • 生产环境:以“优化输出”为核心,对静态资源进行压缩、哈希命名、按需转换(如小图片转
- • 零配置支持:默认支持常见静态资源类型( png、 jpg、 gif、 svg、 woff、 mp4
- • 开发环境:直接通过 import 或 url() 加载图片,Vite Dev Server 实时返回图片资源,支持
- • 小图片转 Base64:默认将小于 4KB 的图片转换为 Base64 编码,嵌入到 JS/CSS 中,减少 HTTP
- • SVG 特殊处理:支持直接导入 SVG 作为组件(需配合 @vitejs/plugin-vue 等框架插件),
- • 处理逻辑与图片类似:开发环境直接加载,生产环境自动压缩、哈希命名、输出到
- • 示例: // CSS 中引用字体
- • 开发环境:直接通过 import 或 <audio>/<video> 标签加载,Vite Dev Server 提供资源服
- 21第 21 卷
- 22第 22 卷
- • targets:决定转译的程度,目标环境越旧,转译后的代码兼容性越好,但体积越大。可通过
- • 语法转译插件:负责将 ES6+ 语法转为 ES5(如 @babel/plugin-transform-arrow-
- • 语法提案插件:负责转译尚未成为 ES 标准的语法(如 @babel/plugin-proposal-class-
- • 工具类插件:负责辅助转译过程(如 @babel/plugin-transform-runtime 复用辅助代码,
- • @babel/preset-env:核心预设,用于转译 ES6+ 语法,可根据目标环境按需加载插件(最常
- • @babel/preset-react:用于转译 React 的 JSX 语法,包含 @babel/plugin-
- • @babel/preset-es2015(已废弃):早期用于转译 ES2015 语法,现被
- • 预设按配置顺序从右到左执行(与插件相反)。例如上述配置中,先执行 @babel/preset-
- • 原因:预设是“插件集合”,右侧预设(如 TypeScript 转译)的输出可能需要左侧预设(如 ES6+
- 23第 23 卷
- • runtime: "automatic"(React 17+ 推荐):自动导入 react/jsx-runtime 模块中的 jsx
- • runtime: "classic"(旧版 React 16-):转译为 React.createElement 函数调用,需手动
- • 属性名转换:JSX 中的 className 转译为 className(避免与 JavaScript 关键字 class
- • 事件名转换:JSX 中的驼峰式事件名(如 onClick)保持不变,转译为函数调用的属性(如
- • 子节点处理:JSX 中的子元素(文本、其他 JSX 元素)会作为 children 参数传入 jsx 或
- • Fragment 处理:空标签 <></>(React Fragment)会转译为 jsx(React.Fragment,
- 24第 24 卷
- 25第 25 卷
- 26第 26 卷