Vue 特性兼容性
Vue Lynx 构建在官方 Vue 3 运行时核心(@vue/runtime-core)之上,因此你应该期望你的 Vue 代码可以直接运行。核心渲染路径 -- 组合式 API、单文件组件、响应式系统、模板指令 -- 与标准 Vue 的使用方式完全一致,无需任何 Lynx 特定的适配。
下面是我们已在 Lynx 上验证的 Vue 特性的逐项说明。如果存在 Lynx 特有的注意事项,会在对应位置标注。关于双线程架构底层工作原理的详细信息,请参阅理解双线程模型。
响应式系统 + 组合式函数
Vue Lynx 100% 复用了 Vue 的响应式核心(@vue/reactivity)。每个响应式 API 的行为与标准 Vue 完全一致,没有任何 Lynx 特有的注意事项或适配。
下面的示例展示了 reactive() + toRefs(),以及一个封装了响应式状态的 useStopwatch() 组合式函数:
SFC CSS 特性
普通 <style> 块、导入的 .css 文件、<style module>、<style scoped> 和 v-bind() CSS 绑定 都可以在 Lynx 上使用。
特性支持详情
:::warning <style scoped> 注意事项
:deep()、:slotted() 和 :global() 暂不支持(#164、#165)。
<style> 中的 v-bind() 需要两个配置选项,以便 Lynx 引擎识别内联样式中的 CSS 自定义属性并将其级联到后代元素:
通过 v-bind() 驱动的影响布局的属性(如 font-size)在初始渲染时可以正确应用,但在响应式更新时可能不会更新视觉效果。这是 Lynx 引擎的限制。解决方法:在元素上直接通过 :style 绑定来驱动布局属性。
:::
v-model
Vue 的 v-model 可以创建双向绑定。在组件上,子组件使用 defineModel()(Vue 3.4+)声明模型 prop,父组件通过 v-model 进行绑定。在原生 <input> 和 <textarea> 元素上,v-model 的使用方式与标准 Vue 一致,支持 .lazy、.trim 和 .number 修饰符。
该示例展示了:
- 默认模型 —
defineModel<number>()配合v-model="count"实现计数器 - 命名模型 —
defineModel('title')+defineModel('body')配合v-model:title/v-model:body - 原生输入 v-model — 在
<input>上直接使用v-model
v-model 不支持 <select>、<input type="checkbox"> 和 <input type="radio"> — Lynx 没有这些元素的原生对应实现。请改用组件级 v-model 配合自定义组件。
事件修饰符
Vue 的事件修饰符(.once、.stop、.self)在 Lynx 上可用。.prevent 作为兼容性空操作被接受——详情请见下表。
特性支持详情
Vue 的修饰符系统不适用于 :main-thread-bind* 处理函数(如 :main-thread-bindtap)。这些处理函数使用 Lynx 原生的 v-bind 语法,完全绕过了 Vue 的 v-on 事件管道——编译器不会为它们生成 onTapOnce key 或 withModifiers() 包装。请使用原生等价方案::main-thread-catchtap 用于阻止冒泡,在 worklet 内部实现 .once/.self 逻辑。
下面的示例展示了 .once、.stop 和 .self。.prevent 卡片说明了为何该修饰符不提供交互式演示:
插槽
Vue 插槽是将模板内容传递给子组件的主要组合机制。Vue Lynx 支持默认插槽、具名插槽和作用域插槽。
下面的示例展示了这三种模式:
- 默认插槽 — 将内容投射到
<Card>组件中 - 具名插槽 —
#header和#footer,带有后备内容 - 作用域插槽 —
<DataList>将每个项暴露给父组件进行自定义渲染
Provide / Inject
Vue 的 provide 和 inject API 允许祖先组件作为其所有后代组件的依赖注入器,无论组件层级有多深。这避免了通过中间组件层层传递 props。
下面的示例在根组件中提供了一个响应式的 theme ref 和一个静态的 appName 字符串。一个孙子组件注入了这两个值——中间层不需要向下传递任何内容。
Suspense
Vue 的 <Suspense> 在等待异步组件解析时显示后备内容。在 Lynx 上,<Suspense> 支持异步 setup()(<script setup> 中的顶层 await)和用于懒加载的 defineAsyncComponent。
Transition
Vue 的 <Transition> 和 <TransitionGroup> 组件在元素插入或移除时应用进入/离开动画。
<Transition> 和 <TransitionGroup> 是实验性的。请始终传递显式的 :duration prop——因为后台线程无法使用 getComputedStyle()。<TransitionGroup> 中的移动(FLIP)动画不受支持,因为 getBoundingClientRect() 不可用。
KeepAlive
Vue 的 <KeepAlive> 会缓存不活跃的组件实例,而不是销毁它们。当组件重新切换回来时,其状态会被保留。支持 include、exclude 和 max props。onActivated 和 onDeactivated 生命周期钩子会正常触发。
选项式 API
Vue 3 在组合式 API 之外还提供了选项式 API 以保持向后兼容。
默认情况下,Vue Lynx 启用了选项式 API(插件中的 optionsApi: true),但你可以禁用它以减小包体积:
下面的示例使用 defineComponent 配合 data()、computed、watch、methods 和 mounted 生命周期钩子:
v-once
v-once 仅渲染元素或组件一次,并跳过所有后续更新。它在 Vue Lynx 中无需任何配置即可使用——SFC 模板编译器会生成使用 setBlockTracking 和组件 _cache 数组的缓存查找,运行时在后续渲染时短路。
在 Lynx 中,v-once 比在浏览器中更有价值,因为缓存命中可以消除整个跨线程 op 批次。首次挂载后:
- VNode patcher 接收到相同的缓存 VNode 对象引用。
- 不会发生
patchProp调用,因此没有 op 进入缓冲区。 doFlush看到空缓冲区并完全跳过callLepusMethod——该子树的主线程永远不会被联系。
对于首次渲染后永远不需要更新的真正静态内容,使用 v-once:
v-once是比v-memo更强的保证——它无条件缓存,无需检查依赖数组。当内容真正静态时优先使用它。
v-memo
v-memo 在依赖数组未发生变化时跳过子树的重新渲染。它在 Vue Lynx 中无需任何配置即可使用——SFC 模板编译器已经生成了正确的 withMemo() 调用,运行时会在 patcher 执行之前进行短路。
在 Lynx 中,v-memo 比在浏览器中更有价值,因为缓存命中可以消除整个跨线程 op 批次,而不仅仅是 DOM diff。当依赖未变化时:
- VNode patcher 立即短路(返回相同的 VNode 对象引用)。
- 不会调用
patchProp,因此 ops 缓冲区保持为空。 doFlush发现缓冲区为空,完全跳过callLepusMethod——主线程对该子树不会有任何联系。
主要使用场景是 v-for 列表,其中每次更新只有部分项目发生变化:
对于用 JavaScript 编写的渲染函数,withMemo 可直接从 vue-lynx 导入:
Teleport
<Teleport> 支持 to="#id" 字符串选择器。暂不支持直接传入元素 ref 或非 ID 选择器。
不支持的特性
部分 Vue 内置特性尚未适配双线程原生环境: