0
点赞
收藏
分享

微信扫一扫

【Vue】Router

洒在心头的阳光 2022-02-10 阅读 70

路由简介

  1. 后端路由:根据不同的用户 URL 请求,服务器返回不同的内容
    本质是:URL 请求与服务器资源之间的对应关系
  2. 前端路由:根据不同的用户事件,显示不同的页面内容
    本质是:用户事件与事件处理函数之间的对应关系
  • SPA,单页面应用程序:不会打开新页面,支持前后回退

Vue Router

  • Vue Router 是 Vue.js 的路由管理器,可以简便地构建单页面应用
  • 未使用的路由组件会被自动销毁,使用时再重新创建

基本使用

  1. 引入 Vue Router:npm install vue-router@4

  2. 创建 router 文件:创建 router 文件夹,在该文件夹下创建 router 文件

router
---- index.js // 主 router 文件

index.js:主路由文件。在路由文件中,设置路由规则,并创建 VueRouter 实例

懒加载component: () => import("vue 组件路径"),
一般除了初始页面,其他页面都使用懒加载

import Vue from 'vue'
import VueRouter from 'vue-router'

// 引入组件 Home
import Home from '../views/Home.vue'

// 通过插件启动,该指令需要在 VueRouter 创建前调用
Vue.use(VueRouter)

// 设置路由规则
const routes = [{
        path: '/', // 设置路由路径
        name: 'Home', // 设置组件名称
        component: Home // 使用组件 Home
    },
    {
        path: '/about',
        name: 'About',
        // 懒加载
        component: () => import('../views/About.vue')
    }
]

// 创建 VueRouter 实例
const router = new VueRouter({
    routes
})

export default router
  1. 在入口文件 main.js 中导入主 router 文件
import Vue from 'vue'
import App from './App.vue'
import router from './router' // 引入 router 文件
import store from './store'

Vue.config.productionTip = false

new Vue({
    router, // 将 router 文件导入到 Vue 中
    store,
    render: h => h(App)
}).$mount('#app')
  1. 定义路由组件:

页面存储在 views 文件夹下:

views
---- About.vue
---- Home.vue

组件存储在 components 文件夹下:

components
---- HelloWorld.vue
  1. 在主 Vue Component (App) 中添加路由
    添加路由链接:<router-link to="/路由路径"> 内容 </router-link>
    添加路由占位符:<router-view> </router-view> || <router-view />
<template>
    <div id="app">
        <div id="nav">
            <!-- 添加路由链接,并设置 to 属性 -->
            <router-link to="/">Home</router-link> |
            <router-link to="/about">About</router-link>
        </div>
        <!-- 添加路由占位符 -->
        <router-view />
    </div>
</template>

<script>
export default {
    name: "App",
};
</script>

路由嵌套

  1. 设置路由规则:
import Vue from 'vue'
import VueRouter from 'vue-router'

import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [{
    path: '/home',
    name: 'Home',
    component: Home,
    children: [{ // 设置 children 属性添加子路由,属性值为数组
        path: 'son', // 子路由的 path 属性值不需要 / 前缀
        name: 'Son',
        component: () => import('../views/Son.vue')
    }]
}]

const router = new VueRouter({
    routes
})

export default router
  1. 配置组件:

设置路由路径时:
① 直接在 router-link 标签中设置 to 属性,属性值就是路由路径
② 绑定 to 属性,以对象形式,通过组件的 name 属性值,设置路由路径
③ 绑定 to 属性,以对象形式,通过组件的 path 属性值,设置路由路径

<template>
    <div id="app">
        <div id="nav">
            <!-- 直接写路由路径 -->
            <router-link to="/home">Home</router-link>

            <!-- 绑定 to 属性,以对象的形式,通过组件的 name 属性,设置路由路径 -->
            <!-- <router-link :to="{ name: 'Home' }">Home</router-link> -->

            <!-- 绑定 to 属性,以对象的形式,通过组件的 path 属性,设置路由路径 -->
            <!-- <router-link :to="{ path: '/home' }">Home</router-link> -->
        </div>
        <router-view />
    </div>
</template>

