wevu 的 Vue 3 兼容性说明
wevu 是一个面向小程序(以微信小程序为主)的 Vue 3 风格运行时:尽量提供熟悉的 Vue 3 API 形态,同时针对小程序运行环境进行适配与约束。
本文用于帮助你快速判断:
- 哪些 API 可以像 Vue 3 一样直接使用
- 哪些 API 在小程序中存在差异或仅部分兼容
- 哪些 Vue 3 能力在小程序中不适用
Vue 3 兼容性目标
wevu 的核心目标是降低 Vue 开发者进入小程序的心智成本:让你可以继续用 Composition API 组织业务逻辑,并通过快照 diff 最小化 setData 更新。
需要注意:小程序不是浏览器环境,没有 DOM / Virtual DOM;事件系统与生命周期也与 Web 存在天然差异,因此 wevu 不可能做到“100% 无差别兼容 Vue 3”。
✅ 完全兼容的 API
以下 API 与 Vue 3 的行为一致(或在 wevu 中保持同等语义),可以直接按 Vue 3 的习惯使用。
响应式(Reactivity)
reactive():创建响应式对象ref():创建 refcomputed():创建计算属性watch():监听响应式源watchEffect():自动追踪依赖的副作用toRefs():把响应式对象转换为 refstoRef():为指定属性创建 reftoRaw():获取原始对象isRef():判断是否为 refisReactive():判断是否为 reactivereadonly():创建只读包装markRaw():标记对象跳过响应式转换isRaw():判断对象是否被标记为 raw
浅层响应式(Shallow Reactivity)
shallowReactive():创建浅层响应式对象shallowRef():创建浅层 refisShallowReactive():判断是否为 shallowReactiveisShallowRef():判断是否为 shallowReftriggerRef():手动触发 ref 更新
生命周期钩子(Lifecycle Hooks)
以下钩子在 wevu 中提供与 Vue 3 对齐的调用方式(但其触发时机映射到小程序生命周期,具体差异见后文):
onMounted():组件/页面已就绪onUpdated():更新后(在setData后触发)onUnmounted():组件/页面卸载onBeforeMount():挂载前(在小程序里会立即执行,见后文)onBeforeUpdate():更新前(在setData前触发)onBeforeUnmount():卸载前(在小程序里会立即执行,见后文)onActivated():组件激活(映射onShow)onDeactivated():组件失活(映射onHide)onErrorCaptured():错误捕获
组件与调度(Component API)
defineComponent():定义组件/页面(底层通过小程序Component()注册)createApp():创建应用实例getCurrentInstance():获取当前实例nextTick():在下一次更新后执行回调
依赖注入(Dependency Injection)
provide():提供依赖inject():注入依赖provideGlobal()/injectGlobal():全局 provide/inject(已弃用,仅用于兼容/过渡)
Store(Pinia 风格)
wevu 内置了 Pinia 风格 Store,并且可以做到“无需全局注册,直接使用”:
defineStore():定义 Store(Setup/Options 两种模式)storeToRefs():从 store 提取 refscreateStore():可选的 store manager(可做插件入口)$patch:批量更新 state$reset:重置 state(仅 Options Store)$subscribe:订阅 state 变更$onAction:订阅 action 调用
关键差异:不需要全局注册
ts
// ❌ Pinia:需要全局注册
import { createPinia } from 'pinia'
// ✅ wevu:直接使用即可
import { defineStore } from 'wevu'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
return { count }
})
const pinia = createPinia()
app.use(pinia) // Pinia 必须先注册更多 Store 细节见:/wevu/store。
⚠️ 部分兼容 / 不同 API
setup() 上下文(Setup Context)
setup() 会收到增强后的 context(在 Vue 3 的 emit/expose/attrs 基础上,补充了小程序运行时相关字段):
ts
defineComponent({
setup(props, { emit, expose, attrs }) {
// Vue 3 风格:props 作为第一个参数(当组件定义了 properties 时)
// 额外的 context 字段:
// - props: 组件 properties(来自小程序)
// - emit: 通过小程序 triggerEvent(eventName, detail?, options?) 派发事件
// - expose: 暴露公共方法(必定存在)
// - attrs: attrs(必定存在;小程序场景为空对象)
// - runtime: wevu 运行时实例
// - state: 响应式状态
// - proxy: 公开实例代理
// - bindModel: 双向绑定辅助方法
// - watch: watch 辅助方法
// - instance: 小程序内部实例
},
})emit 的差异(triggerEvent 语义)
emit 是对小程序 triggerEvent 的薄封装:
emit(eventName, detail?, options?)options.bubbles(默认false):事件是否冒泡options.composed(默认false):事件是否可以穿越组件边界options.capturePhase(默认false):事件是否拥有捕获阶段
与 Vue 3 不同:小程序事件只有一个 detail 载荷,不支持 emit(event, ...args) 的多参数透传。
生命周期映射差异
小程序生命周期与 Web 有差异,wevu 会做映射:
onMounted()映射到onReadyonUnmounted()映射到页面onUnload/ 组件detachedonActivated()映射到onShowonDeactivated()映射到onHideonBeforeMount()/onBeforeUnmount()在小程序中会在setup()同步阶段立即执行,用于模拟语义
❌ 小程序不适用的 Vue 3 API
以下 API 依赖 DOM / Virtual DOM / 浏览器环境,在小程序中不适用:
h()/createElement():小程序没有 Virtual DOMTransition/KeepAlive/Teleport:Web 内置组件语义onServerPrefetch():无 SSRonRenderTracked()/onRenderTriggered():无渲染追踪
使用示例
基础组件
ts
import { computed, defineComponent, onMounted, ref } from 'wevu'
defineComponent({
data: () => ({ count: 0 }),
setup(props, { emit }) {
const count = ref(0)
const doubled = computed(() => count.value * 2)
onMounted(() => {
console.log('组件已挂载')
})
function increment() {
count.value++
emit('update', count.value)
}
return { count, doubled, increment }
},
})使用 Props
ts
defineComponent({
properties: {
title: String,
count: Number,
},
setup(props) {
console.log('title:', props.title)
console.log('count:', props.count)
return {}
},
})Watch 与 WatchEffect
ts
import { ref, watch, watchEffect } from 'wevu'
defineComponent({
setup() {
const count = ref(0)
watchEffect(() => {
console.log('Count changed:', count.value)
})
watch(count, (newValue, oldValue) => {
console.log(`Count: ${oldValue} -> ${newValue}`)
})
return { count }
},
})toRefs:解构同时保持响应式
ts
import { reactive, toRefs } from 'wevu'
defineComponent({
setup() {
const state = reactive({
count: 0,
name: 'wevu',
})
const { count, name } = toRefs(state)
count.value++
return { count, name }
},
})Provide / Inject
ts
// 父组件
defineComponent({
setup() {
const theme = ref('dark')
provide('theme', theme)
return {}
},
})
// 子组件
defineComponent({
setup() {
const theme = inject('theme', 'light')
return { theme }
},
})浅层响应式(Shallow)
ts
import { shallowReactive, shallowRef } from 'wevu'
defineComponent({
setup() {
const state = shallowReactive({
nested: { count: 0 },
})
state.nested = { count: 1 } // 会触发 effect
state.nested.count++ // 不会触发 effect
const foo = shallowRef({ bar: 1 })
foo.value = { bar: 2 } // 会触发 effect
foo.value.bar++ // 不会触发 effect
return { state, foo }
},
})markRaw
ts
import { markRaw, reactive } from 'wevu'
defineComponent({
setup() {
const classInstance = markRaw(new MyClass())
const state = reactive({
// classInstance 不会被转换为响应式
instance: classInstance,
})
return { state }
},
})API 参考与 TypeScript 支持
wevu 提供完整的 TypeScript 类型导出,你可以在项目中直接引用需要的类型:
ts
import type { ComponentPublicInstance, Ref, SetupContext } from 'wevu'从 Vue 3 迁移到 wevu(迁移要点)
- 替换导入:把
from 'vue'改为from 'wevu' - 生命周期映射:大多数钩子无需改动,但请注意
onBeforeMount/onBeforeUnmount在小程序里会立即执行 - 模板差异:使用 WXML(而非 HTML);双向绑定建议使用
bindModel()生成事件/属性绑定对象 - 组件注册:
wevu组件基于小程序Component();SFC 场景一般由构建侧(如 weapp-vite)产出注册代码
License
MIT