全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2025-09-15_Vite 工作原理(简易版)—— 从代码看核心逻辑

您的位置:首页 >> 新闻 >> 行业资讯

Vite 工作原理(简易版)—— 从代码看核心逻辑 当我们谈论 Vite 的 “快”,很多人会联想到它基于浏览器原生 ESM 的开发模式。但具体到实现层面,Vite 是如何处理模块加载、解析单文件组件的?结合这段简易的 Vite 实现代码,我们可以清晰地看到其核心工作流程。 一、JS 加载与裸模块路径重写:让浏览器看懂模块引用浏览器原生支持 ESM(import/export),但有个严格限制:「只能识别相对路径或绝对路径」,无法直接加载 “裸模块”(如import 'vue'中的vue)。这也是 Vite 在开发阶段最核心的处理逻辑之一。 在代码中,我手动添加一个「rewriteImport」函数进行一个转换: functionrewriteImport(content) {returncontent.replace(/ from ['"](.*)['"]/g,(s1, s2) ={ // 相对路径/绝对路径不处理 if(s2.startsWith('./') || s2.startsWith('/') || s2.startsWith('../')) { return }else{ // 裸模块重写为 /@modules/xxx return` from '/@modules/${s2}'`; }}当服务器收到 JS 文件请求(如/src/main.js)时,会先读取文件内容,通过这个函数将所有裸模块引用重写。例如: 「原代码:import Vue from 'vue'」「重写后:import Vue from '/@modules/vue'」这样一来,浏览器会向服务器发起/@modules/vue的请求,而不是直接报错,为后续的裸模块加载铺路。 二、裸模块加载:找到并返回真正的依赖文件重写后的路径/@modules/vue会被服务器拦截处理,这一步的核心是「找到」「node_modules」「中对应的依赖文件」,并以浏览器可识别的 ESM 格式返回。 代码中对应的逻辑: elseif(url.startsWith("/@modules/")) {// 提取模块名(如从 /@modules/vue 中拿到 vue)constmoduleName = url.replace("/@modules/","");// 拼接 node_modules 中该模块的路径constprefix = path.join(__dirname,"node_modules", moduleName);// 读取依赖的 package.json,找到 module 字段(ESM入口)constmodulePath = require(path.join(prefix,"package.json")).module;// 读取入口文件内容constresult = fs.readFileSync(path.join(prefix, modulePath),"utf-8");// 再次重写文件内的裸模块引用(递归处理) ctx.body = rewriteImport(result);}这里的关键是利用了 npm 包中package.json的module字段 —— 现代 npm 包通常会通过该字段指定 ESM 格式的入口文件(而非 CommonJS 的main字段)。Vite 直接读取这个入口文件,并再次通过rewriteImport处理其中的依赖引用,形成完整的模块加载链。 这也是 Vite “预构建” 的简化版逻辑:通过处理裸模块路径,让浏览器能直接加载node_modules中的依赖。 三、解析 SFC(.vue 文件):拆分单文件组件的三大块Vue 的单文件组件(.vue)包含templatescriptstyle三部分,浏览器无法直接解析。Vite 通过@vue/compiler-sfc(SFC 编译器)将其拆分为可处理的模块。 代码中对.vue文件的处理分为三步: (一)处理主请求(无 type 参数)constres = compilerSFC.parse(fs.readFileSync(p,'utf-8'));// 解析SFC为ASTconstscriptContent = res.descriptor.script.content;// 提取script内容// 将 export default 改为常量,方便后续注入render函数constscript = scriptContent.replace("export default","const __script = ");// 支持 scoped: 预计算 scopeId 与样式导入consthasScoped = res.descriptor.styles&& res.descriptor.styles.some(s=s.scoped);constscopeId = hasScoped ?`data-v-${res.descriptor.id}`:'';conststyleImports = (res.descriptor.styles|| []) .map((s, i) =`import '${url}?type=style&index=${i}&lang=${s.lang ||'css'}'`) .join('\n');ctx.body=`${rewriteImport(script)} import {render as __render} from '${url}?type=template'${styleImports}${hasScoped ?`__script.__scopeId = '${scopeId}'`:''} __script.render = __render export default __script`;相比之前的版本,这里新增了样式处理的关键逻辑: 通过res.descriptor.styles检测组件是否包含样式,以及是否使用scoped属性生成scopeId(格式为data-v-xxx),用于隔离 scoped 样式自动生成样式文件的导入语句(如import 'xxx.vue?type=style&index=0'),触发样式处理流程(二)处理模板请求(type=template)elseif(query.type==='template') {consttpl = res.descriptor.template.content;// 提取template内容consthasScoped = res.descriptor.styles&& res.descriptor.styles.some(s=s.scoped);constscopeId = hasScoped ?`data-v-${res.descriptor.id}`:undefined;// 编译为render函数(带 scopeId)constrender = compilerDOM.compile(tpl, {mode:'module', scopeId }).code; ctx.body=rewriteImport(render);// 重写其中的模块引用}当模板对应的组件使用了 scoped 样式时,编译模板时会传入scopeId,让生成的render函数在创建 DOM 元素时自动添加data-v-xxx属性,确保样式只作用于当前组件。 (三)处理样式请求(type=style)这是新增的核心逻辑,用于解析style标签内容并注入页面: elseif(query.type==="style") {constindex =parseInt(query.index);conststyle = res.descriptor.styles[index];// 获取对应索引的样式块// 使用 SFC 提供的样式编译,支持 scoped 与预处理器(scss 等)constid =`data-v-${res.descriptor.id}`;constresult =awaitcompilerSFC.compileStyleAsync({ source: style.content,// 样式源代码 filename: p,// 文件名(用于错误提示) id,// 用于 scoped 样式的标识 scoped: style.scoped,// 是否为 scoped 样式 preprocessLang: style.lang// 预处理器类型(如 'scss')constcssContent = result.code;// 编译后的 CSS// 将 CSS 注入到页面 ctx.body=` const style = document.createElement('style'); style.setAttribute('type','text/css'); style.textContent = `${cssContent.replace(/`/g,'\`')}`; document.head.appendChild(style);}这段代码实现了三个关键功能: 「样式编译」:通过compilerSFC.compileStyleAsync处理 scoped 样式(自动添加data-v-xxx属性选择器)和预处理器语法(如 SCSS 转 CSS)「动态注入」:将编译后的 CSS 通过创建style标签的方式注入到页面头部「兼容性处理」:使用模板字符串转义(replace(//g, ''))避免 CSS 中的反引号破坏 JS 语法四、代码生成与模板编译:从源码到可执行代码整个流程的最终目标是「生成浏览器可直接运行的 ESM 代码」,这涉及两个关键环节: JS 代码生成:对于普通 JS 文件,通过rewriteImport重写路径后直接返回;对于.vue文件的模板编译:通过compilerDOM.compile将template转换为render函数,这一步与 Vue 的运行时编译逻辑一致,最终生成的代码会包含虚拟 DOM 创建逻辑(如createVNode)。样式处理:将style标签内容编译为标准 CSS,通过动态创建style标签注入页面,同时支持 scoped 隔离和预处理器。例如,一段带 scoped 的样式: templatedivclass="box"{{ msg }}/div/templatestylescoped.box{color: red; }/style会被处理为: 1. 模板编译后的render函数会给div添加data-v-xxx属性 2. 样式编译后变为.box[data-v-xxx] { color: red; } 3. 通过style标签注入页面,只作用于当前组件的元素 总结:简易 Vite 的核心逻辑链 从代码到浏览器运行,整个流程可以概括为: 1.入口 HTML:服务器返回index.html,浏览器解析并发起main.js请求; 2.JS 处理:重写裸模块路径,让浏览器能识别依赖引用; 3.裸模块加载:通过/@modules/路径找到node_modules中的依赖,返回 ESM 格式代码; 4.SFC 解析: 拆分.vue文件为templatescriptstyle三部分 处理脚本逻辑,生成样式和模板的导入语句 编译模板为带 scopeId 的render函数(如使用 scoped 样式) 编译样式为 CSS 并动态注入页面 5.代码生成:合并脚本、模板和样式,生成完整的组件模块; 6.浏览器执行:所有模块通过 ESM 链路加载,最终渲染页面。 这段简易手写代码虽然没有实现 Vite 的热更新、依赖预构建缓存等高级特性,但已经完整展现了其核心原理:利用浏览器原生 ESM,通过开发服务器动态处理模块引用和文件解析,跳过打包步骤,实现极速开发体验。这也是 Vite 区别于传统打包工具(如 Webpack)的根本所在。 AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding 点击"阅读原文"了解详情~ 阅读原文

上一篇:2021-11-07_朋友们,拯救“书荒”神级书单来了! 下一篇:2024-02-08_广告门春节红包封面 , 即刻领取

TAG标签:

17
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价