Template Conversion Guide
This page is based on the current implementation of compiler-core and demonstrates common and advanced conversions from Vue to React at the Template layer.
Note: Examples are simplified comparisons, and the final output is subject to local compilation results.
1. Key Rule: ref in templates automatically appends .value
In Vue templates, ref variables are usually written directly by their variable names without manually adding .value. VuReact automatically appends .value when converting template expressions.
Vue Input:
<script setup lang="ts">
import { ref } from 'vue';
const count = ref(1);
</script>
<template>
<p>{{ count }}</p>
<button @click="count++">+1</button>
</template>React Output (illustration):
<p>{count.value}</p>
<button onClick={() => count.value++; }>+1</button>2. Conditional Branches: v-if / v-else-if / v-else
Vue Input:
<template>
<div v-if="a">A</div>
<div v-else-if="b">B</div>
<div v-else>C</div>
</template>React Output (illustration):
{
a ? <div>A</div> : b ? <div>B</div> : <div>C</div>;
}Constraint: v-else / v-else-if must be adjacent to valid branches.
3. Lists: v-for
Array Traversal
Vue Input:
<li v-for="(item, i) in list" :key="item.id">{{ i }} - {{ item.name }}</li>React Output (illustration):
{
list.map((item, i) => (
<li key={item.id}>
{i} - {item.name}
</li>
));
}Object Traversal
Vue Input:
<li v-for="(val, key, i) in obj" :key="key">{{ i }} - {{ key }}: {{ val }}</li>React Output (illustration):
{
Object.entries(obj).map(([key, val], i) => (
<li key={key}>
{i} - {key}: {val}
</li>
));
}4. Events: v-on / @
Basic Events
Vue Input:
<button @click="increment">+1</button>React Output (illustration):
<button onClick={increment}>+1</button>With Modifiers (illustration)
Vue Input:
<button @click.stop.prevent="submit">Submit</button>React Output (illustration):
<button onClick={dir.on('click.stop.prevent', submit)}>Submit</button>Note: Events with modifiers use the runtime directive helper; .capture maps to onClickCapture.
5. Binding: v-bind / :prop / class / style
Regular Binding
Vue Input:
<img :src="url" :alt="title" />React Output (illustration):
<img src={url} alt={title} />Complex class/style Expressions
Vue Input:
<div :class="['card', active && 'is-active']" :style="{ color, fontSize: size + 'px' }" />React Output (illustration):
<div
className={dir.cls(['card', active && 'is-active'])}
style={dir.style({ color, fontSize: size + 'px' })}
/>Note: Complex class/style merging uses the runtime helper functions.
Keyless v-bind
Vue Input:
<button v-bind="btnProps">Go</button>React Output (illustration):
<button {...dir.keyless(btnProps)}>Go</button>Note: Keyless v-bind overrides duplicate props, and a compilation warning will be issued.
6. v-model (Partial Capabilities)
Native Inputs
Vue Input:
<input v-model="keyword" />
<input type="checkbox" v-model="checked" />React Output (illustration):
<input value={keyword.value} onInput={(value) => { keyword.value = value; }} />
<input checked={checked.value} onChecked={(value) => { checked.value = value; }} />Component v-model
Vue Input:
<CounterPanel v-model="count" />React Output (illustration):
<CounterPanel
modelValue={count.value}
onUpdateCount={(value) => {
count.value = value;
}}
/>Note: v-model semantics vary across different elements/components; always validate compiled output during integration.
7. Slots: Default Slots and Scoped Slots
Default Slots
Vue Input:
<MyPanel>
<span>Inner</span>
</MyPanel>React Output (illustration):
<MyPanel>
<span>Inner</span>
</MyPanel>Scoped Slots
Vue Input:
<ListBox>
<template #item="{ row }">
<span>{{ row.name }}</span>
</template>
</ListBox>React Output (illustration):
<ListBox item={({ row }) => <span>{row.name}</span>} />Child Component <slot> Outlet
Vue Input:
<slot name="footer" :count="count"></slot>React Output (illustration):
{
props.footer?.({ count: count.value });
}Note: Dynamic slot keys/props may trigger warnings and use a conservative conversion strategy.
8. ref Attribute: ref="x" and :ref
Vue Input:
<p ref="p"></p>
<span :ref="(el) => (domRef = el)"></span>React Output (illustration):
<p ref={pRef} />
<span ref={(el) => (domRef.value = el)} />Note: Variables bound with useTemplateRef map to the .current semantics.
9. Dynamic Components: is / :is
Vue Input:
<component :is="currentView" />React Output (illustration):
<Component is={currentView} />10. Other Directives
v-show
<div v-show="open">Content</div><div style={{ display: open ? '' : 'none' }}>Content</div>v-text
<p v-text="message"></p><p>{message}</p>v-html
<div v-html="htmlContent"></div><div dangerouslySetInnerHTML={{ __html: htmlContent }} />v-memo
<div v-memo="[id, status]"></div>{
/* Generates memo wrapping (dependency array must be analyzable) */
}11. Component Rules (Post-Template Processing)
Transitionchild nodes must meet structural requirements and are recommended to be used withv-if/v-showTransitionscenarios addkeyto ensure stable switchingRouterLink'sv-slotconverts tocustomRenderformat
12. Common Warnings and Limitations
- Unknown directives trigger warnings
- Unanalyzable Vue runtime
$xxxvariables cause errors - Dynamic prop keys/slot keys may be downgraded
- The more "analyzable" template expressions are, the more stable the conversion results
13. Built-in Component Conversion Support
Vue Built-in Components
VuReact supports conversion for the following Vue built-in components:
| Vue Component | React Equivalent | Description |
|---|---|---|
KeepAlive | KeepAlive | Caches component state |
Suspense | Suspense | Async component loading |
Teleport | Teleport | Portal component |
Transition | Transition | Transition animations |
TransitionGroup | TransitionGroup | List transitions |
Component | Component | Dynamic component container |
Vue Router Components
| Vue Router Component | React Equivalent | Description |
|---|---|---|
RouterLink | RouterLink | Route link |
RouterView | RouterView | Route view |
14. Runtime Helper Functions
During conversion, VuReact uses the following runtime helper functions:
dir.cls(className: string | Array<string | boolean>)
Handles complex class binding expressions, supporting arrays, conditional expressions, etc.
// Vue: :class="['card', active && 'is-active']"
// React: className={dir.cls(['card', active && 'is-active'])}dir.style(styleObject: object)
Handles complex style binding expressions, supporting object expressions.
// Vue: :style="{ color, fontSize: size + 'px' }"
// React: style={dir.style({ color, fontSize: size + 'px' })}dir.on(eventName: string, handler: Function)
Handles event bindings with modifiers.
// Vue: @click.stop.prevent="submit"
// React: onClick={dir.on('click.stop.prevent', submit)}dir.keyless(propsObject: object)
Handles keyless v-bind.
// Vue: v-bind="btnProps"
// React: {...dir.keyless(btnProps)}15. Compilation Process Overview
VuReact's template conversion follows these steps:
1. Parsing Phase
- Parses Vue SFC using
@vue/compiler-sfc - Separates template, script, and style sections
- Generates Vue AST
2. Transformation Phase
- Traverses Vue AST and converts to Intermediate Representation (IR)
- Processes directives, attributes, events, etc.
- Collects reactive variable references
3. Code Generation Phase
- Converts IR to Babel AST
- Generates JSX code
- Applies code formatting
4. Post-Processing Phase
- Optimizes generated JSX structure
- Adds necessary runtime imports
- Processes style and asset references
16. Special Scenario Handling
Dynamic Slot Key
<template #[dynamicSlotName]>Content</template>Dynamic slot keys trigger warnings and use a conservative conversion strategy.
Dynamic Prop Key
<component :[dynamicProp]="value" />Dynamic prop keys may not be fully analyzable, and a compilation hint will be provided.
Vue Runtime Variables
Template expressions containing Vue runtime variables in the form of $xxx will throw errors, as these variables do not exist in the React environment.
Expression Analyzability
The simpler and more statically analyzable template expressions are, the more stable the conversion results. Recommendations:
- Avoid complex chained expressions
- Avoid using Vue-specific global variables
- Use explicit variable references as much as possible
17. Compilation Warnings and Errors
Warning Levels
- Info: Informational messages that do not affect compilation
- Warning: Issues that may affect functionality but allow compilation to continue
- Error: Severe issues that prevent compilation from continuing
Common Warnings
- Unknown directives or attributes
- Structural errors (e.g., v-else without adjacent v-if)
- Unanalyzable dynamic content
- Potential runtime issues
Error Handling
- Syntax errors: Immediately stop compilation
- Type errors: Continue or stop based on configuration
- Missing runtime dependencies: Prompt users to install required packages
18. Best Practice Recommendations
Template Writing
- Keep it simple: Avoid overly complex template logic
- Explicit references: Use clear variable names to avoid ambiguity
- Clear structure: Use components and slots appropriately
Conversion Preparation
- Type checking: Ensure complete TypeScript type definitions
- Dependency management: Verify React equivalents for all used Vue features
- Test validation: Perform functional testing after conversion
Debugging Tips
- Incremental conversion: Convert simple components first, then handle complex logic
- Output comparison: Compare functional behavior before and after conversion
- Runtime checks: Pay attention to console warnings and errors
Next Steps
- See Script Conversion Guide - Learn about script conversion rules
- See Runtime Components - Learn about detailed usage of runtime components
- See Runtime Helper Functions - Learn about detailed usage of runtime helper functions
