Vue Router

yuhuo2022-06-22开发库框架库

Vue Router 官网open in new window

HTML 配置

<div id="app">
	<h1>Hello App!</h1>
  	<p>
    	<!-- <router-link> 将转化为带正确 href 属性的 <a> 标签-->
    	<!-- 等同于 router.push() -->
    	<router-link :to="{path: '/home', query: {a: 1}}">Go to Home</router-link>
      
    	<!-- 等同于 router.replace() -->
    	<router-link to="/about" replace>Go to About</router-link>
  	</p>
  	<!-- 渲染顶层路由匹配的组件 -->
  	<router-view></router-view>
    
    <!-- 添加过渡 -->
    <router-view v-slot="{ Component, route }">
        <!-- 可以通过meta动态指定过渡名称 -->
  		<transition :name="route.meta.transition || 'fade'" >
            <!-- 使用key强制过渡,避免组件被复用时忽略过渡 -->
    		<component :is="Component" :key="route.path"/>
  		</transition>
	</router-view>
</div>

<!-- About -->
<template id="component_about">
    <div class="about">
        <h1>About</h1>
        <!-- 渲染嵌套路由匹配的组件 -->
        <router-view name="Header"></router-view>
        <router-view></router-view>
    </div>
</template>

路由配置

// router.js
import VueRouter from "vue-router";
import Home from "./Home.js";
import About from "./About.js";

const router = VueRouter.createRouter({
    // hash 模式
    history: VueRouter.createWebHashHistory(),
    routes: [
        {
            // 静态路径
            path: "/about",  
            // 动态路径:携带路径参数
            path: "/about/:mode/user_:userId",
            // 参数后加括号,设定正则表达式
            path: "/about/user_:userId(\\d+)_search" 
            // 参数后面加量词 * + ? 设为可重复
            // $route.params.chapters 将是个数组
            path: '/about/:chapters+' 
            // 正则表达式 + 可重复
            path: "/about/:chapters(.*)*" 
            // 静态导入组件
            component: About,
            // 路径参数转成 props
            props: true,
            // 静态 props
            props: { 
            	width: 100 
        	},
        	// 动态 props
            props: route => { return { query: route.query.q }},
            // 名称
            name: "About",
            // 元信息
            meta: {  
                title: "关于"
            },
            // 路径是否区分大小写,默认 false
            sensitive: false, 
            // 路径是否区分带尾部斜线,默认 false
            strict: false,
        	// 嵌套路由(以父路由路径为 /about 示例)
            children: [
                {
                    // 绝对路径
                    path: '/about_child',
                    // 相对路径,相对于父路由路径,
                    path: 'child', // 匹配 /about/child
        			path: '', // 匹配 /about	
                    // 多别名
                    alias: [
                        // 绝对路径
                        "/topic", 
                        // 相对路径,相对于父路由路径,匹配 /about/info
                        "info"
                    ],
    				// 单别名,别名中需要包含相同路径参数
                    alias: "/topic/:mode/u_:userId",
        			// 命名视图
                    components: {
                        // 动态导入组件
                        default: () => import("./Main.js"),
                        Header: () => import("./Header.js"),
                  	},
    				// 每个命名视图定义 props
    				props: { 
                        default: true, 
                        Header: false 
                    }
                },
            ],
        }, 	
        // 注意:导航守卫不会应用在被重定向路由上
        {
            path: "/page/home",
    		// 重定向可以省略 component,除非有 children
            component: Home,
    		// 绝对路径
    		redirect: '/page/about',
    		// 相对路径,相对当前路径,匹配 /page/about
    		redirect: 'about', 
    		// 名称
    		redirect: { name: 'About' },
    		// 方法
    		redirect: to => {
              	return { path: '/page/about', query: { q: to.params.text } }
            },
        },
    ],                                    
    // 全局sensitive
    sensitive: false,                                    
    // 全局strict
    strict: false,
    // 滚动行为
	scrollBehavior (to, from, savedPosition) {
        // 页面滚动位置
        return { 
            top: 0,
            left: 0,
        };
        // 指定元素的相对偏移量
        return {
          	el: '#main',
          	top: -10,
            left: 0,
            // 滚动方式:auto 立即到达(默认),smooth 滑动到达
            behavior: 'smooth',
        };
        // 按下后退/前进按钮时有效
        if (savedPosition) {
            // 浏览器的原生表现
        	return savedPosition;
        }
        // 延迟滚动
        return new Promise((resolve, reject) => {
          	setTimeout(() => {
            	resolve({ left: 0, top: 0 });
          	}, 500)
        });
  	}
});

export default router;
// main.js
import router from "./router.js";
import App from "./App.js";

const app = Vue.createApp(App);
app.use(router);
app.mount("#app");

路由对象

$route

