Counter Component
This tutorial demonstrates a standard workflow: compiling a Vue 3 Single-File Component (SFC) into a React component.
Since there is no online Playground available at present, this page adopts:
- Side-by-side comparison of code before and after generation
- Directory tree structure diagram
Tutorial Objectives
Upon completion, you will clearly understand three key points:
- Under what conventions input SFCs can be stably converted
- The structure of the compiled directory
- The semantic correspondence between the output TSX and the original SFC
Step 0: Prepare the Directory
First, set up a minimal project (illustrative):
txt
my-app/
├─ src/
│ ├─ components/
│ │ └─ Counter.vue
│ ├─ main.ts
│ └─ index.css
├─ package.json
└─ vureact.config.jsStep 1: Write the Input SFC
src/components/Counter.vue
vue
<template>
<section class="counter-card">
<h2>{{ title }}</h2>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
<button @click="methods.decrease">-1</button>
</section>
</template>
<script setup lang="ts">
// @vr-name: Counter (Note: Tells the compiler what component name to generate)
import { computed, ref } from 'vue';
// Component name can also be defined using macros
defineOptions({ name: 'Counter' });
const step = ref(1);
const count = ref(0);
const title = computed(() => `Counter x${step.value}`);
const increment = () => {
count.value += step.value;
};
const methods = {
decrease() {
count.value -= step.value;
},
};
</script>
<style lang="less" scoped>
@border-color: #ddd;
@border-radius: 8px;
@padding-base: 12px;
.counter-card {
border: 1px solid @border-color;
border-radius: @border-radius;
padding: @padding-base;
}
</style>Step 2: Configure the Compiler
vureact.config.js
js
import { defineConfig } from '@vureact/compiler-core';
export default defineConfig({
input: 'src',
// Key: Exclude the Vue entry file to avoid entry semantic conflicts
exclude: ['src/main.ts'],
output: {
workspace: '.vureact',
outDir: 'dist',
// Disable environment initialization for tutorial purposes to easily inspect pure compilation output
bootstrapVite: false,
},
format: {
enabled: true, // Enable formatting (this will increase compilation time).
formatter: 'prettier',
},
});Step 3: Execute Compilation
bash
npx vureact buildStep 4: Inspect the Output Directory Tree
Compiled directory (illustrative):
txt
my-app/
├─ .vureact/
│ ├─ cache/
│ │ └─ _metadata.json
│ └─ dist/
│ └─ src/
│ └─ components/
│ ├─ Counter.tsx
│ └─ counter-<hash>.css
├─ src/
│ └─ ...
└─ vureact.config.jsStep 5: Compare the Generated Results
Below is a typical formatted output (slightly simplified for explanation; actual hashes and property names are subject to local build artifacts):
tsx
import { memo, useCallback, useMemo } from 'react';
import { useComputed, useVRef } from '@vureact/runtime-core';
import './counter-a1b2c3.css';
// Components are always wrapped with memo
const Counter = memo(() => {
// ref/computed are converted to equivalent adaptation APIs
const step = useVRef(1);
const count = useVRef(0);
const title = useComputed(() => `Counter x${step.value}`);
// Automatically analyze dependencies of top-level arrow functions and add useCallback optimization
const increment = useCallback(() => {
count.value += step.value;
}, [count.value, step.value]);
// Automatically analyze dependencies in top-level objects and add useMemo optimization
const methods = useMemo(
() => ({
decrease() {
count.value -= step.value;
},
}),
[count.value],
);
return (
<>
<section className="counter-card" data-css-a1b2c3>
<h2 data-css-a1b2c3>{title.value}</h2>
<p data-css-a1b2c3>Count: {count.value}</p>
<button onClick={increment} data-css-a1b2c3>
+1
</button>
<button onClick={methods.decrease} data-css-a1b2c3>
-1
</button>
</section>
</>
);
});
export default Counter;CSS file content:
css
.counter-card[data-css-a1b2c3] {
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
}Key Observations
- The special comment
// @vr-name: Counterdefines the component name - Components are wrapped with
memoby default ref/computedare converted to runtime adaptation APIs (useVRef/useComputed)- Template event callbacks are generated as React-semantic
onClick - Dependencies of top-level arrow functions are automatically analyzed and
useCallbackis added for optimization - Dependencies in top-level objects are automatically analyzed and
useMemois added for optimization .valueis appended to originalrefstate values in JSX- Less styles are compiled to CSS code
- Scoped styles generate CSS files with hashes and annotate elements with scoped attributes
Common Failure Points
- Failure to exclude
src/main.ts - Calling APIs that will be converted to Hooks outside the top level
- Unanalyzable expressions appearing in templates (triggering warnings)
- Disabling style preprocessing while using
scoped, leading to scoping failure
Next Steps
- Continue reading Capability Matrix Overview
- Review Compilation Conventions when encountering rule-related issues