<script>
export default {
    name: "App",
};
</script>
<template>
    <div class="home">
        <h1>This is an Home page</h1>
        <hr />
        <router-link to="/home/son">Son</router-link>
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: "Home",
};
</script>
<template>
    <div class="son">
        <h2>Son.vue</h2>
    </div>
</template>

<script>
export default {
    name: "Son",
};
</script>

query

query 用于:父路由给子路由传递数据
① 绑定 to 属性,以对象的形式,设置路由路径 并传递父路由中的数据
② 绑定 to 属性,直接写路由路径 并传递父路由中的数据

<template>
    <div class="home">
        <h1>This is an Home page</h1>
        <ul>
            <!-- 遍历所有的数据 -->
            <li v-for="item in arr" :key="item.id">
                <!-- 绑定 to 属性,以对象的形式,设置路由路径并传递父组件中的数据 -->
                <router-link
                    :to="{
                        // 通过组件的 name 属性,设置路由路径
                        name: 'Son',
                        // 通过组件的 path 属性,设置路由路径
                        // path: '/home/son',
                        // 通过 query 属性,传递父组件中的数据
                        query: {
                            way: '对象',
                            id: item.id,
                            name: item.hero,
                        },
                    }"
                    >{{ item.hero }}
                </router-link>
                |
                <!-- 绑定 to 属性,以字符串模版的形式,设置路由路径并传递父组件中的数据 -->
                <router-link
                    :to="`/home/son?way=字符串模版&id=${item.id}&name=${item.hero}`"
                    >{{ item.hero }}
                </router-link>
            </li>
        </ul>
        <hr />
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: "Home",
    data() {
        return {
            arr: [
                {
                    id: "001",
                    hero: "卡莎",
                },
                {
                    id: "002",
                    hero: "火男",
                },
                {
                    id: "003",
                    hero: "卡萨",
                },
            ],
        };
    },
};
</script>
  • 在子路由中获取父路由中的数据:
    &route:点击的路由组件的信息对象
    &router:VueRouter 的实例化对象
  • 通过当前的路由对象 &route 取值:&route.query.属性值
<template>
    <div class="son">
        <!-- 在子组件中,通过 “ &route.query.属性值 ” 获取父组件中的数据 -->
        <p>way:{{ $route.query.way }}</p>
        <p>id:{{ $route.query.id }}</p>
        <p>name:{{ $route.query.name }}</p>
    </div>
</template>

<script>
export default {
    name: "Son",
};
</script>

params

除了通过 query 传参,我们还可以通过 params 传参

此时,router 文件该如下设置:接收参数的路由的 path 属性值应改成如下格式

import Vue from 'vue'
import VueRouter from 'vue-router'

import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [{
    path: '/home',
    name: 'Home',
    component: Home,
    children: [{
        path: 'son/:way/:id/:name', // 动态路由
        name: 'Son',
        component: () => import('../views/Son.vue')
    }]
}]

const router = new VueRouter({
    routes
})

export default router
  • 父路由通过对象 / 字符串模版的形式传参,相比较 query,需要修改一下格式
  • 通过 params 接收数据时,不可以使用 path 属性设置路由,需要使用 name 属性设置路由
<template>
    <div class="home">
        <h1>This is an Home page</h1>
        <ul>
            <!-- 遍历所有的数据 -->
            <li v-for="item in arr" :key="item.id">
                <!-- 绑定 to 属性,以对象的形式,设置路由路径并传递父组件中的数据 -->
                <router-link
                    :to="{
                        // 通过组件的 name 属性,设置路由路径
                        name: 'Son',
                        // 通过 params 属性,传递父组件中的数据
                        params: {
                            way: '对象',
                            id: item.id,
                            name: item.hero,
                        },
                    }"
                    >{{ item.hero }}
                </router-link>
                |
                <!-- 绑定 to 属性,以字符串模版的形式,设置路由路径并传递父组件中的数据 -->
                <router-link
                    :to="`/home/son/字符串模版/${item.id}/${item.hero}`"
                    >{{ item.hero }}
                </router-link>
            </li>
        </ul>
        <hr />
        <router-view></router-view>
    </div>
</template>

子路由通过 &route.params.属性值 获取参数:

<template>
    <div class="son">
        <!-- 在子组件中,通过 “ &route.params.属性值 ” 获取父组件中的数据 -->
        <p>way:{{ $route.params.way }}</p>
        <p>id:{{ $route.params.id }}</p>
        <p>name:{{ $route.params.name }}</p>
    </div>
</template>

<script>
export default {
    name: "Son",
};
</script>

props

在 router 文件中设置 props 属性,可较方便地在路由中接收参数。简化参数的传递

const routes = [{
    path: '/home',
    name: 'Home',
    component: Home,
    children: [{
        path: 'son/:way/:id/:name', // 动态路由
        name: 'Son',
        component: () => import('../views/Son.vue'),
        // 方法1:props 对象,传递属性及其属性值
        props: {
            msg: "props中的数据"
        }
    }]
}]
<template>
    <div class="son">
        <!-- 在子组件中,通过 “ &route.params.属性值 ” 获取父组件中的数据 -->
        <p>way:{{ $route.params.way }}</p>
        <p>id:{{ $route.params.id }}</p>
        <p>name:{{ $route.params.name }}</p>

        <!-- 在 template 标签中直接使用 -->
        <p>{{ msg }}</p>
    </div>
</template>

<script>
export default {
    name: "Son",
    props: ["msg"], // 在路由中设置 props 属性接收数据
};
</script>
// 方法2:布尔值,表示父路由的所有数据都通过 props 传递
props: true
<template>
    <div class="son">
        <!-- 在 template 标签中直接使用 -->
        <p>way:{{ way }}</p>
        <p>id:{{ id }}</p>
        <p>name:{{ name }}</p>
    </div>
</template>

<script>
export default {
    name: "Son",
    props: ["way", "id", "name"], // 在路由中设置 props 属性接收数据
};
</script>
// 方法3:方法,接收 1 个参数,为 $route 对象
props(route) {
    return {
        id: route.params.id,
        name: route.params.name,
        way: route.params.way,
    }
}

需要注意的是,方式 1 2 都只可以传递 params 参数;方式 3 还可以传递 query 参数

const routes = [{
    path: "/",
    name: "Hero",
    component: Hero,
    children: [{
        path: "myself",
        name: "MyHero",
        component: MyHero,
        children: [{
            // query 使用静态路由地址
            path: "show",
            name: "SomeOne",
            component: SomeOne,
            // 接收 $route 对象作为参数
            props(route) {
                return {
                    // 这里通过 query 获取数据
                    id: route.query.id,
                    name: route.query.name,
                    way: route.query.way,
                }
            }
        }]
    }, {
        path: "his",
        name: "HisHero",
        component: HisHero,
    }]
}, {
    path: "/about",
    name: "About",
    component: () =>
        import("../views/About.vue"),
}, ];
<template>
    <div class="home">
        <h1>This is an Home page</h1>
        <ul>
            <!-- 遍历所有的数据 -->
            <li v-for="item in arr" :key="item.id">
                <!-- 绑定 to 属性,以对象的形式,设置路由路径并传递父组件中的数据 -->
                <router-link
                    :to="{
                        // 通过组件的 name 属性,设置路由路径
                        name: 'Son',
                        // 通过 query 属性,传递父组件中的数据
                        query: {
                            way: '对象',
                            id: item.id,
                            name: item.hero,
                        },
                    }"
                    >{{ item.hero }}
                </router-link>
                |
                <!-- 绑定 to 属性,以字符串模版的形式,设置路由路径并传递父组件中的数据 -->
                <router-link
                    :to="`/home/son?way=字符串模版&id=${item.id}&name=${item.hero}`"
                    >{{ item.hero }}
                </router-link>
            </li>
        </ul>
        <hr />
        <router-view></router-view>
    </div>
</template>

路由重定向

rediract:"指定路由地址",原本链接到 a 地址 → 链接到 b 地址

let router = new VueRouter({
    routes: [{
        path: "/", // 原路由地址
        redirect: "/user" // 重定向的路由地址
    }, {
        path: '/user',
        component: User
    }, {
        path: '/register',
        component: register
    }]
});