// About.js
export default {
    template: "#component_about", 
    // 路径参数转成 props
    props: ["mode", "userId"],
    created() {
        // 路由路径 /about/:mode/user_:userId
        // 访问路径 index.html#/about/pc/user_1001?a=1&b=2
    	
        this.$route = {
            fullPath: "/about/pc/user_1001",
            hash: "",
            href: "#/about/pc/user_1001",
            // router.js中匹配的路由对象
            matched: [{}],
            meta: { title: "关于" },
            name: "About",
            params: { mode: "pc", userId: "1001" },
            path: "/about/pc/user_1001",
            query: { a: "1", b: "2" },
            redirectedFrom: undefined,
        };
    },
    // 当 从 /about/pc/user_1001 导航到 /about/pc/user_1002 时,
    // 仅路径参数变化,相同的组件实例将被重复使用,生命周期钩子不会被调用,
    // 可以通过监听 $route.params 变化,或者添加导航守卫,以做出响应
    watch:{
        "$route.params": (toParams, fromParams) => {
            // 路径参数变化
        }
    },
}

$router

// About.js
import router from "./router.js";
export default {
    template: "#component_about", 
    create() {
      	// 两者是同一对象,使用this的方式可以减少导入
      	this.$router == router;
    },
    // 路由路径:/about/:mode/user_:userId
    methods: {  
        // 跳转路由
        push() {
            // 路径
            this.$router.push('"/about/pc/user_1001?a=1&b=2"').then((result) => {
                if(result) {
                    // 导航被阻止,result 为错误对象
                } else {
                    // 导航成功,result 为 undefined
                }
            });
            
            // 路径 + 查询参数(注意:路径参数在此无效)
            this.$router.push({
                path: '/about/pc/user_1001',
                // 查询参数,可选
                query: { a: 1, b: 2 },
                // 是否替换,等同于 $router.replace(),默认false
                replace: true,
            });
            
            // 名称 + 路径参数 + 查询参数
            this.$router.push({
                name: 'About',
                // 路径参数
                params: {
                    mode: "h5", // 必传
                    userId: "1003", // 必传
                    sex: "男" // 可选,跳转后刷新页面就不存在了
                },
                // 查询参数,可选
                query: { a: 1, b: 2 },
            });                                                 
        },
        // 替换路由
        replace() {
            // 参数同 push
            this.$router.replace({...});
        },
    	// 解析路由
    	resolve() {
            // 返回 route 对象,参数同 push
            this.$router.resolve({...});
        },
    	// 移动路由
    	go() {
            // 向前移动x条记录,x可为负数
            this.$router.go(1);
            
            // 等同 router.go(1);
            this.$router.forward();
            
            // 等同 router.go(-1);
            this.$router.back();
        },
    	// 动态路由
    	dynamic() {
            // 添加路由
            // 如果新增加的路由与当前位置相匹配,
            // 需要执行 router.push() 或 router.replace() 才显示新路由
            const removeRoute = router.addRoute({ path: '/about', name: 'about' component: About });
            
            // 将嵌套路由添加到现有路由中
            router.addRoute('about', { path: 'child', component: Child })
            
            // 名字相同时,重设路由
            router.addRoute({ path: '/other', name: 'about', component: Other });
            
            // 删除路由,所有的别名和子路由也会被同时删除
            // 根据添加路由的返回方法删除
            removeRoute();
            // 根据路由名称删除
            router.removeRoute('about');
            
            // 检查路由是否存在
            router.hasRoute('about');
            
            // 获取一个包含所有路由记录的数组
            router.getRoutes();
        }
	},
};

导航守卫

全局守卫

const router = VueRouter.createRouter({ ... });
// 前置守卫
router.beforeEach((to, from) => {
    // 重定向,参数同 router.push()
  	return { name: 'Login' };
  	// 取消导航
  	return false
    // 无返回,或者返回 true,则正常执行
});

// 解析守卫
router.beforeResolve((to, from) => {
    // 重定向,参数同 router.push()
  	return { name: 'Login' };
  	// 取消导航
  	return false
    // 无返回,或者返回 true,则正常执行
})

// 后置守卫
router.afterEach((to, from) => {
    // 不会改变导航本身,可以做一下辅助工作,如更改页面标题
})

路由守卫

function handle1(to, from) { }
function handle2(to, from) { }

const routes = [
    {
        path: "/about",
        component: About,
        // 只在进入不同路由时触发,params、query 或 hash 改变时不会触发
        beforeEnter: (to, from) => {
            // 重定向,参数同 router.push()
            return { name: 'Login' };
            // 取消导航
            return false
            // 无返回,或者返回 true,则正常执行
        },
        // 函数数组
        beforeEnter: [ handle1, handle2 ],
    },
];

组件守卫

export default {
  	// 导航确认前被调用,组件实例还未创建,不能访问 this
  	beforeRouteEnter(to, from, next) {
      	// 导航被确认的时候执行回调,通过 vm 访问组件实例
      	next(vm => {});
  	},
    // 在当前路由改变,但是该组件被复用时调用,如仅路径参数变化时
  	beforeRouteUpdate(to, from) { 
    },
    // 在导航离开该组件的对应路由时调用
  	beforeRouteLeave(to, from) {
    },
}

导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
Last Updated 2024/4/26 21:09:00