A .vue files may have HTML, CSS, JS template syntax inside. <template></template> is for HTML structure, <style></style> is for CSS style, <script></script> is for JavaScript script.
Vue have two code modes, Options and Composition Mode. But in this documentation. We use composition
This briefly shows some fundamentals concepts on Vue in two files. The concepts are declarative rendering, attribute bindings, event listeners, form bindings, conditional rendering, list rendering, computed property, lifecycle and template refs, watchers, components, props, emits, slots.
Show code
<!-- Only one setup can be made on each file --><scriptsetup>import{reactive,ref}from'vue'// Import state library
// Declarative rendering: renders dynamically
constcounter=reactive({count:0})// Access by counter.count
constmessage=ref('Hello World!')// Access by message.value
conststyleRef=ref('red')functionincrement(){counter.count+=1}// Access DOM manually after elements being mounted. During script setup, the DOM is not created yet.
import{onMounted}from'vue'constpElementRef=ref(null)onMounted(()=>{pElementRef.value.textContent='Mounted! (DOM operation)'})// Making(triggers) 'side effects' reactively using watch
import{watch}from'vue'consttodoId=ref(1)consttodoData=ref(null)asyncfunctionfetchData(){todoData.value=nullconstres=awaitfetch(`https://jsonplaceholder.typicode.com/todos/${todoId.value}`)todoData.value=awaitres.json()}fetchData()watch(todoId,fetchData)// Import other components
importChildCompfrom'./ChildComp.vue'// Define ref to receive child component data
constchildMsg=ref('No child msg yet')</script><template><!-- Attribute Binding: ':class' is 'v-bind:class', binding class values to styleRef ref, message under mustache '{{ message }}' render dynamically to message ref --><h1:class="styleRef">{{ message }}</h1><!-- Event Listeners: '@click' is 'v-on:click', calls the function 'increment() when clicked' --><button@click="increment"> Count is: {{ counter.count }}</button><!-- Form Binding: 'v-model:"message"'create two-way binding --><inputv-model="message"placeholder="Type here"><!-- Conditional Rendering: 'v-if:"counter.count % 2 !== 0"' --><pv-if="counter.count % 2 !== 0">The counter is odd</p><pv-else>The counter is even</p><!-- Lifecycle and Template Refs: manually work with DOM --><pref="pElementRef">Hello</p><!-- Watchers: trigger when reactivity data changes --><p>Todo id: {{ todoId }}
<button@click="todoId++":disabled="!todoData">Fetch next todo</button></p><pv-if="!todoData">Loading...</p><prev-else>{{ todoData }}</pre><!-- Import ChildComp component and prop to child component --><ChildComp:propToChild="'This is a prop to child component'"@response="(msg) => childMsg = msg"><!-- Slots: pass down template fragments to child --> Message to child: {{ message }}
</ChildComp><p>{{ childMsg }}</p></template><style>.red{color:red;}</style>
<scriptsetup>import{ref,computed}from'vue'letid=0constnewTodo=ref('')consthideCompleted=ref(false)consttodos=ref([{id:id++,text:'Learn HTML',done:true},{id:id++,text:'Learn JavaScript',done:true},{id:id++,text:'Learn Vue',done:false}])// Computed property: computes its value based on other reactive data sources
constfilteredTodos=computed(()=>{returnhideCompleted.value?todos.value.filter((t)=>!t.done):todos.value})functionaddTodo(){todos.value.push({id:id++,text:newTodo.value,done:false})newTodo.value=''}functionremoveTodo(todo){// t is the item iterated in todos, filter returns list after removing item with false statement
todos.value=todos.value.filter((t)=>t!==todo)}// Define props to receive data from parent
constprops=defineProps({propToChild:String})// Setup emit to send data to parent
constemitFromChild=defineEmits(['response'])emitFromChild('response','This is an emit from child')</script><template><h2>{{ propToChild || 'No props passed yet' }}</h2><form@submit.prevent="addTodo"><inputv-model="newTodo"requiredplaceholder="new todo"><button>Add Todo</button></form><ul><!-- List Rendering --><liv-for="todo in filteredTodos":key="todo.id"><inputtype="checkbox"v-model="todo.done"><span:class="{ done: todo.done }">{{ todo.text }}</span><button@click="removeTodo(todo)">X</button></li></ul><button@click="hideCompleted = !hideCompleted"> {{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button><slot>Fallback content from parent</slot></template><style>.done{text-decoration:line-through;}</style>
The structure are as below. Configure index.js, add views and make sure main.js imports router to be use
src/router/index.js
import{createRouter,createWebHashHistory}from'vue-router'importHomeViewfrom'../views/HomeView.vue'importHomeViewfrom'../views/LoginView.vue'// You have two ways to configure router
constroutes=[{path:'/',redirect:'/about/home',// Default route
},{path:'/about',name:'about',// Nested routes
children:[{path:'home',component:HomeView},{path:'login',component:LogInView},],// Route level code-splitting: this generates a separate chunk about.[hash].js)
component:()=>import(/* webpackChunkName: "about" */'../views/AboutView.vue')},]constrouter=createRouter({history:createWebHashHistory(),routes})exportdefaultrouter
<script>import{useRouter}from'vue-router';constrouter=useRouter();router.push('/path');// Move to a route
</script><template><router-linkto="/path"><a>Link</a></router-link><router-view>Routerviewswillberenderedhere</router-view></template>
src/views/HomeView.vue
<scriptsetup>// @ is an alias to /src
importHelloWorldfrom'@/components/HelloWorld.vue'</script><template><divclass="home"><HelloWorldmsg="Welcome to Your Vue.js App"/></div></template>
import{defineStore}from'pinia';exportconstuseGlobalStore=defineStore('global',{state:()=>({// The global value
value:null,uname:null}),getters:{getValue:(state)=>state.value,getUname:(state)=>state.uname},actions:{setValue(newValue){this.value=newValue;},setUname(uname){this.uname=uname;},clear(){this.value=null;this.uname=null;}},});