重走 Vue 长征路 Weapp-vite:编译链路与 Wevu 运行时原理拆解

重走 Vue 长征路 Weapp-vite:编译链路与 Wevu 运行时原理拆解

书接上篇

我当时在团队里做《Vue 编译本质论》分享,正好把一些判断过程也整理了下来:为什么这么做,没选什么,以及这些取舍在小程序里到底值不值。

如果你更关心怎么上手,先看发布文会更顺:Weapp-vite:原生模式之外,多一种 Vue SFC 选择

先把边界说清:Wevu 不是 Vue 3 的搬运工

Wevu 用起来确实很像 Vue 3,但骨子里不是一回事。

对比维度 Vue 3 Wevu
运行环境 Web 浏览器 微信小程序
响应式系统 Proxy + effect Proxy + effect(同源)
渲染目标 DOM 节点 小程序页面/组件实例
渲染方式 Virtual DOM Diff → DOM API Snapshot Diff → setData
数据模型 VNode 树 纯 JS 对象快照
更新机制 异步调度 + DOM 操作 异步调度 + setData
生命周期 onMounted/onUpdated 等 映射到小程序生命周期
事件系统 DOM 事件 小程序 bind/catch 事件
SFC 编译 @vitejs/plugin-vue Weapp-vite 内置

说白了就一件事:响应式 API 长得一样,但最后数据往哪送、怎么送,完全不同

API 为什么能"几乎同写法"

refcomputedwatch 这些在 wevu 里跟 Vue 3 写法一模一样,没必要再造一套 DSL 出来。

ts 复制代码
import { computed, ref, watch } from 'wevu'

const count = ref(0)
const doubled = computed(() => count.value * 2)

watch(count, (val) => {
  console.log('count changed:', val)
})

很多团队迁过来之后第一反应不是"又要学新东西",而是"这不就是我平时写的吗,换了个宿主而已"。

渲染链路才是真正不一样的地方

Vue 3 走的是这条路:

text 复制代码
状态变化 -> effect 触发 -> 组件更新 -> VNode Diff -> DOM 操作

Wevu 走的是这条:

text 复制代码
状态变化 -> effect 触发 -> 快照 Diff -> setData -> 小程序渲染

Wevu 干的事情说穿了就是把"算出哪些东西变了"这一步尽量提前做完,等到真正调 setData 的时候,payload 已经被压到最小了。这在小程序里特别关键------大家踩过坑的都知道,setData 传多了,页面就卡,尤其是列表页。

.vue 到四件套:编译阶段干了啥

一个 MyComponent.vue 最终会变成小程序四件套:

text 复制代码
MyComponent.vue
  ├─> MyComponent.js
  ├─> MyComponent.wxml
  ├─> MyComponent.wxss
  └─> MyComponent.json

中间的流程大概是这样:先把 SFC 拆成四块------<script><template><style><json>,各自按小程序的规矩做转换,最后拼成产物。

其中 <json> 块用来声明页面或组件的配置(比如 usingComponentsnavigationBarTitleText 之类的),不过我更推荐用 definePageJson / defineComponentJson / defineAppJson 这几个编译宏来代替它------有类型提示,能跟 <script setup> 共享上下文,IDE 重构的时候也不容易漏改。<json> 块当兼容手段用没问题,但不太适合当主力。

text 复制代码
.vue 文件
  ↓
vue/compiler-sfc 解析
  ↓
┌─────────┬──────────┬─────────┬────────┐
│ <script>│<template>│ <style> │ <json> │
└────┬────┴────┬─────┴────┬────┴───┬────┘
     │         │          │        │
     ↓         ↓          ↓        ↓
  处理宏    指令映射     样式转换  配置提取
     │         │          │        │
     └─────────┴──────────┴────────┘
               ↓
         生成 .js/.wxml/.wxss/.json

增量构建的时候只处理改过的文件,HMR 能跑得比较稳也是靠这个缓存策略撑着。

defineXxxJson 宏的用法

