Suim
状态
- 网站出生日期2026-06-26
- 文章总数2
- 评论数量0
路由守卫是 Vue Router 提供的钩子函数,用于在导航过程中执行逻辑检查。可以把它理解为路由系统的"安检门",在页面跳转前进行条件判定。
Vue Router 提供了三种层级的守卫:
| 类型 | 触发时机 | 典型用途 |
|---|---|---|
| 全局前置 | 导航开始前 | 登录校验、权限验证 |
| 全局解析 | 导航确认前 | 数据预加载 |
| 全局后置 | 导航完成后 | 页面标题更新、埋点上报 |
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [...]
})
router.beforeEach((to, from) => {
// to 表示目标路由对象
// from 表示来源路由对象
// 返回 false 则取消导航
console.log(`从 ${from.path} 跳转到 ${to.path}`)
})
Vue Router 4.x 推荐使用返回值控制导航,但 next 函数仍兼容:
// 方式一:返回 false 取消导航
router.beforeEach(() => {
return false;
});
// 方式二:返回路径字符串重定向
router.beforeEach(() => {
return "/login";
});
// 方式三:返回路由对象
router.beforeEach(() => {
return { name: "Login", query: { redirect: to.fullPath } };
});
const routes = [
{
path: "/admin",
component: AdminDashboard,
meta: { permission: "admin", needLogin: true },
},
{
path: "/editor",
component: EditorPanel,
meta: { permission: "editor", needLogin: true },
},
{
path: "/profile",
component: UserProfile,
meta: { needLogin: true },
},
];
router.beforeEach((to) => {
const isAuthenticated = sessionStorage.getItem("sessionId");
const userRole = sessionStorage.getItem("role") || "guest";
if (to.meta.needLogin && !isAuthenticated) {
return { name: "SignIn", query: { back: to.fullPath } };
}
if (to.meta.permission && to.meta.permission !== userRole) {
return { name: "Forbidden" };
}
});
<script setup>
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
async function doLogin(form) {
const res = await api.auth.signIn(form);
sessionStorage.setItem("sessionId", res.token);
const fallback = route.query.back || "/dashboard";
router.replace(fallback);
}
</script>
练习一:计数器按钮
<script setup>
import { ref } from "vue";
const counter = ref(0);
function increment() {
counter.value++;
console.log(`当前计数: ${counter.value}`);
}
</script>
<template>
<button @click="increment">点我 +1(已点 {{ counter }} 次)</button>
</template>
练习二:双向绑定演示
<script setup>
import { ref } from "vue";
const keyword = ref("");
</script>
<template>
<input v-model="keyword" placeholder="输入搜索词" />
<p>您正在搜索:{{ keyword || "(暂无内容)" }}</p>
</template>
练习三:表单提交
<script setup>
import { ref } from "vue";
const email = ref("");
function onSubmit() {
alert(`提交邮箱: ${email.value}`);
}
</script>
<template>
<form @submit.prevent="onSubmit">
<input v-model="email" type="email" placeholder="请输入邮箱" />
<button type="submit">提交</button>
</form>
</template>
练习四:事件修饰符
<script setup>
function parentHandler() {
console.log("父级 div 被触发");
}
function childHandler() {
console.log("子级按钮被触发");
}
function onEnter() {
console.log("按下了回车键");
}
</script>
<template>
<div @click="parentHandler">
<button @click.stop="childHandler">点击我(阻止冒泡)</button>
</div>
<input @keyup.enter="onEnter" placeholder="按回车触发" />
</template>
练习五:动态样式绑定
<script setup>
import { ref } from "vue";
const active = ref(false);
</script>
<template>
<div
:class="{ 'box-active': active }"
:style="{ backgroundColor: active ? '#42b983' : '#ccc' }"
@mouseenter="active = true"
@mouseleave="active = false"
>
{{ active ? "鼠标已进入" : "鼠标已离开" }}
</div>
</template>
<style scoped>
.box-active {
transform: scale(1.05);
transition: all 0.3s;
}
</style>
练习六:插槽封装
<!-- CardBox.vue -->
<template>
<div class="card">
<div class="card-head">
<slot name="head"></slot>
</div>
<div class="card-body">
<slot></slot>
</div>
</div>
</template>
<style scoped>
.card {
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 16px;
}
.card-head {
font-weight: bold;
margin-bottom: 12px;
}
</style>
练习七:v-model 修饰符
<script setup>
import { ref } from "vue";
const age = ref(null);
const nickname = ref("");
</script>
<template>
<input v-model.number="age" placeholder="年龄(自动转数字)" />
<input v-model.trim="nickname" placeholder="昵称(自动去空格)" />
<p>年龄类型:{{ typeof age }},昵称:「{{ nickname }}」</p>
</template>
练习八:综合注册表单
<script setup>
import { reactive, computed } from "vue";
const form = reactive({
account: "",
pwd: "",
confirmPwd: "",
gender: "",
hobbies: [],
city: "",
agreement: false,
});
const cities = [
{ label: "杭州", val: "hz" },
{ label: "成都", val: "cd" },
{ label: "武汉", val: "wh" },
];
const isValid = computed(() => {
return (
form.account.length >= 4 &&
form.pwd.length >= 6 &&
form.pwd === form.confirmPwd &&
form.agreement
);
});
function register() {
if (!isValid.value) return;
console.log("注册信息:", form);
}
</script>
<template>
<div class="register-box">
<h3>账号注册</h3>
<input v-model="form.account" placeholder="账号(至少4位)" />
<input v-model="form.pwd" type="password" placeholder="密码(至少6位)" />
<input v-model="form.confirmPwd" type="password" placeholder="确认密码" />
<div>
<label><input type="radio" v-model="form.gender" value="M" /> 男</label>
<label><input type="radio" v-model="form.gender" value="F" /> 女</label>
</div>
<div>
<label
><input type="checkbox" v-model="form.hobbies" value="code" />
编程</label
>
<label
><input type="checkbox" v-model="form.hobbies" value="game" />
游戏</label
>
<label
><input type="checkbox" v-model="form.hobbies" value="music" />
音乐</label
>
</div>
<select v-model="form.city">
<option value="">选择城市</option>
<option v-for="c in cities" :key="c.val" :value="c.val">
{{ c.label }}
</option>
</select>
<label>
<input type="checkbox" v-model="form.agreement" />
同意用户协议
</label>
<button @click="register" :disabled="!isValid">立即注册</button>
</div>
</template>