YouTip LogoYouTip

Vue3 Taskhub Router

Routing System

\\n

In the previous chapter, our task management logic was made persistent via Pinia. Now we need to inject a soul into the applicationβ€”the Routing System.

\\n

In enterprise-level development, routing is not just about switching pages; it also bears the heavy responsibility of security (permission control) and user experience (dynamic titles).

\\n
\\n

Installation and Basic Configuration

\\n

First, install Vue Router:

\\n
npm install vue-router
\\n

Create a router directory under the src directory, and create a router/index.js file.

\\n

Image 1

\\n

We will define two pages: Dashboard and Login.

\\n

Example

\\n
import{ createRouter, createWebHistory } from 'vue-router'\\n\\nconst routes =[\\n\\n{\\n\\n path:'/login',\\n\\n name:'Login',\\n\\n component:()=>import('@/views/Login.vue'),\\n\\n meta:{ title:'Login - TaskHub', requiresAuth:false}\\n\\n},\\n\\n{\\n\\n path:'/',\\n\\n name:'Dashboard',\\n\\n component:()=>import('@/views/Dashboard.vue'),\\n\\n// Route meta information: used to store custom data\\n\\n meta:{\\n\\n title:'My Workspace',\\n\\n requiresAuth:true,\\n\\n breadcrumb:'Home'\\n\\n}\\n\\n}\\n\\n]\\n\\nconst router = createRouter({\\n\\n history: createWebHistory(),\\n\\n routes\\n\\n})\\n\\nexport default router
\\n

Create a views folder under src and assign the pages accordingly.

\\n
    \\n
  • src/App.vue: The root container, containing only <router-view /> and global animations.
  • \\n
  • src/views/Dashboard.vue: The original core functionality of TaskHub (task list, input box, etc.).
  • \\n
  • src/views/Login.vue: The newly added login page.
  • \\n
\\n

The complete structure is as follows:

\\n

Image 2

\\n

Refactor App.vue (into a clean container)

\\n

Example

\\n
<template>\\n\\n<div class="min-h-screen bg-slate-50">\\n\\n<router-view v-slot="{ Component }">\\n\\n<transition name="page" mode="out-in">\\n\\n<component :is="Component"/>\\n\\n</transition>\\n\\n</router-view>\\n\\n</div>\\n\\n</template>\\n\\n<style>\\n\\n /* Fade in/out effect for page transitions */\\n\\n .page-enter-active, .page-leave-active {\\n\\n transition: opacity 0.2s ease;\\n\\n }\\n\\n .page-enter-from, .page-leave-to {\\n\\n opacity: 0;\\n\\n }\\n\\n</style>
\\n

Migrate the original logic into Dashboard.vue

\\n

Cut all the code previously written in App.vue (importing Store, importing components, template layout) and paste it into src/views/Dashboard.vue.

\\n

Dashboard.vue File Code

\\n
<script setup>\\n\\n import { storeToRefs } from 'pinia'\\n\\n import { useTaskStore } from '@/stores/taskStore'\\n\\n import { useRouter } from 'vue-router' // Import router\\n\\n import TaskHeader from '@/components/TaskHeader.vue'\\n\\n import TaskInput from '@/components/TaskInput.vue'\\n\\n import TaskFilter from '@/components/TaskFilter.vue'\\n\\n import TaskItem from '@/components/TaskItem.vue'\\n\\nconst taskStore = useTaskStore()\\n\\n const router = useRouter()\\n\\n const { filter, filteredTasks } = storeToRefs(taskStore)\\n\\n const { addTask, removeTask, toggleTask } = taskStore\\n\\n// Logout method\\n\\n const handleLogout = () => {\\n\\n localStorage.removeItem('isLoggedIn')\\n\\n router.push('/login')\\n\\n }\\n\\n</script>\\n\\n<template>\\n\\n<div class="py-12 px-4">\\n\\n<div class="max-w-md mx-auto bg-white rounded-3xl shadow-xl border border-slate-100 overflow-hidden">\\n\\n<TaskHeader />\\n\\n<main class="p-6">\\n\\n<TaskInput @add-task="addTask"/>\\n\\n<TaskFilter v-model="filter"/>\\n\\n<ul class="space-y-3">\\n\\n<TransitionGroup name="list">\\n\\n<TaskItem \\n\\nv-for="task in filteredTasks"\\n\\n:key="task.id"\\n\\n:task="task"\\n\\n@toggle="toggleTask"\\n\\n@remove="removeTask"\\n\\n/>\\n\\n</TransitionGroup>\\n\\n</ul>\\n\\n<button\\n\\n@click="handleLogout"\\n\\nclass="mt-8 w-full py-2 text-xs text-slate-400 hover:text-red-500 transition-colors"\\n\\n>\\n\\n Log out current account\\n\\n</button>\\n\\n</main>\\n\\n</div>\\n\\n</div>\\n\\n</template>
\\n

Login Logic Implementation (Login.vue)

\\n

Use Tailwind v4 to quickly build a minimalist login page and simulate login behavior.

\\n

Example

\\n
<script setup>\\n\\n import { ref } from 'vue'\\n\\n import { useRouter } from 'vue-router'\\n\\nconst router = useRouter()\\n\\n const isLoading = ref(false)\\n\\nconst handleLogin = () => {\\n\\n isLoading.value = true\\n\\n // Simulate async request\\n\\n setTimeout(() => {\\n\\n localStorage.setItem('isLoggedIn', 'true')\\n\\n router.push('/') // LoginSuccessfully redirect to Home\\n\\n isLoading.value = false\\n\\n }, 1000)\\n\\n }\\n\\n</script>\\n\\n<template>\\n\\n<div class="min-h-screen flex items-center justify-center bg-slate-50">\\n\\n<div class="p-8 bg-white rounded-3xl shadow-xl w-full max-w-sm border border-slate-100">\\n\\n<h2 class="text-2xl font-black mb-6 text-slate-800">Welcome back</h2>\\n\\n<button\\n\\n @click="handleLogin"\\n\\n :disabled="isLoading"\\n\\nclass="w-full bg-linear-to-r from-blue-600 to-indigo-600 text-white py-3 rounded-xl font-bold hover:opacity-90 active:scale-95 transition-all disabled:opacity-50"\\n\\n>\\n\\n {{ isLoading ? 'Loginin...' : 'One-click system access' }}\\n\\n</button>\\n\\n<p class="mt-4 text-center text-xs text-slate-400">Test environment: Click to Login</p>\\n\\n</div>\\n\\n</div>\\n\\n</template>
\\n
\\n

Perfect the interception logic in router/index.js

\\n

Ensure that security check mode is enabled in your route configuration:

\\n

Example

\\n
import{ createRouter, createWebHistory } from 'vue-router'\\n\\nconst routes =[\\n\\n{\\n\\n path:'/login',\\n\\n name:'Login',\\n\\n component:()=>import('@/views/Login.vue'),\\n\\n meta:{ title:'Login - TaskHub', requiresAuth:false}\\n\\n},\\n\\n{\\n\\n path:'/',\\n\\n name:'Dashboard',\\n\\n component:()=>import('@/views/Dashboard.vue'),\\n\\n// Route meta information: used to store custom data\\n\\n meta:{\\n\\n title:'My Workspace',\\n\\n requiresAuth:true,\\n\\n breadcrumb:'Home'\\n\\n}\\n\\n}\\n\\n]\\n\\nconst router = createRouter({\\n\\n history: createWebHistory(),\\n\\n routes\\n\\n})\\n\\n// src/router/index.js (Core code snippet)\\n\\n router.beforeEach((to, from, next)=>{\\n\\nconst isAuthenticated = localStorage.getItem('isLoggedIn')==='true'\\n\\n// If navigating to Home without Login -> Redirect to Login\\n\\nif(to.meta.requiresAuth&&!isAuthenticated){\\n\\n next('/login')\\n\\n}\\n\\n// If already logged in but still redirecting to the Login page -> Redirect to Home\\n\\nelse if(to.path==='/login'&& isAuthenticated){\\n\\n next('/')\\n\\n}\\n\\nelse{\\n\\n next()\\n\\n}\\n\\n})\\n\\nexport default router
\\n

Complete the final puzzle piece in main.js

\\n

Ensure your main.js imports and mounts the router:

\\n

Example

\\n
import{ createApp } from 'vue'\\n\\nimport{ createPinia } from 'pinia'\\n\\nimport router from './router'// Import router\\n\\nimport App from './App.vue'\\n\\nimport'./style.css'\\n\\nconst app = createApp(App)\\n\\n app.use(createPinia())\\n\\n app.use(router)// 2. Register routes\\n\\n app.mount('#app')
\\n

Final Result

\\n
    \\n
  1. Access /: If you are not logged in, the page will flash briefly and then redirect to /login.
  2. \\n
  3. Access /login: Click the button, the local storage isLoggedIn becomes true, and it instantly switches to the dashboard.
  4. \\n
  5. Refresh Page: Pinia will read tasks from LocalStorage, and the route guard will check the login status; no data will be lost.
  6. \\n
\\n

Image 3

\\n

After logging in, you can click to log out:

\\n

Image 4

\\n

Key Concepts Explanation

\\n
    \\n
  1. Why did App.vue become empty?
    \\nIn a Single Page Application (SPA), App.vue is the shell of the entire application. We emptied it so that we can dynamically insert different pages (Dashboard or Login) into it based on URL changes.
  2. \\n
  3. Practical use of useRouter:
    \\nIn Dashboard.vue, we implemented the logout function via router.push('/login'). Note: push adds a new record to the browser history, so clicking the browser back button can go back.
  4. \\n
  5. Tailwind v4 Layout Inheritance:
    \\nSince we added bg-slate-50 and min-h-screen to the global container in App.vue, all sub-pages (Dashboard, Login) will inherit this light gray background by default, ensuring visual consistency.
  6. \\n
← Openclaw Clawdbot TutorialQoder Cli β†’