上面提到推荐用编译宏来代替 <json> 块,这里展开说一下。defineAppJsondefinePageJsondefineComponentJson 都是编译期宏,构建时提取合并到对应的 .json 文件里,运行时零开销。写起来大概是这样:

html 复制代码
<script setup lang="ts">
  definePageJson({
    navigationBarTitleText: '首页',
    usingComponents: {},
  })
</script>

好处就是直接写在 <script setup> 里,有完整的类型推导,改字段名的时候 IDE 能帮你检查,不会出现"json 里改了但别的地方没跟上"的情况。

原生组件与插槽

.vue 里 import 原生组件之后,构建阶段会看模板里到底用没用到,用到了才往 usingComponents 里补。这样就不用手动维护那堆路径配置了,少写少错。

插槽也是类似的思路。你写的是 Vue 的 slot 语法,但输出的时候会按小程序的 slot 语义来生成。作用域插槽稍微复杂一点,背后走的是一套语义映射加代码生成,不是简单的字符串替换能搞定的。

Rolldown:收益主要体现在日常开发体感

v6 切到 Rolldown 不是为了赶时髦,就是想把开发时的等待再缩短一点。

日常能感受到的主要是三个地方:冷启动快了、改完代码后增量构建更灵敏、项目依赖多的时候不容易抽风。不是那种"跑分暴涨 300%"的故事,更像是每次都省个几百毫秒,积少成多,一天下来体感差挺多的。

为什么没走 createRenderer 这条路

@vue/runtime-corecreateRenderer 技术上能跑通,但拿来对小程序用,会发现抽象层对不上:它要求你提供一套完整的宿主节点操作接口,而小程序这边最核心的更新通道就是 setData(payload),两边的假设不太匹配。

Wevu 选了"编译到 WXML + 快照 diff + 最小 setData"这条路,优化点压在更贴近小程序实际约束的地方。不一定是最优雅的方案,但在真实业务里跑下来更稳当。

展开聊的话内容比较多,单独写了一篇:为什么没有使用 @vue/runtime-core 的 createRenderer 来实现

当前能力范围

日常开发用到的东西基本都覆盖了:v-if / v-for / v-model 这些核心指令,事件和属性绑定,SCSS/Less 和 CSS Modules,props/emits/slots/provide/inject,生命周期,常用的响应式 API,还有 TypeScript 类型推导和泛型组件。

如果你是从 Vue 3 过来的,写法上基本不用重新学,主要就是记住最后跑的不是浏览器而是小程序。

最后

感谢每一位提建议、报 bug、提 PR 的同学。


如果 Weapp-vite 帮到了你,欢迎给项目点个 Star

Happy Coding! 🚀

相关推荐
有梦想的程序星空4 分钟前
【环境配置】使用 Vue CLI 构建 Vue 项目脚手架完整指南
前端·javascript·vue.js
之歆8 分钟前
Ajax 进阶:跨域、CORS、JSONP 与请求封装实战
前端·javascript·ajax
it-102414 分钟前
抖音快手短视频去水印微信小程序/一键去水印/小程序去水印接口代码
微信小程序·小程序·php
杨先生哦15 分钟前
【2026 热端攻防系列 2/12】DOM 型 XSS 深度实战:AI 多态变形免杀 + 全维度防御
前端·人工智能·笔记·安全·web安全·xss
sugar__salt15 分钟前
前端Ajax核心原理与实战:从异步机制到接口请求全解析
前端·javascript·ajax
问心无愧051315 分钟前
ctf show web入门115
android·前端·笔记
難釋懷16 分钟前
Nginx缓冲区
前端·javascript·nginx
程序猿小泓16 分钟前
2026 前端面试全攻略:手写题、算法与计网/TS 高频考点
前端·javascript·css
JustHappy9 小时前
古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了
前端·后端·网络协议
snow@li9 小时前
SEO-文章标题:写文章时候,分类+主标题+大纲+解释 作为标题 / 不点进去也知道全文覆盖什么 / 标题即架构
前端