wevu

Vue 3 风格的小程序运行时,包含响应式、diff+setData 与轻量状态管理
78,992 516
Install
npm install wevu
Latest Version:7.5.0
Vue:3
License:MIT
Last Updated:Oct 21, 2026
Links: GitHub  ·  npm
Maintainer: icebreaker

wevu

Vue 3 风格的小程序运行时,复用同款响应式与调度器,通过快照 diff 最小化 setData,并内置 Pinia 风格的状态管理。

特性

  • ref/reactive/computed/watch 与 nextTick 同源于 Vue 3 的响应式核心,并提供面向静态 WXML 的 useAsyncDerivation
  • defineComponent + setup 生命周期钩子(onShow/onPageScroll/onShareAppMessage 等)自动注册微信小程序 Component(在微信中可用于页面/组件)
  • 快照 diff + 去重调度,最小化 setData 体积,支持 bindModel / useBindModel 的双向绑定语法
  • 插件、app.config.globalProperties 及小程序原生选项可自由组合
  • 内置 defineStore/storeToRefs/createPinia,支持 getters、actions、订阅与补丁
  • TypeScript first,输出 ESM/CJS/types

安装

pnpm add -D wevu
# 或 npm/yarn/pnpm dlx 按需安装

快速上手:页面定义

import {
  computed,
  defineComponent,
  nextTick,
  onMounted,
  onPageScroll,
  onShareAppMessage,
  ref,
} from 'wevu'

defineComponent({
  data: () => ({ count: 0 }),
  computed: {
    doubled() {
      return this.count * 2
    },
  },
  methods: {
    inc() {
      this.count += 1
    },
  },
  watch: {
    count(n) {
      console.log('count changed', n)
    },
  },
  setup(_props, { state }) {
    const title = computed(() => `count: ${state.count}`)
    const local = ref(0)

    onMounted(() => {
      nextTick(() => console.log('reactive queue flushed'))
    })
    onPageScroll((e) => {
      console.log('scrollTop', e?.scrollTop)
    })
    onShareAppMessage(() => ({ title: title.value }))

    return { local }
  },
})
  • defineComponent 的 data 必须是函数(与 Vue 3 一致,和小程序原生对象写法不同)。原生小程序在实例化时会拷贝 data 对象以隔离实例;wevu 需要为每个实例创建独立的响应式 state/代理与快照 diff,因此必须返回新对象,避免共享引用污染与 diff 不稳定。
  • 小程序部分页面事件是“按需派发”(分享/滚动等):使用 weapp-vite 时,若你调用了 onPageScroll/onShareAppMessage/...,编译阶段会自动补齐 features.enableOnXxx = true;非 weapp-vite 场景可在 defineComponent({ features: ... }) 中手动开启。
  • 当全局存在 Component 构造器时自动注册;否则可拿到 component.__wevu_runtime 手动挂载适配器。
  • 分享/朋友圈/收藏是否触发由微信官方机制决定(例如右上角菜单/open-type="share";朋友圈通常需配合 wx.showShareMenu() 开启菜单项)。
  • 组件场景使用 defineComponent,SFC 构建产物可调用 createWevuComponent。

异步派生状态

useAsyncDerivation() 把异步结果表示为静态模板可读取的 status/value/error,默认立即加载:

import { useAsyncDerivation } from 'wevu'

const users = useAsyncDerivation(({ signal }) => fetchUsers({ signal }))
  • initial-pending 表示首次加载,refreshing 保留最近一次成功值。
  • 首次失败的 value 为 undefined;刷新失败保留旧值。加载错误进入 status/error,refresh() 本身正常完成。
  • 连续刷新只允许最新任务提交,新刷新会通过 signal 取消旧任务;旧任务不响应取消也不会阻塞等待。
  • 返回值自动跟随当前 effect scope 清理,也可幂等调用 dispose()。模板数据只包含可枚举的 status/value/error,继续经过现有序列化链路。
  • 加载函数不会自动追踪响应式依赖;它不是 Promise 版 computed(),依赖变化后需要显式 refresh()。

