如果你已经用 Vue 写过一段时间的业务代码第一次打开微信小程序开发者工具时大概率会有一种熟悉又陌生的割裂感这边刚习惯单文件组件、热更新和 Composition API那边就要面对 WXML、手动 setData、照着 JSON 注册组件开发体验仿佛退回很多年前。Weapp-vite 和 Wevu 这两套东西就是想改变这个局面。Weapp-vite 负责把 Vite 的构建能力带进小程序工程Wevu 则负责在缺少 DOM 的运行时环境里还原 Vue 的开发模型。这篇文章我会把编译链路的每个关键环节、Wevu 运行时的核心机制、以及实际落地时踩过的坑逐一拆开讲。适合已经会用 Vue、正准备深入小程序底层原理或者对前端框架如何跨端运行感兴趣的开发者。1. 为什么值得重走一遍Weapp-vite 与 Wevu 的定位1.1 小程序开发的基础设施缺失问题先回到最根本的问题微信小程序不是普通 Web 页面。它没有浏览器 DOM逻辑层跑在 JavaScript 引擎里渲染层跑在 WebView 中两层之间只能用 setData 传数据。这意味着你在 Vue Web 项目里依赖的很多东西——直接操作 DOM、事件冒泡、运行时模板渲染——在小程序里天然不存在。小程序第一波框架潮里wepy 和 mpvue 都做过用类 Vue 语法写小程序的尝试。mpvue 当年靠的是把 Vue 2 整个 runtime 搬进小程序逻辑层让 Vue 实例在内存里正常跑再通过 setData 把视图同步到 WXML。思路没错但 Vue 2 的响应式、虚拟 DOM 和组件更新机制都是为浏览器设计的硬搬过来就会遇到性能瓶颈一次数据更新经常导致整页数据重新 setData在小程序里这是很致命的。后来的 uni-app、Taro 转向了编译时为主的路线把 Vue 模板在编译期尽量翻译成小程序原生 DSL减少运行时负担。Weapp-vite 和 Wevu 本质上也是这个思路的延续只不过它做得更彻底构建层完全基于 Vite运行时层做成一套轻量适配让 Vue 3 的 Composition API 和模板写法在小程序里尽量接近原生体验。1.2 Weapp-vite 负责编译Wevu 负责运行时把这两样东西拆开看职责其实非常清晰。Weapp-vite 解决的是构建问题怎么把一个 .vue 单文件组件、一套 Vite 工程编译成微信小程序认识的四件套JS、WXML、WXSS、JSON。它要做的事情包括解析 app.json 页面清单、拆解 SFC、把 Vue 模板指令翻译成 WXML 指令、把样式转成 WXSS、按需打包依赖。你可以把它理解成一个专门面向小程序产物格式的 Vite 插件集合。Wevu 解决的是运行时问题当代码跑在小程序逻辑层时怎么让 Vue 的响应式系统、组件实例、生命周期、事件系统真正工作起来。它要做的事情包括提供 createApp/createComponent 之类的入口、管理组件实例树、执行 render 函数产出虚拟 DOM、计算数据变更路径、最后通过 setData 同步给渲染层。按我的理解Weapp-vite 是编译时Wevu 是运行时两者合在一起才构成一套完整的跨端方案。只做编译没有运行时承接模板翻译得再漂亮也驱动不起来只做运行时没有编译期的指令映射运行时也得在 JavaScript 里做大量模板解释性能上扛不住。1.3 这篇文章适合谁读如果你是 Vue 开发者想了解用 Vue 语法写小程序背后到底发生了什么这篇文章能帮你建立整体认知。如果你已经在用 Weapp-vite / Wevu 或者类似的方案做项目文中关于 setData 调度、模板指令映射、生命周期映射的细节能帮你在排查问题时更快定位。我写的时候会尽量讲清楚为什么为什么模板要这样编译、为什么 setData 不能频繁调用、为什么虚拟 DOM 在小程序里不是用来操作 DOM 的。这些原理理解了换任何一套跨端方案你都能快速上手。2. 编译链路拆解.vue 文件如何变成小程序四件套2.1 Vite 插件如何接管 .vue 文件Vite 本身是一个通用构建器它并不认识小程序。Weapp-vite 要做的事就是通过 Vite 的插件钩子把整个构建流程重新定向到小程序产物。第一个关键问题是入口。Web 项目的入口是 index.html小程序没有这个概念它的入口是 app.json里面声明了页面路径、window 配置、tabBar 等。插件需要拦截构建入口把 app.json 当作起点读取 pages 字段拿到所有页面路径把这些页面文件挂到一个虚拟模块图上让 Vite 知道要构建哪些模块。第二个关键问题是 .vue 文件的处理。这步通常借助 vue/compiler-sfc 完成插件里注册一个 transform 钩子遇到 .vue 文件时先解析成 template、script、style 三个块然后分别交给对应的编译流程。编译后的结果不是一个文件而是一组文件插件要在输出阶段把它们写成小程序的四件套目录结构。整个链路大致是这样的读取 app.json确定页面与组件清单扫描并解析所有 .vue / .ts / .js 文件对 .vue 文件做 SFC 拆解、模板指令翻译、脚本编译把所有模块打包成一个个 JS chunk每个页面/组件输出对应的 WXML、WXSS、JSON依赖预构建与按需编译在 Vite 内部完成node_modules 里的代码会被打包进产物这跟普通 Vite 构建的最大区别是产物的组织方式不是输出 index.html 静态资源而是输出一个小程序开发者工具可以直接导入的 dist 目录。2.2 模板编译Vue 指令到 WXML 的映射模板编译是整条链路里最直观、也最繁琐的部分。Vue 模板和 WXML 都是一种声明式 DSL但语法细节完全不同编译就是把两边对齐。最核心的指令映射大概是这样Vue 模板语法WXML 对应写法说明v-if / v-else-if / v-elsewx:if / wx:elif / wx:else条件渲染一一对应比较简单v-foritem in listwx:for{{list}} wx:for-itemitem wx:keyid循环渲染注意 wx:key 的生成:titlemsgtitle{{msg}}属性绑定变成 WXML 数据绑定clickonClickbindtaponClick事件名映射click 对应 tapv-modelvaluevalue{{value}} bindinput表单双向绑定需要拆成两部分插槽保留具名插槽对应 name 属性事件映射是我实际开发中容易踩坑的地方。Vue 里 click、change、input 这些事件名到了小程序里要对应成 bindtap、bindchange、bindinput名字不一样。编译期必须维护一张事件名映射表同时还需要处理事件修饰符比如 .stop、.prevent这些小程序的 WXML 没有原生支持只能在生成的事件处理函数里手动调用对应 API 来模拟。模板编译还有一个难题动态组件和复杂指令。比如 Vue 里的 、Teleport、KeepAlive小程序没有现成对应物编译期只能枚举所有可能性生成条件渲染分支或者直接报错提示不支持。这也是为什么跨端方案对小程序不友好的 Vue 特性会有能力边界。插槽的处理在小程序里也比较特殊。Vue 的作用域插槽可以拿到子组件内部数据而小程序的插槽传递数据能力有限通常需要通过额外属性把数据绑上去编译期要完成这场数据搬运。2.3 script 与 style 的处理Composition API 与 scoped 样式脚本部分Weapp-vite 支持 Vue 3 的