组件缓存

  • 组件不用时,默认会被销毁
  • 此时我们可以使用 keep-alive 标签包裹路由出口 router-view
    此时,在该路由出口显示的所有路由都不会被销毁
<keep-alive><router-view /></keep-alive>
  • 我们可以设置 include="组件名"|
    此时只有指定组件会被缓存
<keep-alive include="Home"><router-view /></keep-alive>
  • 当需要缓存多个组件时,我们可以绑定 include 属性,并传入一个数组参数
<keep-alive :include="['Home', 'About']"><router-view /></keep-alive>
  • 我们也可以设置 exclude 属性,表示除了指定组件,其他组件都会被缓存
    书写格式与 include 属性一样
<keep-alive exclude="Home"><router-view /></keep-alive>
  • activated() {} keep-alive 缓存的组件激活时调用
  • deactivated() {} keep-alive 缓存的组件失活时调用
activated() {
    console.log("进入缓存组件");
},
deactivated() {
    console.log("离开缓存组件");
},

编程式路由

import Vue from 'vue'
import VueRouter from 'vue-router'

import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [{
        path: '/home',
        name: 'Home',
        component: Home,
        children: [{
            path: 'son', // 静态路由
            name: 'Son',
            component: () => import('../views/Son.vue'),
            // 接收 $route 对象作为参数
            props(route) {
                return {
                    // 这里通过 query 获取数据
                    id: route.query.id,
                    name: route.query.name,
                    way: route.query.way,
                }
            }
        }]
    },
    {
        path: '/about',
        name: 'About',
        component: () => import('../views/About.vue')
    }
]

const router = new VueRouter({
    routes
})

export default router

push & replace

  • this.$router.push("指定路由地址"):跳转到指定路由地址
<template>
    <div id="app">
        <div id="nav">
            <router-link to="/home">Home</router-link> |
            <router-link :to="{ name: 'About' }">About</router-link> |
            <button @click="jump">jump Son.vue</button>
        </div>
        <router-view />
    </div>
</template>

<script>
export default {
    name: "App",
    methods: {
        jump() {
            this.$router.push("/home/son");
        },
    },
};
</script>
  • 使用 push 跳转,会有历史记录
  • 如果不想产生历史记录,可以使用 replace 代替 push 跳转
this.$router.replace("/myself");
  • 字符串形式
// 传递 query 数据
this.$router.push("/home/son?way=push&id=01&name=superman");
// 传递 params 数据
this.$router.push("/home/son/push/01/superman");
  • 对象形式
this.$router.push({
    name: "Son",
    // 传递 params 数据则该属性名为 params
    query: {
        way: "push",
        id: "01",
        name: "superman",
    },
});

跳转历史记录

  • this.$router.forward() 前进
  • this.$router.back() 后退
  • this.$router.go(num):num > 0,则前进;num < 0,则回退
<template>
    <div id="app">
        <div id="nav">
            <router-link to="/home">Home</router-link> |
            <router-link :to="{ name: 'About' }">About</router-link> |
            <button @click="jump">jump Son.vue</button> |

            <button @click="forward">前进</button> |
            <button @click="back">后退</button>
        </div>
        <router-view />
    </div>
</template>

<script>
export default {
    name: "App",
    methods: {
        jump() {
            this.$router.replace({
                name: "Son",
                query: {
                    way: "push",
                    id: "01",
                    name: "superman",
                },
            });
        },
        forward() {
            this.$router.forward();
        },
        back() {
            this.$router.back();
        },
    },
};
</script>

导航守卫

vue-router 提供的导航守卫主要用来通过跳转 / 取消的方式守卫导航

全局路由守卫

  • router.beforeEach((to, from, next) => {}):全局前置守卫,路由进入前触发
  • 当一个导航触发时,全局前置守卫按照创建顺序调用
  • 每个守卫方法接收 3 个参数:
    to: 即将要进入的目标
    from: 当前导航正要离开的路由
    next:放行函数,不调用则会一直卡在这里,无法执行后面的函数
// 全局前置守卫,任何路由进入之前触发
router.beforeEach((to, from, next) => {
    console.log("to", to);
    console.log("from", from);
    next(); // 放行
});
  • router.afterEach(to, from):全局后置守卫,路由离开后触发
  • 每个守卫方法接收 2 个参数:
    to: 即将要进入的目标
    from: 当前导航正要离开的路由
  • 对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用