状态管理

旧项目升级前请阅读 Store 迁移指南。本次按 minor 发布,初始化、Setup 状态访问、重置和订阅行为仍需按指南迁移。

新代码推荐 createPinia();旧名称 createStore() 为同一个函数,StoreManager 类型继续保留。只创建实例不会激活它。

在 app.vue 中安装一次 manager:

<script setup lang="ts">
import { createPinia, use } from 'wevu'

use(createPinia())
</script>

使用 createApp() 时调用 app.use(pinia);use() 只能在 app setup 中调用。定义 Store 并在安装后的页面/组件中使用:

import { defineStore, storeToRefs } from 'wevu'

export const useCounter = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    doubled: state => state.count * 2,
  },
  actions: {
    inc() {
      this.count += 1
    },
  },
})

// 在已安装 manager 的页面/组件内使用
const counter = useCounter()
const { count, doubled } = storeToRefs(counter)
counter.$subscribe(({ type }) => console.log('mutation', type))
counter.inc()

事件派发(emit)

在 setup(props, ctx) 中可使用 ctx.emit(eventName, detail?, options?),底层调用宿主事件能力。微信和抖音使用 triggerEvent;支付宝通过 props.onXxx 回调传递事件,并保留模板监听器需要的 detail 和数据集。支付宝回调不提供微信的冒泡、捕获和跨组件传播选项。

组件边界遵循 Vue 的大小写约定:camelCase prop 与事件会统一映射为小程序 kebab-case 宿主名称,例如 maxQuantity 映射为 max-quantity,emit('quantityChange') 可由父组件的 @quantity-change 监听。update:modelValue 等带冒号事件继续沿用既有的 update-modelvalue 映射。

组件模板中的 tag、普通/函数/model prop 和监听器源码可使用 camelCase 或 kebab-case,两者会生成相同且唯一的 kebab-case 宿主名称,不会输出重复别名。运行时 emit('quantityChange') 与 emit('quantity-change') 每次都只派发一个 quantity-change 宿主事件。

SFC 中静态可识别的组件函数 prop 绑定默认会进入 setData 快照,例如 :callback="fn"、:on-save="handlers.save"。动态绑定或手写组件无法被编译器精确标记时,可设置 allowFunctionProps: true 全量放行;需要恢复严格过滤时,可设置 allowFunctionProps: false。常规父子通信仍可优先使用 emit / 小程序事件。

options 支持:

  • bubbles:事件是否冒泡(默认 false)
  • composed:事件是否可以穿越组件边界(默认 false)
  • capturePhase:事件是否拥有捕获阶段(默认 false)

与 Vue 3 不同:小程序事件只有一个 detail 载荷,不支持 emit(event, ...args) 的多参数透传。

双向绑定(bindModel / useBindModel)

在 setup(props, ctx) 中使用 ctx.bindModel();在 <script setup> 中使用 useBindModel():

import { useBindModel } from 'wevu'

const bindModel = useBindModel()
const titleModel = bindModel<string>('form.title').model({ event: 'change' })
<t-input v-bind="titleModel" />

注意:weapp-vite 模板编译目前不支持 v-bind="object" 的对象展开语法(不会生成任何属性),建议使用显式绑定。

最佳实践(适配 t-switch 这类 value + change 组件):

import { ref, useBindModel } from 'wevu'

const isActive = ref(true)
const bindModel = useBindModel({ event: 'change', valueProp: 'value' })
const onActiveChange = bindModel.model<boolean>('isActive').onChange
<t-switch :value="isActive" @change="onActiveChange" />

