从 Vue 3 到 Lyt.js:无痛迁移指南
基于 Lyt.js v5.0.1 实际源码,手把手带你从 Vue 3 迁移到 Lyt.js。API 高度兼容,模板语法更简洁,迁移成本极低。
前言
如果你是一个 Vue 3 开发者,正在寻找一个更轻量、更现代的替代方案,那么 Lyt.js 值得你关注。
Lyt.js v5.0.1 是一个零依赖、纯 TypeScript 实现的前端框架,核心 8 包 gzip 仅 ~35KB(Vue 3 约 ~44KB),同时提供了 Vue 3 级别的开发体验。它不仅兼容 Vue 3 的核心 API,还额外提供了 Signal 响应式、Vapor Mode(无 VDOM 渲染)、50+ 内置 UI 组件、完整 DevTools 等能力。
本文将基于 Lyt.js 实际源码,从 API 对比、模板语法、路由迁移、状态管理、SSR 迁移等方面,提供一份完整的迁移指南。
一、API 对比:相似度有多高?
先看核心 API 的对比。你会发现,Lyt.js 的设计哲学就是"保持熟悉感,减少迁移成本"。
1.1 核心 API 对照表
| 概念 | Vue 3 | Lyt.js | 差异说明 |
|---|---|---|---|
| 创建应用 | createApp(App) | createApp(App) | 完全一致 |
| 定义组件 | defineComponent({...}) | defineComponent({...}) | 完全一致 |
| 响应式数据 | reactive() / ref() | reactive() / signal() | ref 兼容,新增 signal |
| 计算属性 | computed(() => ...) | computed(() => ...) | 完全一致 |
| 侦听器 | watch() / watchEffect() | watch() / effect() | watch 兼容,新增 effect |
| 生命周期 | onMounted() 等 | onMounted() 等 | 完全一致 |
| 挂载 | app.mount('#app') | app.mount('#app') | 完全一致 |
可以看到,核心 API 几乎完全一致。你已有的 Vue 3 知识可以直接复用。
1.2 响应式 API 全量兼容
通过 @lytjs/compat 兼容层,Lyt.js 提供了 Vue 3 响应式 API 的全量兼容:
// 这些 API 在 Lyt.js 中完全可用,无需修改任何代码
import {
ref, reactive, computed, watch, watchEffect,
shallowRef, shallowReactive, readonly,
toRef, toRefs, unref, triggerRef,
isRef, isReactive, isReadonly, isProxy,
toRaw, markRaw, nextTick,
provide, inject,
onMounted, onUpdated, onUnmounted,
onBeforeMount, onBeforeUpdate, onBeforeUnmount,
h, Fragment, defineAsyncComponent,
} from '@lytjs/compat'
1.3 Lyt.js 新增的 Signal API
除了兼容 Vue 3 的 Proxy 响应式,Lyt.js 还提供了 Signal 响应式模式,适合性能敏感场景:
import { signal, computed as computedSignal, batch } from '@lytjs/reactivity'
// Signal 模式:更轻量,细粒度更新
const count = signal(0)
const doubled = computedSignal(() => count() * 2)
// 读取用函数调用,更新用 .set()
count.set(count() + 1)
console.log(doubled()) // 4
// 批量更新
batch(() => {
count.set(1)
count.set(2)
count.set(3)
}) // 只触发一次更新
二、模板语法对比:更简洁的表达
Lyt.js 的模板语法去掉了 Vue 的 v- 前缀,让模板更接近原生 HTML,降低心智负担。
2.1 模板指令对照表
| 功能 | Vue 3 | Lyt.js | 说明 |
|---|---|---|---|
| 条件渲染 | v-if / v-else-if / v-else | if / else-if / else | 去掉 v- 前缀 |
| 列表渲染 | v-for="item in list" | each="item in list" | v-for 改为 each |
| 双向绑定 | v-model="value" | model="value" | 去掉 v- 前缀 |
| 事件绑定 | @click="handle" | on:click="handle" | @ 改为 on: |
| 属性绑定 | :class="cls" | :class="cls" | 完全一致 |
| 显示隐藏 | v-show="show" | show="show" | 去掉 v- 前缀 |
| 插槽 | <slot /> / #default | <slot /> / #default | 完全一致 |
| 组件引用 | <MyComponent /> | <MyComponent /> | 完全一致 |
| HTML 内容 | v-html="html" | html="html" | 去掉 v- 前缀 |
| 文本内容 | v-text="text" | text="text" | 去掉 v- 前缀 |
| 只渲染一次 | v-once | once | 去掉 v- 前缀 |
2.2 实际对比示例
下面是一个完整的组件迁移示例,直观感受差异:
Vue 3 版本:
<template>
<div class="app">
<h1 v-if="loading">加载中...</h1>
<div v-else>
<input v-model="searchQuery" placeholder="搜索..." />
<ul>
<li v-for="item in filteredList" :key="item.id">
{{ item.name }}
<button @click="remove(item.id)">删除</button>
</li>
</ul>
<p v-show="list.length === 0">暂无数据</p>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const searchQuery = ref('')
const list = ref([{ id: 1, name: 'Vue' }, { id: 2, name: 'React' }])
const loading = ref(false)
const filteredList = computed(() =>
list.value.filter(item => item.name.includes(searchQuery.value))
)
function remove(id) {
list.value = list.value.filter(item => item.id !== id)
}
</script>
Lyt.js 版本:
<template>
<div class="app">
<h1 if="loading">加载中...</h1>
<div else>
<input model="searchQuery" placeholder="搜索..." />
<ul>
<li each="item in filteredList" :key="item.id">
{{ item.name }}
<button on:click="remove(item.id)">删除</button>
</li>
</ul>
<p show="list.length === 0">暂无数据</p>
</div>
</div>
</template>
<script setup>
import { ref, computed } from '@lytjs/core'
const searchQuery = ref('')
const list = ref([{ id: 1, name: 'Vue' }, { id: 2, name: 'React' }])
const loading = ref(false)
const filteredList = computed(() =>
list.value.filter(item => item.name.includes(searchQuery.value))
)
function remove(id) {
list.value = list.value.filter(item => item.id !== id)
}
</script>
差异一目了然:
v-if->ifv-else->elsev-model->modelv-for->each@click->on:clickv-show->show
逻辑代码完全不变,只是模板指令更简洁了。
三、组件定义差异
3.1 Options API:data() 改为 state()
这是 Options API 唯一需要修改的地方:
// Vue 3
export default defineComponent({
data() {
return {
count: 0,
message: 'Hello Vue'
}
},
computed: {
doubled() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
}
})
// Lyt.js
export default defineComponent({
state() { // data() -> state()
return {
count: 0,
message: 'Hello Lyt'
}
},
computed: {
doubled() {
return this.count * 2 // 完全一致
}
},
methods: {
increment() {
this.count++ // 完全一致
}
}
})
只改了一个词:data -> state。 其他选项(computed、methods、watch、props、emits)完全一致。
3.2 Composition API:完全一致
// Vue 3 和 Lyt.js 的 Composition API 写法完全相同
import { ref, computed, onMounted } from '@lytjs/core'
export default defineComponent({
props: {
title: String
},
emits: ['update'],
setup(props, { emit }) {
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
emit('update', count.value)
}
onMounted(() => {
console.log('mounted!')
})
return { count, doubled, increment }
}
})
3.3 Signal 模式组件
迁移完成后,你可以选择将部分组件切换到 Signal 模式,获得更好的性能:
import { defineComponent } from '@lytjs/component'
export default defineComponent({
reactivityMode: 'signal', // 启用 Signal 模式
setup() {
// 在 Signal 模式下,状态管理更轻量
// 适合高频更新的组件(如计数器、实时数据展示)
}
})
四、路由迁移
4.1 API 几乎完全一致
Lyt.js 的路由包 @lytjs/router 提供了与 Vue Router 几乎一致的 API:
// ===== Vue Router =====
import { createRouter, createWebHistory, createHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/user/:id', component: User },
]
})
router.beforeEach((to, from, next) => {
// 导航守卫
next()
})
// ===== Lyt.js Router =====
import { createRouter, createWebHistory, createHashHistory } from '@lytjs/router'
const router = createRouter({
mode: 'history', // 注意:Lyt.js 用 mode 选项
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About },
{ path: '/user/:id', component: User },
]
})
router.beforeEach((to, from, next) => {
// 导航守卫:API 完全一致
next()
})
4.2 导航守卫对比
| 功能 | Vue Router | @lytjs/router |
|---|---|---|
| 全局前置守卫 | beforeEach | beforeEach |
| 全局解析守卫 | beforeResolve | beforeResolve |
| 全局后置钩子 | afterEach | afterEach |
| 路由独享守卫 | beforeEnter | beforeEnter |
| 组件内守卫 | onBeforeRouteLeave | 通过生命周期实现 |
| 编程式导航 | push / replace / go | push / replace / go |
| 响应式路由 | currentRoute | currentRoute |
4.3 在应用中使用
import { createApp } from '@lytjs/core'
import { createRouter, createWebHistory } from '@lytjs/router'
import App from './App.lyt'
const router = createRouter({
mode: 'history',
routes: [/* ... */]
})
const app = createApp(App)
app.use(router)
app.mount('#app')
五、状态管理迁移
5.1 Pinia -> @lytjs/store
Lyt.js 的 @lytjs/store 提供了 Pinia 风格的 API,迁移非常直观:
// ===== Pinia (Vue 3) =====
import { defineStore } from 'pinia'
const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Vue'
}),
getters: {
doubled: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
async fetchData() {
// 异步 action
}
}
})
// ===== @lytjs/store =====
import { createStore } from '@lytjs/store'
const counterStore = createStore({
name: 'counter',
state: () => ({
count: 0,
name: 'Lyt'
}),
getters: {
doubled: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
async fetchData() {
// 异步 action
}
}
})
5.2 Store 额外能力
@lytjs/store 还提供了一些实用功能:
const store = createStore({
name: 'myStore',
state: () => ({ count: 0 }),
actions: {
increment(state) { state.count++ }
}
})
// 状态订阅
store.$subscribe(({ type, key, newValue, oldValue }) => {
console.log(`${type}: ${key} changed from ${oldValue} to ${newValue}`)
})
// 状态重置
store.$reset()
// Store 销毁
store.$dispose()
// 获取 Store 实例
import { getStore } from '@lytjs/store'
const sameStore = getStore('myStore')
六、@lytjs/compat 兼容层:零成本起步
如果你不想一步到位修改所有代码,Lyt.js 提供了完整的 Vue 3 兼容层 @lytjs/compat,让你可以渐进式迁移。
6.1 兼容层覆盖范围
// @lytjs/compat 导出的所有 API
import {
// 响应式 API — 全量兼容
ref, reactive, computed, watch, watchEffect,
shallowRef, shallowReactive, readonly,
toRef, toRefs, unref, triggerRef,
isRef, isReactive, isReadonly, isProxy,
toRaw, markRaw, effect, nextTick,
// 依赖注入 — 完全兼容
provide, inject,
// 生命周期 — 完全兼容
onMounted, onUpdated, onUnmounted,
onBeforeMount, onBeforeUpdate, onBeforeUnmount,
onErrorCaptured, onRenderTracked, onRenderTriggered,
onActivated, onDeactivated, onServerPrefetch,
// 渲染函数 — 完全兼容
h, Fragment, defineAsyncComponent,
// 编译器宏 — 占位兼容
defineProps, defineEmits, withDefaults, defineExpose,
// Composition API 工具 — 占位兼容
useSlots, useAttrs, useTemplateRef,
// 应用 & 组件 — 完全兼容
createApp, defineComponent,
// 内置组件 — 完全兼容
KeepAlive, Teleport, Transition, TransitionGroup, Suspense,
} from '@lytjs/compat'
6.2 SFC 转换工具
@lytjs/compat 提供了自动化的 SFC 转换工具:
import { convertVueSfcToLyt, migrateVueFile, formatMigrationReport } from '@lytjs/compat'
// 方式一:SFC 转换
const result = convertVueSfcToLyt(vueSourceCode)
console.log(result.code) // 转换后的 Lyt.js 代码
console.log(result.warnings) // 转换警告
// 方式二:带详细分析的迁移
const report = migrateVueFile(vueSourceCode)
console.log('兼容性评分:', report.compatibilityScore) // 0-100
console.log('错误数:', report.errorCount)
console.log('警告数:', report.warningCount)
console.log('需手动修改:', report.manualFixes)
console.log(formatMigrationReport(report))
6.3 CLI 迁移工具
# 转换单个文件
npx @lytjs/compat vue-to-lyt ./src/MyComponent.vue
# 递归转换整个目录
npx @lytjs/compat vue-to-lyt ./src --recursive
# 预览模式(不实际写入文件)
npx @lytjs/compat vue-to-lyt ./src --recursive --dry-run
# 输出到指定目录
npx @lytjs/compat vue-to-lyt ./src --recursive --output ./lyt-src
七、SSR 迁移
如果你的 Vue 3 项目使用了 SSR,Lyt.js 内置了完整的 SSR 渲染器,迁移非常简单。
7.1 SSR 渲染 API
Lyt.js 提供了三种 SSR 渲染方式:
import { renderToString, renderToStream, renderToStreamGenerator } from '@lytjs/renderer/ssr'
方式一:同步字符串渲染
import { renderToString } from '@lytjs/renderer/ssr'
const html = renderToString(createVNode(App))
// 返回完整的 HTML 字符串
方式二:异步流式渲染
import { renderToStream } from '@lytjs/renderer/ssr'
const stream = renderToStream(createVNode(App))
// 返回可读流,逐步输出 HTML
方式三:异步生成器渲染
import { renderToStreamGenerator } from '@lytjs/renderer/ssr'
for await (const chunk of renderToStreamGenerator(createVNode(App))) {
process.stdout.write(chunk)
}
7.2 高级特性
Lyt.js SSR 支持以下高级特性:
- Suspense 边界:自动处理异步组件的流式渲染
- Islands Architecture(Partial Hydration):支持部分注水,提升首屏性能
- HTML 转义:内置 XSS 防护
- 自闭合标签处理:自动识别
br、hr、img等标签
7.3 元框架 LytX
如果你之前使用 Nuxt.js,可以迁移到 Lyt.js 的元框架 LytX:
// LytX 提供与 Nuxt 类似的全栈能力
// - SSR / SSG / SPA 多种渲染模式
// - 文件系统路由
// - API 路由
// - 中间件
// - 布局系统
八、构建工具迁移
8.1 Vite 配置
// vite.config.ts — 迁移前 (Vue 3)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})
// vite.config.ts — 迁移后 (Lyt.js)
import { defineConfig } from 'vite'
import lyt from '@lytjs/compiler'
export default defineConfig({
plugins: [lyt()], // 使用 Lyt.js 编译器插件
resolve: {
alias: {
'@': '/src',
},
},
})
8.2 TypeScript 支持
安装 Lyt.js VSCode 扩展,获得语法高亮、代码片段和调试支持:
- 语法高亮(
.lyt文件) - 代码片段(快速生成组件模板)
- Emmet 语法支持
- 调试集成
九、完整迁移清单
下面是一份可直接使用的迁移 Checklist:
基础迁移
- 入口文件:
import路径从vue改为@lytjs/core - 文件后缀:
.vue改为.lyt - 模板指令:
v-if->if,v-for->each,v-model->model,@click->on:click - 组件选项:
data()->state() - 构建工具:Vite 插件从
@vitejs/plugin-vue改为@lytjs/compiler
生态迁移
- 路由:
vue-router->@lytjs/router - 状态管理:
pinia/vuex->@lytjs/store - Composition API:
ref()可选迁移为signal()
进阶优化
- Signal 模式:性能敏感组件切换到
reactivityMode: 'signal' - Vapor Mode:高频更新组件使用无 VDOM 渲染
- TypeScript:安装 Lyt.js VSCode 扩展
- DevTools:启用 Lyt.js 浏览器 DevTools
批量操作命令
# 1. 安装 Lyt.js
npm uninstall vue vue-router pinia @vitejs/plugin-vue
npm install @lytjs/core @lytjs/router @lytjs/store @lytjs/compiler
# 2. 批量重命名 .vue -> .lyt
find src -name "*.vue" -exec sh -c 'mv "$0" "${0%.vue}.lyt"' {} \;
# 3. 使用迁移工具自动转换
npx @lytjs/compat vue-to-lyt ./src --recursive --dry-run
# 4. 确认无误后正式转换
npx @lytjs/compat vue-to-lyt ./src --recursive
十、迁移后的额外收益
完成迁移后,你不仅能保持原有的开发体验,还能获得以下额外收益:
10.1 更小的包体积
| 指标 | Vue 3 | Lyt.js |
|---|---|---|
| 核心 gzip | ~44KB | ~35KB |
| 运行时依赖 | 0 | 0 |
10.2 Signal 模式
对于性能敏感的组件(如实时数据面板、高频更新的列表),可以切换到 Signal 模式,获得细粒度更新能力,避免不必要的重渲染。
10.3 Vapor Mode
Lyt.js 的 Vapor Mode 已经是稳定特性(Vue 3 仍处于实验阶段),可以直接操作 DOM 而无需虚拟 DOM,性能接近原生 JavaScript。
10.4 零依赖
整个框架的运行时没有任何第三方依赖。所有能力 —— 响应式系统、虚拟 DOM、模板编译器、路由、状态管理 —— 全部用原生 TypeScript 实现。
10.5 增强模板语法
去掉 v- 前缀后,模板更接近原生 HTML,减少心智负担。一个 if 比 v-if 少打两个字符,更重要的是不需要在 HTML 和 Vue 指令之间做上下文切换。
10.6 50+ 内置 UI 组件
Lyt.js 内置了 50+ UI 组件,覆盖表单、数据展示、导航、反馈等场景,无需额外安装 Element Plus 或 Ant Design Vue。
10.7 完整的 DevTools
内置浏览器 DevTools,支持组件树查看、状态检查、性能分析、时间旅行调试等功能。
10.8 元框架 LytX
提供 SSR / SSG / SPA 全栈渲染能力,对标 Nuxt.js,无需额外搭建服务端渲染基础设施。
总结
从 Vue 3 迁移到 Lyt.js 的核心路径可以总结为三步:
- 替换导入路径:
vue->@lytjs/core,vue-router->@lytjs/router,pinia->@lytjs/store - 调整模板语法:去掉
v-前缀,v-for改为each,@click改为on:click - 修改组件选项:
data()改为state()
这三步完成后,你的项目就已经在 Lyt.js 上运行了。之后可以按需引入 Signal 模式、Vapor Mode 等高级特性。
建议的迁移策略:先完成基础迁移,验证功能正常后,再逐步引入 Signal 和 Vapor Mode。 这样可以最大程度降低迁移风险,同时逐步享受 Lyt.js 带来的性能和功能提升。
如果你有大型项目需要迁移,推荐使用 @lytjs/compat 兼容层 + vue-to-lyt CLI 工具的组合,实现渐进式、自动化的迁移过程。
Lyt.js v5.0.1 | 零依赖 | 核心 ~35KB gzip | 32 个子包 | 2,833+ 测试用例