Skip to content

ui 框架迁移指南

  • React <-> Solid
  • React <-> Vue.js
  • Solid <-> Vue.js
框架响应式原理更新粒度
React虚拟 DOM + 状态提升组件级重渲染
Solid细粒度信号 (Signals)原子级更新
Vue.js响应式对象 (Proxy)组件级 + 依赖追踪
概念ReactSolidVue.js
状态useStatecreateSignalref / reactive
副作用useEffectcreateEffectwatchEffect / watch
计算值useMemocreateMemocomputed
子组件通信propspropsprops / emits
渲染JSXJSXTemplate
特性watchEffectwatch
依赖追踪自动追踪函数内响应式依赖需显式指定监听源
执行时机立即执行一次默认不立即执行(除非 { immediate: true }
参数只有一个函数第一个参数是监听源,第二个是回调
旧值/新值无法直接获取回调提供 (newVal, oldVal)

与 React/Solid 副作用的关系

  • watchEffectuseEffect (React) / createEffect (Solid) — 自动追踪依赖
  • watchuseEffect(() => {}, [dep]) (React 手动依赖数组) — 需显式指定监听源
  • 状态useStatecreateSignal (需 count() 读取)
  • 副作用useEffect(() => {}, [dep])createEffect(() => {}) (自动追踪依赖)
  • 更新粒度:虚拟 DOM 组件级重渲染 ↔ 细粒度信号级更新
  • 编译器:React Compiler 可自动优化,Solid 原生细粒度
  • 状态useStateref/reactive (需 .value)
  • 模板:JSX ↔ Template (指令 v-if/v-for ↔ 三元/map)
  • 事件onClick@click (驼峰 ↔ 短横线)
  • 副作用useEffectwatchEffect/watch (Vue 需区分自动/手动依赖追踪)
  • context/inject:Context API (createContext) ↔ provide/inject
  • 响应式:信号 (Signals) ↔ Proxy 响应式对象
  • 语法:JSX ↔ Template
  • 依赖追踪:函数式 count().value 属性访问

React 写法:

import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

Solid 写法:

import { createSignal } from 'solid-js';
function Counter() {
const [count, setCount] = createSignal(0);
return (
<div>
<p>Count: {count()}</p>
<button onClick={() => setCount(count() + 1)}>Increment</button>
</div>
);
}

关键差异:

  • Solid 的信号是函数式访问count() 而不是 count
  • 不需要 useState,使用 createSignal
  • JSX 类似,但 Solid 没有虚拟 DOM

React:

useEffect(() => {
console.log('Count changed:', count);
}, [count]);

Solid:

createEffect(() => {
console.log('Count changed:', count());
});

注意: Solid 的 createEffect 自动追踪依赖,不需要依赖数组。

React(传统写法):

const doubled = useMemo(() => count * 2, [count]);

React(React Compiler / Forget):

// 新版 React Compiler 会自动优化,无需手动 useMemo
const doubled = count * 2; // 编译器自动处理依赖追踪

注意:React Compiler(Forget)已逐步推广,可自动将组件和 Hooks 转换为等效代码,减少手动优化需求。但 useMemo 仍兼容。

Solid:

const doubled = createMemo(() => count() * 2);
ReactSolid
useEffect(() => {}, [])onMount(() => {})
useEffect(() => { return cleanup }, [])onCleanup(() => {})
useEffect(() => { return () => {} }, [])onCleanup(() => {})

React ↔ Vue.js:

条件渲染:

React (JSX):

{show && <p>Visible</p>}

Vue.js (Template):

<p v-if="show">Visible</p>
<!-- 或 -->
<p v-show="show">Visible</p>

列表渲染:

React (JSX):

{items.map(item => (
<div key={item.id}>{item.name}</div>
))}

Vue.js (Template):

<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>

事件处理:

React (JSX):

<button onClick={handleClick}>Click</button>

Vue.js (Template):

<button @click="handleClick">Click</button>

插值:

React (JSX):

<p>{count}</p>

Vue.js (Template):

<p>{{ count }}</p>

属性绑定:

React (JSX):

<input
value={text}
onChange={e => setText(e.target.value)}
/>

Vue.js (Template):

<input :value="text" @input="text = $event.target.value" />
<!-- 或双向绑定 -->
<input v-model="text" />

React (JSX):

import { useState } from 'react';
export default function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}

Vue.js (SFC):

<script setup>
import { ref } from 'vue';
const count = ref(0);
const increment = () => count.value++;
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
ReactVue.js
useState(initial)ref(initial) / reactive(obj)
count / setCount(c)count.value
对象状态reactive 更适合

React:

// 父组件
<Child data={data} onUpdate={handleUpdate} />
// 子组件
function Child({ data, onUpdate }) {
return <button onClick={() => onUpdate()}>{data}</button>;
}

Vue.js:

<!-- 父组件 -->
<Child :data="data" @update="handleUpdate" />
<!-- 子组件 -->
<script setup>
const props = defineProps(['data']);
const emit = defineEmits(['update']);
</script>
<template>
<button @click="emit('update')">{{ props.data }}</button>
</template>