// 全局后置守卫,任何路由离开后触发
router.afterEach((to, from) => {
    console.log("to", to);
    console.log("from", from);
    document.title = to.name; // 修改页面标题
});

指定路由守卫

  • beforeEnter: (to, from, next) => {}:指定路由守卫,路由进入前触发
  • 指定路由守卫只有进入前触发,没有离开后触发
  • 直接在路由配置上定义 beforeEnter 守卫:
const routes = [{
    path: '/home',
    name: 'Home',
    component: Home,
    children: [{
        path: 'son',
        name: 'Son',
        component: () => import('../views/Son.vue'),
        props(route) {
            return {
                id: route.query.id,
                name: route.query.name,
                way: route.query.way,
            }
        },
        // 指定路由守卫
        beforeEnter: (to, from, next) => {
            console.log("to", to);
            console.log("from", from);
            next(); // 放行
        },
    }]
}, {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
}]

组件内的守卫

  • 页面路由进入 / 离开,一般在模版中设置:
  1. beforeRouteEnter(to, from, next):路由进入前触发
  2. beforeRouteLeave(to, from, next):路由离开后触发
  3. beforeRouteUpdate(to, from, next):路由复用时触发
<script>
export default {
    name: "Son",
    props: ["way", "id", "name"],
    beforeRouteEnter(to, from, next) {
        console.log("路由进入前触发");
        console.log("to", to);
        console.log("from", from);
        next();
    },
    beforeRouteLeave(to, from, next) {
        console.log("路由离开后触发");
        console.log("to", to);
        console.log("from", from);
        next();
    },
    beforeRouteUpdate(to, from) {
        console.log("路由组件复用时触发");
        console.log("to", to);
        console.log("from", from);
    },
};
</script>

beforeRouteEnter 方法中,无法直接获取 this 对象

beforeRouteEnter(to, from, next) {
    console.log("路由进入之前触发", this); // 这里的 this 为 undefined
    next((vm) => { // 但是我们可以通过 next 的回调函数获取
        console.log("vm", vm);
    });
},

完整的导航解析流程

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

路由元信息

  • 用于将任意信息附加到路由上,如过渡名称、谁可以访问路由等
  • 这些事情可以通过接收属性对象的 meta 属性来实现,并且它可以在路由地址和导航守卫上都被访问到

定义路由的时候你可以这样配置 meta 字段:

const routes = [{
    path: '/home',
    name: 'Home',
    component: Home,
    meta: { // 设置 meta 属性
        name: 'myHome'
    },
    children: [{
        path: 'son',
        name: 'Son',
        component: () => import('../views/Son.vue'),
        props(route) {
            return {
                id: route.query.id,
                name: route.query.name,
                way: route.query.way,
            }
        },
        meta: { // 设置 meta 属性
            name: 'mySon'
        },
    }]
}, {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue'),
    meta: { // 设置 meta 属性
        name: 'myAbout'
    },
}]

const router = new VueRouter({
    routes
})

// 全局后置守卫,任何路由离开后触发
router.afterEach((to, from) => {
    document.title = to.meta.name; // 获取 meta 中的数据
});

mode

mode:路由模式 abstract、hash(默认)、history

  • hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器
  • history: 依赖 HTML5 History API 和服务器配置
  • abstract: 支持 JS 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式
const router = new VueRouter({
    mode: "abstract",
    routes
})

mode: 'history' 配置已经被一个更灵活的 history 配置所取代。根据你使用的模式,你必须用适当的函数替换它:

  • "history": createWebHistory()
  • "hash": createWebHashHistory()
  • "abstract": createMemoryHistory()

下面是一个完整的代码段:

import { createRouter, createWebHistory } from 'vue-router'
// 还有 createWebHashHistory 和 createMemoryHistory

createRouter({
  history: createWebHistory(),
  routes: [],
})
举报

相关推荐

Vue Router

Vue路由-Vue Router

[Vue]Vue-router

Vue-router

Vue_Router

Vue 路由 Router

Vue Router入门

0 条评论