finyx_data_frontend/docs/代码转换快速参考.md

8.2 KiB

React 到 Vue 代码转换快速参考

🔄 常用转换模式

1. 组件定义

React

import React from 'react';

interface Props {
  title: string;
  count: number;
}

export const Component: React.FC<Props> = ({ title, count }) => {
  return <div>{title}: {count}</div>;
};

Vue 3

<template>
  <div>{{ title }}: {{ count }}</div>
</template>

<script setup lang="ts">
interface Props {
  title: string;
  count: number;
}

defineProps<Props>();
</script>

2. 状态管理

React useState

const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: '', age: 0 });

setCount(5);
setUser({ ...user, name: 'John' });

Vue ref

const count = ref(0);
const user = ref({ name: '', age: 0 });

count.value = 5;
user.value.name = 'John';

Vue reactive (对象)

const user = reactive({ name: '', age: 0 });

user.name = 'John'; // 不需要 .value

3. 副作用处理

React useEffect

useEffect(() => {
  // 副作用逻辑
  return () => {
    // 清理逻辑
  };
}, [dependency]);

Vue watchEffect

watchEffect(() => {
  // 副作用逻辑
  return () => {
    // 清理逻辑
  };
});

Vue watch

watch(() => dependency, (newVal, oldVal) => {
  // 副作用逻辑
}, { immediate: true });

4. 计算属性

React useMemo

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

Vue computed

const doubled = computed(() => {
  return count.value * 2;
});

5. 条件渲染

React

{isVisible && <Component />}
{condition ? <A /> : <B />}
{items.length > 0 && items.map(item => <Item key={item.id} />)}

Vue

<Component v-if="isVisible" />
<A v-if="condition" />
<B v-else />
<Item v-for="item in items" :key="item.id" v-if="items.length > 0" />

6. 事件处理

React

<button onClick={handleClick}>Click</button>
<input onChange={(e) => setValue(e.target.value)} />
<form onSubmit={handleSubmit}>

Vue

<button @click="handleClick">Click</button>
<input @change="handleChange" v-model="value" />
<form @submit.prevent="handleSubmit">

7. 双向绑定

React

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

Vue

<input v-model="value" />

8. 样式绑定

React

<div className={`base ${isActive ? 'active' : ''}`}>
<div className={cn('base', { active: isActive })}>
<div style={{ width: `${percent}%` }}>

Vue

<div :class="['base', { active: isActive }]">
<div :class="{ base: true, active: isActive }">
<div :style="{ width: `${percent}%` }">

9. 列表渲染

React

{items.map(item => (
  <Item key={item.id} data={item} />
))}

Vue

<Item 
  v-for="item in items" 
  :key="item.id" 
  :data="item" 
/>

10. 组件通信

React Props

<ChildComponent 
  prop1={value1} 
  prop2={value2}
  onClick={handleClick}
/>

Vue Props & Emits

<ChildComponent 
  :prop1="value1" 
  :prop2="value2"
  @click="handleClick"
/>

11. Context API

React Context

const Context = createContext();
const value = useContext(Context);

Vue provide/inject

// 提供
provide(key, value);

// 注入
const value = inject(key);

Pinia Store (推荐)

// store
export const useStore = defineStore('store', () => {
  const state = ref(0);
  return { state };
});

// 使用
const store = useStore();
store.state;

12. 生命周期

React

useEffect(() => {
  // componentDidMount
  return () => {
    // componentWillUnmount
  };
}, []);

useEffect(() => {
  // componentDidUpdate
}, [dependency]);

Vue

onMounted(() => {
  // 组件挂载后
});

onUnmounted(() => {
  // 组件卸载前
});

onUpdated(() => {
  // 组件更新后
});

13. 表单处理

React

const [form, setForm] = useState({ name: '', email: '' });

<input 
  value={form.name}
  onChange={(e) => setForm({ ...form, name: e.target.value })}
/>

Vue

<script setup>
const form = reactive({ name: '', email: '' });
</script>

<template>
  <input v-model="form.name" />
</template>

14. 条件类名

React

<div className={cn(
  'base-class',
  { 'active': isActive, 'disabled': isDisabled }
)}>

Vue

<div :class="[
  'base-class',
  { active: isActive, disabled: isDisabled }
]">

15. 动态属性

React

<div {...props}>
<img src={imageSrc} alt={altText} />

Vue

<div v-bind="props">
<img :src="imageSrc" :alt="altText" />

16. 插槽 (Slots)

React

function Layout({ children }) {
  return <div>{children}</div>;
}

Vue

<template>
  <div>
    <slot />
  </div>
</template>

具名插槽

<!-- 父组件 -->
<Layout>
  <template #header>Header</template>
  <template #default>Content</template>
  <template #footer>Footer</template>
</Layout>

<!-- 子组件 -->
<template>
  <div>
    <slot name="header" />
    <slot />
    <slot name="footer" />
  </div>
</template>

17. 图标使用

React (lucide-react)

import { FileJson, Terminal } from 'lucide-react';

<FileJson size={20} />
<Terminal size={20} className="text-blue-500" />

Vue (lucide-vue-next)

<script setup>
import { FileJson, Terminal } from 'lucide-vue-next';
</script>

<template>
  <FileJson :size="20" />
  <Terminal :size="20" class="text-blue-500" />
</template>

18. 异步数据获取

React

const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);

useEffect(() => {
  setLoading(true);
  fetchData().then(result => {
    setData(result);
    setLoading(false);
  });
}, []);

Vue

const data = ref(null);
const loading = ref(false);

onMounted(async () => {
  loading.value = true;
  data.value = await fetchData();
  loading.value = false;
});

19. 防抖/节流

React

const debouncedSearch = useMemo(
  () => debounce((value) => {
    // 搜索逻辑
  }, 300),
  []
);

Vue (使用 @vueuse/core)

import { useDebounceFn } from '@vueuse/core';

const debouncedSearch = useDebounceFn((value) => {
  // 搜索逻辑
}, 300);

20. 组件引用

React useRef

const inputRef = useRef<HTMLInputElement>(null);

<input ref={inputRef} />
inputRef.current?.focus();

Vue ref

<template>
  <input ref="inputRef" />
</template>

<script setup>
const inputRef = ref<HTMLInputElement | null>(null);

onMounted(() => {
  inputRef.value?.focus();
});
</script>

📝 常见陷阱

1. ref 值访问

// ❌ 错误
const count = ref(0);
console.log(count); // RefImpl 对象

// ✅ 正确
const count = ref(0);
console.log(count.value); // 0

2. 响应式对象

// ❌ 错误 - 解构会失去响应性
const { name } = user;

// ✅ 正确 - 使用 toRefs
const { name } = toRefs(user);

3. 数组更新

// React
setItems([...items, newItem]);

// Vue
items.value.push(newItem);
// 或
items.value = [...items.value, newItem];

4. 对象更新

// React
setUser({ ...user, name: 'John' });

// Vue (ref)
user.value = { ...user.value, name: 'John' };

// Vue (reactive)
user.name = 'John'; // 直接修改

🎯 最佳实践

  1. 优先使用 ref 处理基本类型,reactive 处理对象
  2. 使用 computed 而不是 watch 处理派生状态
  3. 使用 watchEffect 处理副作用,watch 处理特定依赖
  4. 使用 Pinia 管理全局状态,而不是 provide/inject
  5. 使用 <script setup> 语法,更简洁
  6. 合理使用 v-model 处理表单双向绑定
  7. 使用 definePropsdefineEmits 定义组件接口

📚 参考资源