React:

useEffect(() => {
console.log('Count changed:', count);
}, [count]);

Vue.js:

<script setup>
import { watch, watchEffect } from 'vue';
// watchEffect:自动追踪依赖(类似 useEffect)
watchEffect(() => {
console.log('Count changed:', count.value);
});
// watch:显式指定监听源(类似 useEffect with dep array)
watch(count, (newVal, oldVal) => {
console.log('Count changed:', newVal, oldVal);
});
</script>

注意watchEffectuseEffect(自动追踪),watchuseEffect(() => {}, [dep])(手动指定依赖)。

Vue 的 inject 类似于 React 的 Context API,用于跨组件层级传递数据。Solid 也有 Context API(createContext/useContext)。

React (Context API):

import { createContext, useContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext('light');
// 提供者组件
function App() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
</ThemeContext.Provider>
);
}
// 消费者组件
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
}

Vue.js (Provide / Inject):

<!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue';
const theme = ref('dark');
provide('theme', theme); // 提供数据
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue';
const theme = inject('theme', 'light'); // 注入数据,默认值为 'light'
</script>
<template>
<div>Current theme: {{ theme }}</div>
</template>

对应关系:

React ContextSolid ContextVue Provide/Inject
createContext(default)createContext(default)provide(key, value)
useContext(Context)useContext(Context)inject(key, default)
Provider 组件Context.Providerprovide() 函数
需要 Provider 包裹需要 Provider 包裹不需要额外组件

注意:Solid 有 Context API(createContext/useContext),与 React 类似;Vue 使用 provide/inject


Solid (JSX):

function App() {
const show = createSignal(true);
const items = [{ id: 1, name: 'Item 1' }];
return (
<div>
{show() && <p>Visible</p>}
{items.map(item => (
<div key={item.id}>{item.name}</div>
))}
<input value={text()} onInput={e => setText(e.target.value)} />
</div>
);
}

Vue.js (Template):

<template>
<div>
<p v-if="show">Visible</p>
<div v-for="item in items" :key="item.id">{{ item.name }}</div>
<input v-model="text" />
</div>
</template>
<script setup>
import { ref } from 'vue';
const show = ref(true);
const items = ref([{ id: 1, name: 'Item 1' }]);
const text = ref('');
</script>

Solid (信号/Signals):

function App() {
const [count, setCount] = createSignal(0);
const doubled = createMemo(() => count() * 2);
return (
<div>
<p>Count: {count()}</p>
<p>Doubled: {doubled()}</p>
<button onClick={() => setCount(count() + 1)}>Increment</button>
</div>
);
}

Vue.js (响应式对象/Ref):

<script setup>
import { ref, computed } from 'vue';
const count = ref(0);
const doubled = computed(() => count.value * 2);
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubled }}</p>
<button @click="count++">Increment</button>
</div>
</template>

关键差异:

  • Solid:count() 函数式访问,createMemo 自动追踪
  • Vue.js:count.value 属性访问,computed 需显式定义

Solid:

createEffect(() => {
console.log('Count changed:', count());
});

Vue.js:

<script setup>
import { watch, watchEffect } from 'vue';
// watchEffect:自动追踪(类似 createEffect)
watchEffect(() => {
console.log('Count changed:', count.value);
});
// watch:显式指定源(类似 useEffect with deps)
watch(count, (newVal, oldVal) => {
console.log('Count changed:', newVal, oldVal);
});
</script>

Solid (Context API):

import { createContext, useContext } from 'solid-js';
const ThemeContext = createContext('light');
// 提供者组件
function App() {
return (
<ThemeContext.Provider value={'dark'}>
<Toolbar />
</ThemeContext.Provider>
);
}
// 消费者组件
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme()}</div>;
}

Vue.js (Provide / Inject):

<!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue';
const theme = ref('dark');
provide('theme', theme);
</script>
<!-- 后代组件 -->
<script setup>
import { inject } from 'vue';
const theme = inject('theme', 'light');
</script>
<template>
<div>Current theme: {{ theme }}</div>
</template>

对应关系:

Solid ContextVue Provide/Inject
createContext(default)provide(key, value)
useContext(Context)inject(key, default)
Context.Provider 组件provide() 函数
需要 Provider 包裹不需要额外组件
  1. React → Solid

  2. React → Vue

  3. Vue → React

  • 状态管理转换(useState → createSignal / ref)
  • 生命周期钩子对应(useEffect → createEffect / watch)
  • 事件处理转换(onClick → onClick / @click)
  • 条件渲染转换(三元/&& → v-if / v-show)
  • 列表渲染转换(map → v-for)
  1. React <-> Solid:语法相似,主要改状态管理和副作用,对 TS 开发者成本低
  2. React <-> Vue.js:学习曲线高(需学 HTML、JS、TS、模板语法),对只会 TS 者成本高于 React/Solid
  3. Solid <-> Vue.js:响应式模型和语法差异大,需同时理解信号和 Proxy

选择迁移时,建议先在小模块试验,验证兼容性和性能后再全面推广。

t1