TL;DR
Source-to-source ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ Π΄Π»Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠΉ ΠΊΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΠΈ React-ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² (JSX/TSX) Π² Vue 3 SFC Ρ <script setup>. ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ Ρ
ΡΠΊΠΈ, JSX-ΡΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΈ ΠΎΡΠ½ΠΎΠ²Π½ΡΠ΅ React-ΠΏΠ°ΡΡΠ΅ΡΠ½Ρ. Π Π°Π±ΠΎΡΠ°Π΅Ρ ΡΠ΅ΡΠ΅Π· CLI ΠΈΠ»ΠΈ API, ΡΠΎΠΊΡΠ°ΡΠ°Ρ Π²ΡΠ΅ΠΌΡ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠΊΠΎΡΠΈΡΡΠ΅ΠΌΠ°ΠΌΠΈ.
ΠΠ²Π΅Π΄Π΅Π½ΠΈΠ΅: ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠΎΠ²ΠΎΠ³ΠΎ lock-in
Π ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΌ frontend-ΡΠ°Π·ΡΠ°Π±ΠΎΡΠΊΠ΅ ΡΠΌΠ΅Π½Π° ΡΡΠ΅ΠΊΠ° ΡΠ°ΡΡΠΎ ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΠΏΠΎΠ»Π½ΡΠΉ ΡΠ΅ΡΠ°ΠΊΡΠΎΡΠΈΠ½Π³ ΠΊΠΎΠ΄ΠΎΠ²ΠΎΠΉ Π±Π°Π·Ρ. Π’ΠΈΠΏΠΈΡΠ½ΡΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΠΈ:
- ΠΠ΅ΡΠ΅Ρ ΠΎΠ΄ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Ρ React Π½Π° Vue (ΠΈΠ»ΠΈ Π½Π°ΠΎΠ±ΠΎΡΠΎΡ)
- ΠΠ΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΡ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°ΡΡ ΠΏΡΠΎΠ΅ΠΊΡΡ Π½Π° ΡΠ°Π·Π½ΡΡ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠ°Ρ
- ΠΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΡ Ρ Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π½ΡΠΌΠΈ ΡΠ΅Ρ Π½ΠΎΠ»ΠΎΠ³ΠΈΡΠΌΠΈ
Π ΡΡΠ½ΠΎΠΉ ΠΏΠ΅ΡΠ΅Π½ΠΎΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ²:
- Π’ΡΡΠ΄ΠΎΠ΅ΠΌΠΊΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ Ρ Π²ΡΡΠΎΠΊΠΈΠΌ ΡΠΈΡΠΊΠΎΠΌ ΠΎΡΠΈΠ±ΠΎΠΊ
- Π’ΡΠ΅Π±ΡΠ΅Ρ Π³Π»ΡΠ±ΠΎΠΊΠΎΠ³ΠΎ Π·Π½Π°Π½ΠΈΡ ΠΎΠ±Π΅ΠΈΡ ΡΠΊΠΎΡΠΈΡΡΠ΅ΠΌ
- Π‘ΠΎΠ·Π΄Π°Π΅Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ Ρ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ ΠΊΠΎΠ³Π΅ΡΠ΅Π½ΡΠ½ΠΎΡΡΠΈ
Π‘ΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΡ (AI tools, regex-ΠΊΠΎΠ½Π²Π΅ΡΡΠ΅ΡΡ) ΡΠ°ΡΡΠΎ Π΄Π°ΡΡ Π½Π΅ΡΡΠ°Π±ΠΈΠ»ΡΠ½ΡΠΉ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ. ΠΡ ΠΏΡΠ΅Π΄Π»Π°Π³Π°Π΅ΠΌ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ ΡΠ΅ΡΠ΅Π· Π½Π°ΡΡΠΎΡΡΠΈΠΉ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ Ρ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠΌ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ (IR).
ΠΠ°ΠΊ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΡΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΡ
ΠΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡΠ½ΡΠΉ pipeline
// ΠΡΠΈΠΌΠ΅Ρ ΠΊΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΠΈ ΡΠ΅ΡΠ΅Π· API
import { convertCode } from "cross-framework"
const vueCode = convertCode({
from: "react",
to: "vue",
sourceCode: reactSource,
filename: "Component.tsx",
})
ΠΡΠ°ΠΏΡ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΡ:
- Parse: Babel-based ΠΏΠ°ΡΡΠΈΠ½Π³ ΠΈΡΡ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° Π² AST
- Transform: ΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ framework-agnostic IR
- Emit: Π³Π΅Π½Π΅ΡΠ°ΡΠΈΡ Vue SFC Ρ Composition API
ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΠΈ
Π€ΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΡΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ
// React input
export default function Button({ children }) {
return <button className="btn">{children}</button>
}
// Vue output
<template>
<button class="btn"><slot /></button>
</template>
<script setup>
defineProps(['children'])
</script>
Π₯ΡΠΊΠΈ ΠΈ ΡΠ΅Π°ΠΊΡΠΈΠ²Π½ΠΎΡΡΡ
| React | Vue 3 |
|---|---|
useState | ref |
useRef | ref + template ref |
useMemo | computed |
useEffect | watch/watchEffect |
ΠΡΠΈΠΌΠ΅Ρ ΡΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΠΈ:
// React
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => console.log(count), [count])
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// Vue
<template>
<button @click="count++">{{ count }}</button>
</template>
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, () => console.log(count.value))
</script>
ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΎΠ΅ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅
CLI Usage
npx cross-framework convert \
--from react \
--to vue \
--out-dir ./vue-components \
./src/components/**/*.tsx
ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Π² ΡΠ±ΠΎΡΠΊΡ
ΠΠ»Ρ ΠΏΠΎΡΡΠ΅ΠΏΠ΅Π½Π½ΠΎΠΉ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΡΡΡΠΎΠΈΡΡ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΠ΅ ΡΠ΅ΡΠ΅Π· ΠΏΠ»Π°Π³ΠΈΠ½ Π΄Π»Ρ Vite/Rollup:
// vite.config.js
import { crossFramework } from 'cross-framework/vite'
export default {
plugins: [
crossFramework({
from: 'react',
to: 'vue'
})
]
}
ΠΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΡ ΠΈ roadmap
Π’Π΅ΠΊΡΡΠΈΠ΅ constraints:
- ΠΠ΅Ρ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΏΡΠ΅ΠΎΠ±ΡΠ°Π·ΠΎΠ²Π°Π½ΠΈΡ ΠΈΠΌΠΏΠΎΡΡΠΎΠ²
- Complex HOCs ΠΌΠΎΠ³ΡΡ ΡΡΠ΅Π±ΠΎΠ²Π°ΡΡ ΡΡΡΠ½ΠΎΠΉ Π΄ΠΎΡΠ°Π±ΠΎΡΠΊΠΈ
- Context API ΡΡΠ΅Π±ΡΠ΅Ρ ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΠΎΠΉ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ
ΠΠ»Π°Π½Ρ ΡΠ°Π·Π²ΠΈΡΠΈΡ:
- ΠΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠ° Class Components β Options API
- Π’ΡΠ°Π½ΡΡΠΎΡΠΌΠ°ΡΠΈΡ React Router β Vue Router
- ΠΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΡ Redux β Pinia/Vuex
ΠΠ°ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅
Source-to-source ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΡ ΠΏΡΠ΅Π΄Π»Π°Π³Π°Π΅Ρ pragmatic approach ΠΊ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ΅ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠΎΠ²ΠΎΠΉ ΠΌΠΈΠ³ΡΠ°ΡΠΈΠΈ. Π₯ΠΎΡΡ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ Π½Π΅ ΠΏΠΎΠΊΡΡΠ²Π°Π΅Ρ 100% use cases, ΠΎΠ½:
- Π‘ΠΎΠΊΡΠ°ΡΠ°Π΅Ρ Π²ΡΠ΅ΠΌΡ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄Π° ΠΌΠ΅ΠΆΠ΄Ρ ΡΠΊΠΎΡΠΈΡΡΠ΅ΠΌΠ°ΠΌΠΈ
- ΠΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡΡΠ΅Ρ ΡΠ΅Π»ΠΎΠ²Π΅ΡΠ΅ΡΠΊΠΈΠΉ ΡΠ°ΠΊΡΠΎΡ
- ΠΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΡΠΎΡ ΡΠ°Π½ΠΈΡΡ Π±ΠΈΠ·Π½Π΅Ρ-Π»ΠΎΠ³ΠΈΠΊΡ
ΠΠ»Ρ ΡΠ»ΠΎΠΆΠ½ΡΡ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ² ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌ:
- ΠΠ°ΡΠ°ΡΡ Ρ Π°ΡΠΎΠΌΠ°ΡΠ½ΡΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ²
- ΠΠΎΡΡΠ΅ΠΏΠ΅Π½Π½ΠΎ ΡΠ°ΡΡΠΈΡΡΡΡ coverage
- ΠΠ½ΡΠ΅Π³ΡΠΈΡΠΎΠ²Π°ΡΡ Π² CI Π΄Π»Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΊΠΎΠ½ΡΠΈΡΡΠ΅Π½ΡΠ½ΠΎΡΡΠΈ
ΠΠ½ΡΡΡΡΠΌΠ΅Π½Ρ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ ΠΏΠΎΠ»Π΅Π·Π΅Π½ Π΄Π»Ρ:
- ΠΠΎΠΌΠ°Π½Π΄, ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°ΡΡΠΈΡ ΠΌΡΠ»ΡΡΠΈΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠΎΠ²ΡΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΡ
- ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² Ρ ΠΊΡΠΎΡΡ-ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠΎΠ²ΠΎΠΉ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΊΠΎΠΉ
- ΠΠ½ΡΠ΅ΡΠΏΡΠ°ΠΉΠ·-ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ² Ρ Π΄ΠΎΠ»Π³ΠΈΠΌ lifecycle
# ΠΠΎΠΏΡΠΎΠ±ΠΎΠ²Π°ΡΡ Π½Π° ΡΠ΅Π°Π»ΡΠ½ΠΎΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠ΅
npm install cross-framework
npx cross-framework convert ./your-component.tsx
ΠΠ°ΠΊ next-step β ΠΏΡΠ΅Π΄Π»Π°Π³Π°Π΅ΠΌ ΠΏΠΎΡΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°ΡΡ Ρ ΠΊΠΎΠ½Π²Π΅ΡΡΠ°ΡΠΈΠ΅ΠΉ Π΄ΠΈΠ·Π°ΠΉΠ½-ΡΠΈΡΡΠ΅ΠΌ ΠΈ ΠΏΡΠΎΠ²Π΅ΡΡΠΈ benchmark ΠΏΠΎ ΡΡΠ°Π²Π½Π΅Π½ΠΈΡ Ρ ΡΡΡΠ½ΡΠΌ ΡΠ΅ΡΠ°ΠΊΡΠΎΡΠΈΠ½Π³ΠΎΠΌ.
ΠΡΡΠΎΡΠ½ΠΈΠΊ: https://dev.to/parsajiravand/stop-rewriting-components-convert-react-to-vue-automatically-pfc