Vue3生态实战:Router、Pinia与完整项目
恭喜你走到最后一期!前四期我们学习了ES6、Vite、Vue3基础和组合式API。现在,让我们把这些知识整合起来,构建一个完整的Vue3应用。
这一期,我们学习Vue Router(路由)和Pinia(状态管理),然后做一个实战项目。
Vue Router
安装
pnpm add vue-router
基础配置
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/users/:id',
name: 'User',
component: () => import('@/views/User.vue') // 懒加载
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/NotFound.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
<!-- App.vue -->
<nav>Home
About</nav>```
### 路由参数
```vue
import { useRoute } from 'vue-router'
const route = useRoute()
// 获取参数
console.log(route.params.id) // /users/123 → 123
console.log(route.query.page) // /users?page=2 → 2
编程式导航
import { useRouter } from 'vue-router'
const router = useRouter()
function goToUser(id) {
router.push({ name: 'User', params: { id } })
}
function goBack() {
router.back()
}
function replace() {
router.replace({ name: 'Home' }) // 不留历史记录
}
嵌套路由
const routes = [
{
path: '/settings',
component: () => import('@/views/Settings.vue'),
children: [
{
path: '', // /settings
component: () => import('@/views/SettingsProfile.vue')
},
{
path: 'account', // /settings/account
component: () => import('@/views/SettingsAccount.vue')
},
{
path: 'security', // /settings/security
component: () => import('@/views/SettingsSecurity.vue')
}
]
}
]
<!-- Settings.vue -->
<div><nav>Profile
Account
Security</nav></div>
导航守卫
// 全局前置守卫
router.beforeEach((to, from) => {
// 需要登录
if (to.meta.requiresAuth && !isAuthenticated()) {
return { name: 'Login', query: { redirect: to.fullPath } }
}
})
// 全局后置守卫
router.afterEach((to) => {
document.title = to.meta.title || 'My App'
})
// 路由元信息
const routes = [
{
path: '/admin',
component: Admin,
meta: {
requiresAuth: true,
title: 'Admin Dashboard'
}
}
]
Pinia
Pinia是Vue3官方推荐的状态管理库,比Vuex更简单、更强大。
安装
pnpm add pinia
基础配置
// src/stores/index.js
import { createPinia } from 'pinia'
const pinia = createPinia()
export default pinia
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import pinia from './stores'
const app = createApp(App)
app.use(pinia)
app.use(router)
app.mount('#app')
定义Store
// src/stores/user.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
// 组合式API风格(推荐)
export const useUserStore = defineStore('user', () => {
// State
const user = ref(null)
const token = ref(localStorage.getItem('token'))
// Getters
const isLoggedIn = computed(() => !!token.value)
const userName = computed(() => user.value?.name ?? 'Guest')
// Actions
async function login(credentials) {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials)
})
const data = await response.json()
user.value = data.user
token.value = data.token
localStorage.setItem('token', data.token)
}
function logout() {
user.value = null
token.value = null
localStorage.removeItem('token')
}
return {
user,
token,
isLoggedIn,
userName,
login,
logout
}
})
使用Store
import { useUserStore } from '@/stores/user'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
// 解构state和getters(保持响应性)
const { user, isLoggedIn, userName } = storeToRefs(userStore)
// actions直接解构
const { login, logout } = userStore
// 直接访问
console.log(userStore.user)
<div>
Welcome, {{ userName }}
<button>Logout</button>
</div>
<div><button>Login</button></div>
选项式API风格
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
async fetchCount() {
const response = await fetch('/api/count')
this.count = await response.json()
}
}
})
持久化
// 使用pinia-plugin-persistedstate
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export const useUserStore = defineStore('user', () => {
// ...
}, {
persist: true // 启用持久化
})
实战项目:任务管理应用
让我们把学到的知识整合起来,做一个任务管理应用。
项目结构
src/
├── main.js
├── App.vue
├── router/
│ └── index.js
├── stores/
│ ├── index.js
│ ├── tasks.js
│ └── user.js
├── composables/
│ └── useLocalStorage.js
├── views/
│ ├── Home.vue
│ ├── Login.vue
│ └── Tasks.vue
└── components/
├── TaskItem.vue
└── TaskForm.vue
任务Store
// src/stores/tasks.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useTaskStore = defineStore('tasks', () => {
const tasks = ref([
{ id: 1, title: 'Learn Vue3', completed: false },
{ id: 2, title: 'Build a project', completed: false }
])
const filter = ref('all') // all, active, completed
const filteredTasks = computed(() => {
if (filter.value === 'active') {
return tasks.value.filter(t => !t.completed)
}
if (filter.value === 'completed') {
return tasks.value.filter(t => t.completed)
}
return tasks.value
})
const remainingCount = computed(() =>
tasks.value.filter(t => !t.completed).length
)
function addTask(title) {
tasks.value.push({
id: Date.now(),
title,
completed: false
})
}
function removeTask(id) {
const index = tasks.value.findIndex(t => t.id === id)
if (index > -1) {
tasks.value.splice(index, 1)
}
}
function toggleTask(id) {
const task = tasks.value.find(t => t.id === id)
if (task) {
task.completed = !task.completed
}
}
function clearCompleted() {
tasks.value = tasks.value.filter(t => !t.completed)
}
return {
tasks,
filter,
filteredTasks,
remainingCount,
addTask,
removeTask,
toggleTask,
clearCompleted
}
}, {
persist: true
})
任务列表组件
<!-- src/views/Tasks.vue -->
import { useTaskStore } from '@/stores/tasks'
import { storeToRefs } from 'pinia'
import TaskItem from '@/components/TaskItem.vue'
import TaskForm from '@/components/TaskForm.vue'
const taskStore = useTaskStore()
const { filteredTasks, filter, remainingCount } = storeToRefs(taskStore)
const { addTask, removeTask, toggleTask, clearCompleted } = taskStore
<div class="tasks">
<h1>Tasks</h1>
<div class="filters"><button>
All
</button>
<button>
Active
</button>
<button>
Completed
</button></div>
<div class="footer">{{ remainingCount }} items left
<button>Clear completed</button></div>
</div>
.tasks {
max-width: 500px;
margin: 0 auto;
padding: 20px;
}
.filters button.active {
font-weight: bold;
}
.task-list {
list-style: none;
padding: 0;
}
任务项组件
<!-- src/components/TaskItem.vue -->
defineProps({
task: {
type: Object,
required: true
}
})
const emit = defineEmits(['toggle', 'remove'])
<ul>
<li class="task-item">{{ task.title }}
<button>×</button></li>
</ul>
.task-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px;
border-bottom: 1px solid #eee;
}
.task-item.completed span {
text-decoration: line-through;
color: #999;
}
任务表单组件
<!-- src/components/TaskForm.vue -->
import { ref } from 'vue'
const title = ref('')
const emit = defineEmits(['add'])
function handleSubmit() {
if (title.value.trim()) {
emit('add', title.value.trim())
title.value = ''
}
}
<button type="submit">Add</button>
.task-form {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.task-form input {
flex: 1;
padding: 10px;
font-size: 16px;
}
项目优化
代码分割
// router/index.js
const routes = [
{
path: '/admin',
component: () => import(/* webpackChunkName: "admin" */ '@/views/Admin.vue')
}
]
懒加载组件
import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent(() =>
import('@/components/HeavyComponent.vue')
)
构建优化
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'ui': ['element-plus']
}
}
}
}
})
系列总结
恭喜你完成了现代前端开发到Vue3的完整学习之旅!
第一期:ES6+、Node.js、npm基础
第二期:Vite构建工具
第三期:Vue3响应式和组件
第四期:组合式API和逻辑复用
第五期:Router、Pinia和实战项目
你现在掌握了:
- 现代JavaScript语法
- Vite项目构建
- Vue3核心概念
- 组合式API和组合式函数
- Vue Router路由管理
- Pinia状态管理
- 完整项目开发流程
下一步:
- 学习TypeScript(Vue3的最佳伴侣)
- 探索UI框架(Element Plus、Naive UI)
- 学习服务端渲染(Nuxt 3)
- 阅读官方文档(https://vuejs.org/)
进阶资源:
恭喜!你已经掌握了现代前端开发到Vue3的核心技能。去构建你的下一个项目吧!
Views: 2
