从 Vue 3 到 Lyt.js:无痛迁移指南

1,099 阅读7分钟

从 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 3Lyt.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 3Lyt.js说明
条件渲染v-if / v-else-if / v-elseif / 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-onceonce去掉 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 -> if
  • v-else -> else
  • v-model -> model
  • v-for -> each
  • @click -> on:click
  • v-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 其他选项(computedmethodswatchpropsemits)完全一致。

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
全局前置守卫beforeEachbeforeEach
全局解析守卫beforeResolvebeforeResolve
全局后置钩子afterEachafterEach
路由独享守卫beforeEnterbeforeEnter
组件内守卫onBeforeRouteLeave通过生命周期实现
编程式导航push / replace / gopush / replace / go
响应式路由currentRoutecurrentRoute

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 防护
  • 自闭合标签处理:自动识别 brhrimg 等标签

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 -> ifv-for -> eachv-model -> model@click -> on:click
  • 组件选项data() -> state()
  • 构建工具:Vite 插件从 @vitejs/plugin-vue 改为 @lytjs/compiler

生态迁移

  • 路由vue-router -> @lytjs/router
  • 状态管理pinia / vuex -> @lytjs/store
  • Composition APIref() 可选迁移为 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 3Lyt.js
核心 gzip~44KB~35KB
运行时依赖00

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,减少心智负担。一个 ifv-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 的核心路径可以总结为三步:

  1. 替换导入路径vue -> @lytjs/corevue-router -> @lytjs/routerpinia -> @lytjs/store
  2. 调整模板语法:去掉 v- 前缀,v-for 改为 each@click 改为 on:click
  3. 修改组件选项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+ 测试用例

项目地址:gitee.com/lytjs/lytjs