调度与适配

  • 更新被批量加入微任务队列;nextTick 只等待当前 JavaScript/响应式任务队列排空,不等待小程序 setData 回调或视图提交。
  • 对状态做快照 diff,只把变更路径传给 setData;宿主提交失败时,下一次响应式更新会发送完整恢复快照。
  • 可用 setData.debugPhases: true 在现有 debug 回调中关联 prepare、物理 dispatch 与 commit。合并调用按 ID 去重;callback/Promise/同步返回不等于可见视图完成,未观测边界保持 null。参见 阶段观测与测量示例。
  • weapp.wevu.preset: 'performance' 默认开启 patch、隐藏时暂停和 diagnostics: 'fallback'。首次快照收集也会记录 needsFullSnapshot;启用阶段观测后,同一 revision 可有多条日志,不能将 warning 条数视为失败或回退次数。配置覆盖见 预设说明。
  • 提供 batch/startBatch/endBatch 用于同步更新合并触发;以及 effectScope/getCurrentScope/onScopeDispose 统一管理 effect/watch 的销毁,setup() 同步阶段内创建的副作用会自动归属到实例级 scope,便于避免泄漏。

开发产物与源码调试

使用 weapp-vite 构建时,现有 --platform / weapp.platform 会自动注入编译期常量 import.meta.env.PLATFORM,取值为 weapp、alipay、tt、swan、jd、xhs 或 web。Wevu 发布包保留平台表达式,消费构建替换后即可移除非目标平台分支及构建、IDE 等元数据,无需额外裁剪配置。

能力裁剪取决于实际使用:继续从 wevu 使用具名导入,未使用的 Store、router 和 API/fetch 不会因根入口导出而强制进入产物。SFC / JSX 编译器根据 Binding Manifest 安装所需能力;普通 SFC 不依赖 JSX island 处理器,没有创建 router 时不加载首航 guard 状态机。公开动态工厂仍保留兼容能力安装,不能把编译器精简入口的测量值当成任意动态工厂的体积承诺。

直接在其他工具链中使用时,未提供构建目标会保留宿主探测;显式使用 API/fetch 后,其动态跨平台 adapter 仍保留。Web 继续使用自己的宿主桥接,不等同于 Vue DOM runtime,也不表示已支持原生 App 渲染。

wevu 默认导出压缩后的生产产物,用于降低小程序包体积。包内同时提供未压缩并带 sourcemap 的开发产物:

  • 支持 development export condition 的构建器会在开发模式下优先解析到 dist/dev/*。
  • 需要手动切换时,可以把 wevu alias 到 wevu/dev,也可以把 wevu/router alias 到 wevu/dev/router 等同名开发入口。
  • 生产构建默认仍使用 dist/*.mjs 压缩产物,避免开发产物进入正式包。

本地开发

# 从仓库根目录执行
pnpm dev --filter wevu
pnpm test --filter wevu
pnpm build --filter wevu

更多资料

  • 架构细节:packages-runtime/wevu/ARCHITECTURE.md
  • 与 Vue 3 对比:packages-runtime/wevu/VUE3_VS_WEVU.md
  • 兼容性说明:packages-runtime/wevu/VUE3_COMPAT.md

Related Packages

@weapp-vite/eslint

Wevu compatibility ESLint plugin and recommended configuration

34,024 516
@wevu/compiler

wevu 编译器基础包,面向小程序模板的编译与转换

65,143 516
@weapp-vite/volar

Volar plugin for weapp-vite - Provides IntelliSense and type checking for WeChat...

43,268 516
vue-mini-bridge-template-compiler

Vue-compatible template compiler for mini-program targets

497 0
vue-mini-bridge

Vue-compatible runtime for mini-program targets

535 0

Version History

Version Released License
7.5.0 MIT
7.4.0 MIT
7.3.0 MIT
7.2.2 MIT
7.2.1 MIT
Pre-releases (6)
Version Released License
1.0.0-alpha.5 MIT
1.0.0-alpha.4 MIT
1.0.0-alpha.3 MIT
1.0.0-alpha.2 MIT
1.0.0-alpha.1 MIT