LiveCodes supports both Vue 2 and Vue 3 Single File Components with the official Vue compiler, including <script setup>, TypeScript, and scoped styles.
Configuration
Language Name: vue (Vue 3) or vue2 (Vue 2)
File Extensions: .vue, .vue3 (Vue 3), .vue2 (Vue 2)
Editor: Script editor
Compiler: Official Vue SFC compiler
Runtime: Vue 3.x runtime (automatic imports)
Vue 3 Single File Components
Basic Component
< template >
< div class = "counter" >
< h2 > Count: {{ count }} </ h2 >
< button @ click = " increment " > Increment </ button >
< button @ click = " decrement " > Decrement </ button >
</ div >
</ template >
< script >
import { ref } from 'vue' ;
export default {
setup () {
const count = ref ( 0 );
const increment = () => count . value ++ ;
const decrement = () => count . value -- ;
return {
count ,
increment ,
decrement ,
};
} ,
} ;
</ script >
< style scoped >
.counter {
padding : 2 rem ;
text-align : center ;
}
button {
margin : 0.5 rem ;
padding : 0.5 rem 1 rem ;
}
</ style >
Script Setup
Use <script setup> for cleaner syntax:
< template >
< div class = "user-profile" >
< h1 > {{ user . name }} </ h1 >
< p > {{ user . email }} </ p >
< button @ click = " updateProfile " > Update </ button >
</ div >
</ template >
< script setup >
import { ref , computed , onMounted } from 'vue' ;
const user = ref ({
name: 'John Doe' ,
email: '[email protected] ' ,
});
const displayName = computed (() => {
return user . value . name . toUpperCase ();
});
const updateProfile = () => {
user . value . name = 'Jane Doe' ;
};
onMounted (() => {
console . log ( 'Component mounted!' );
});
</ script >
TypeScript Support
Use TypeScript in your Vue components:
< template >
< div >
< h2 > {{ title }} </ h2 >
< p > Count: {{ count }} </ p >
< button @ click = " handleClick " > Increment </ button >
</ div >
</ template >
< script setup lang = "ts" >
import { ref , computed } from 'vue' ;
interface Props {
title : string ;
initialCount ?: number ;
}
const props = withDefaults ( defineProps < Props >(), {
initialCount: 0 ,
});
const emit = defineEmits <{
countChanged : [ value : number ];
}>();
const count = ref < number >( props . initialCount );
const handleClick = () => {
count . value ++ ;
emit ( 'countChanged' , count . value );
};
</ script >
Composition API
Full Composition API support:
< template >
< div >
< input v-model = " searchQuery " placeholder = "Search..." />
< ul >
< li v-for = " item in filteredItems " : key = " item . id " >
{{ item . name }}
</ li >
</ ul >
</ div >
</ template >
< script setup >
import { ref , computed } from 'vue' ;
const searchQuery = ref ( '' );
const items = ref ([
{ id: 1 , name: 'Apple' },
{ id: 2 , name: 'Banana' },
{ id: 3 , name: 'Cherry' },
]);
const filteredItems = computed (() => {
return items . value . filter ( item =>
item . name . toLowerCase (). includes ( searchQuery . value . toLowerCase ())
);
});
</ script >
Reactive State
Using ref, reactive, and computed:
< script setup >
import { ref , reactive , computed , watch } from 'vue' ;
// Ref for primitives
const count = ref ( 0 );
// Reactive for objects
const state = reactive ({
user: {
name: 'John' ,
age: 30 ,
},
settings: {
theme: 'dark' ,
},
});
// Computed properties
const doubleCount = computed (() => count . value * 2 );
// Watchers
watch ( count , ( newValue , oldValue ) => {
console . log ( `Count changed from ${ oldValue } to ${ newValue } ` );
});
watch (
() => state . user . name ,
( newName ) => {
console . log ( `Name changed to ${ newName } ` );
}
);
</ script >
Vue 2 Support
Use Vue 2 SFC with the vue2 language:
< template >
< div class = "counter" >
< h2 > Count: {{ count }} </ h2 >
< button @ click = " increment " > Increment </ button >
</ div >
</ template >
< script >
export default {
data () {
return {
count: 0 ,
};
} ,
methods: {
increment () {
this . count ++ ;
},
} ,
computed: {
doubleCount () {
return this . count * 2 ;
},
} ,
} ;
</ script >
< style scoped >
.counter {
padding : 1 rem ;
}
</ style >
Template Syntax
Directives
< template >
< div >
<!-- Text interpolation -->
< p > {{ message }} </ p >
<!-- Attribute binding -->
< img : src = " imageUrl " : alt = " imageAlt " />
<!-- Event listeners -->
< button @ click = " handleClick " > Click me </ button >
< input @ input = " handleInput " @ keyup . enter = " handleEnter " />
<!-- Conditional rendering -->
< p v-if = " isVisible " > Visible </ p >
< p v-else > Hidden </ p >
<!-- List rendering -->
< ul >
< li v-for = " item in items " : key = " item . id " >
{{ item . name }}
</ li >
</ ul >
<!-- Two-way binding -->
< input v-model = " text " />
<!-- Show/hide (CSS) -->
< div v-show = " isActive " > Toggle visibility </ div >
</ div >
</ template >
Slots
Component composition with slots:
< template >
< div class = "card" >
< div class = "card-header" >
< slot name = "header" > Default Header </ slot >
</ div >
< div class = "card-body" >
< slot > Default content </ slot >
</ div >
< div class = "card-footer" >
< slot name = "footer" : data = " footerData " / >
</ div >
</ div >
</ template >
< script setup >
const footerData = { date: new Date () };
</ script >
Scoped Styles
Component-scoped CSS:
< template >
< div class = "button" >
< slot > Click me </ slot >
</ div >
</ template >
< style scoped >
.button {
background : #3b82f6 ;
color : white ;
padding : 0.5 rem 1 rem ;
border : none ;
border-radius : 0.25 rem ;
cursor : pointer ;
}
.button:hover {
background : #2563eb ;
}
/* Deep selector for nested components */
:deep( .nested-class ) {
color : red ;
}
/* Slotted content */
:slotted( button ) {
margin : 0.5 rem ;
}
</ style >
CSS Preprocessors
Use SCSS, Less, or Stylus:
< style scoped lang = "scss" >
$primary-color: #3b82f6;
.component {
background: $primary-color;
&:hover {
background: darken($primary-color, 10%);
}
.nested {
color: white;
}
}
</ style >
Component Communication
Props
< template >
< div >
< h2 > {{ title }} </ h2 >
< p > {{ description }} </ p >
</ div >
</ template >
< script setup >
const props = defineProps ({
title: {
type: String ,
required: true ,
},
description: {
type: String ,
default: 'No description' ,
},
});
</ script >
Events
< template >
< button @ click = " handleClick " > Click me </ button >
</ template >
< script setup >
const emit = defineEmits ([ 'submit' , 'cancel' ]);
const handleClick = () => {
emit ( 'submit' , { timestamp: Date . now () });
};
</ script >
Provide/Inject
<!-- Parent component -->
< script setup >
import { provide , ref } from 'vue' ;
const theme = ref ( 'dark' );
provide ( 'theme' , theme );
</ script >
<!-- Child component -->
< script setup >
import { inject } from 'vue' ;
const theme = inject ( 'theme' );
</ script >
Lifecycle Hooks
< script setup >
import {
onMounted ,
onUpdated ,
onUnmounted ,
onBeforeMount ,
onBeforeUpdate ,
onBeforeUnmount ,
} from 'vue' ;
onBeforeMount (() => {
console . log ( 'Before mount' );
});
onMounted (() => {
console . log ( 'Mounted' );
// Fetch data, set up listeners, etc.
});
onUpdated (() => {
console . log ( 'Updated' );
});
onBeforeUnmount (() => {
console . log ( 'Before unmount' );
// Cleanup
});
onUnmounted (() => {
console . log ( 'Unmounted' );
});
</ script >
Compiler Configuration
Configure the Vue compiler:
{
"customSettings" : {
"vue" : {
"compilerOptions" : {
"isCustomElement" : (tag) => tag.startsWith('my-')
}
}
}
}
External Libraries
Import Vue ecosystem packages:
< script setup >
import { useRouter , useRoute } from 'vue-router' ;
import { useStore } from 'vuex' ;
import { useI18n } from 'vue-i18n' ;
const router = useRouter ();
const route = useRoute ();
const store = useStore ();
const { t } = useI18n ();
</ script >
Example Projects
Todo App
< template >
< div class = "todo-app" >
< h1 > Todo List </ h1 >
< input
v-model = " newTodo "
@ keyup . enter = " addTodo "
placeholder = "Add a todo..."
/>
< ul >
< li
v-for = " todo in todos "
: key = " todo . id "
: class = " { done: todo . completed } "
>
< input
type = "checkbox"
v-model = " todo . completed "
/>
{{ todo . text }}
< button @ click = " removeTodo ( todo . id ) " > Delete </ button >
</ li >
</ ul >
</ div >
</ template >
< script setup >
import { ref } from 'vue' ;
const newTodo = ref ( '' );
const todos = ref ([]);
let nextId = 1 ;
const addTodo = () => {
if ( newTodo . value . trim ()) {
todos . value . push ({
id: nextId ++ ,
text: newTodo . value ,
completed: false ,
});
newTodo . value = '' ;
}
};
const removeTodo = ( id ) => {
todos . value = todos . value . filter ( todo => todo . id !== id );
};
</ script >
< style scoped >
.done {
text-decoration : line-through ;
opacity : 0.6 ;
}
</ style >
Data Fetching
< template >
< div >
< div v-if = " loading " > Loading... </ div >
< div v-else-if = " error " > Error: {{ error }} </ div >
< ul v-else >
< li v-for = " user in users " : key = " user . id " >
{{ user . name }} - {{ user . email }}
</ li >
</ ul >
</ div >
</ template >
< script setup >
import { ref , onMounted } from 'vue' ;
const users = ref ([]);
const loading = ref ( true );
const error = ref ( null );
onMounted ( async () => {
try {
const response = await fetch ( 'https://jsonplaceholder.typicode.com/users' );
users . value = await response . json ();
} catch ( e ) {
error . value = e . message ;
} finally {
loading . value = false ;
}
});
</ script >
Automatic formatting with Prettier:
<!-- Before formatting -->
< template >< div >< h1 > {{ title }} </ h1 ></ div ></ template >
<!-- After formatting (Ctrl+Shift+F) -->
< template >
< div >
< h1 > {{ title }} </ h1 >
</ div >
</ template >
Best Practices
Prefer <script setup> for cleaner, more performant code: <!-- Good -->
< script setup >
import { ref } from 'vue' ;
const count = ref ( 0 );
</ script >
<!-- Verbose -->
< script >
import { ref } from 'vue' ;
export default {
setup () {
const count = ref ( 0 );
return { count };
}
} ;
</ script >
Use ref for primitives, reactive for objects: < script setup >
import { ref , reactive } from 'vue' ;
// Good for primitives
const count = ref ( 0 );
const name = ref ( 'John' );
// Good for objects
const user = reactive ({
name: 'John' ,
age: 30 ,
});
</ script >
Always provide unique keys: <!-- Good -->
< li v-for = " item in items " : key = " item . id " >
{{ item.name }}
</ li >
<!-- Bad -->
< li v-for = " ( item , index ) in items " : key = " index " >
{{ item.name }}
</ li >
Limitations
No SSR : Server-Side Rendering is not supported. All components are client-side rendered.
Vue Router and Vuex work but require manual configuration. State management libraries like Pinia are recommended.
React Alternative framework with JSX
Svelte Compiler-based framework
TypeScript Use TypeScript in Vue components
SCSS Use SCSS in style blocks