1.Vue路由

1.1.Vue路由基础

Vue属于单页应用(SPA),即整个应用程序中只有一个html页面。

在单页应用中(SPA),由于只是更改DOM来模拟多页面,所以页面浏览历史记录的功能就丧失了。此时,就需要前端路由来实现浏览历史记录的功能。

  1. <body>
  2. <div id="app">
  3. <p>
  4. <!-- 使用 router-link 组件来导航. to属性指定导航地址-->
  5. <router-link to="/home">home</router-link>&nbsp;
  6. <router-link to="/news">news</router-link>
  7. </p>
  8. <!-- 路由出口(路由匹配到的组件将渲染在这里) -->
  9. <router-view></router-view>
  10. </div>
  11. <script src="https://unpkg.com/vue@3.0.11"></script>
  12. <!-- 关于路由的库文件需要单独导入() -->
  13. <script src="https://unpkg.com/vue-router@4.0.6"></script>
  14. <!-- <script src="https://unpkg.com/vue-router@next"></script> -->
  15. <script type="text/javascript">
  16. // 1. 定义(路由)组件。
  17. const Home = {
  18. template: '<div>首页</div>'
  19. }
  20. const News = {
  21. template: '<div>新闻</div>'
  22. }
  23. // 2. 定义路由规则对象(每个路由应该映射一个组件)
  24. const routes = [
  25. {
  26. path: '/home',
  27. component: Home
  28. },{
  29. path: '/news',
  30. component: News
  31. }
  32. ]
  33. // 3. 创建 router 实例
  34. const router = VueRouter.createRouter({
  35. //配置路由模式
  36. history: VueRouter.createWebHashHistory(),
  37. //配置路由规则(当路由规则对象也为routes时,可简写为:routes)
  38. routes: routes
  39. })
  40. // 4. 将路由对象挂载到Vue实例上
  41. // 通过use(router)注入路由,从而让整个应用都有路由功能
  42. Vue.createApp({
  43. data() {
  44. return {}
  45. }
  46. }).use(router).mount('#app');
  47. </script>
  48. </body>

注意:

  • 上面代码中,router-link标签默认会被渲染成一个a标签
  • 路由模式有两种:
    • createWebHistory 路由模式:路径中不带#号。(生产环境下不能直接访问,需要进行转发)
    • createWebHashHistory 路由模式:路径中带#号。

路由重定向:上面代码中,我们应该设置打开浏览器就默认调整到 “首页”,所以需要把根路由/重定向到/home。 修改路由配置:

// 2. 定义路由规则对象(每个路由应该映射一个组件)
const routes = [
    {
        path: '/',            //根路由
        redirect: '/home'     //把根路由重定向到home
    },{
        path: '/home',
        component: Home
    },{
        path: '/news',
        component: News
    }
]

1.2.嵌套路由

实际应用界面,通常由多层嵌套的组件组合而成。 比如,我们 “首页”组件中,还嵌套着 “登录”和 “注册”组件,那么URL对应就是/home/login和/home/reg。

<body>
    <div id="app">
        <p>
            <!-- 使用 router-link 组件来导航. to属性指定导航地址-->
            <router-link to="/home">home</router-link>&nbsp;
            <router-link to="/news">news</router-link>
        </p>
        <!-- 路由出口(路由匹配到的组件将渲染在这里) -->
        <router-view></router-view>
    </div>

    <script src="https://unpkg.com/vue@3.0.11"></script>
    <script src="https://unpkg.com/vue-router@4.0.6"></script>

    <script type="text/javascript">
        // 1. 定义(路由)组件。
        const Home = {
            template: `<div>
                          <h2>首页</h2>
                          <router-link to="/home/login">登录</router-link>&nbsp;
                          <router-link to="/home/reg">注册</router-link>
                          <router-view></router-view>
                       </div>`
        }
        const News = {
            template: '<div>新闻</div>'
        }
        const Login = {
            template: '<div>登陆</div>'
        }
        const Reg = {
            template: '<div>注册</div>'
        }

        // 2. 定义路由规则对象(每个路由应该映射一个组件)
        const routes = [
            {
                path: '/',                //根路由
                redirect: '/home'         //把根路由重定向到home
            }, {
                path: '/home',
                component: Home,
                children: [               //配置子路由
                    {
                        path: '/home',
                        redirect: '/home/login'
                    }, {
                        path: '/home/login',
                        component: Login
                    }, {
                        path: '/home/reg',
                        component: Reg
                    }
                ]
            }, {
                path: '/news',
                component: News
            },
        ]

        // 3. 创建 router 实例
        const router = VueRouter.createRouter({
            //配置路由模式
            history: VueRouter.createWebHashHistory(),
            //配置路由规则
            routes: routes
        })

        // 4. 将路由对象挂载到Vue实例上
        // 通过use(router)注入路由,从而让整个应用都有路由功能
        Vue.createApp({
            data() {
                return {}
            }
        }).use(router).mount('#app');
    </script>
</body>

1.3.路由传参

路由传参有多种方式,这里我们学习两种:params与query。

1.3.1.params形式传参

<body>
    <div id="app">
        <p>
            <router-link to="/home">home</router-link>&nbsp;
            <router-link :to="{name:'News',params:{id:id,name:name}}">news</router-link>
        </p>
        <router-view></router-view>
    </div>

    <script src="https://unpkg.com/vue@3.0.11"></script>
    <script src="https://unpkg.com/vue-router@4.0.6"></script>

    <script type="text/javascript">
        const Home = {
            template: '<div>首页</div>'
        }
        const News = {
            template: `<div>新闻;  
                        参数1:{{$route.params.id}}; 
                        参数2:{{$route.params.name}}
                    </div>`
        }

        const routes = [
            {
                path: '/',
                redirect: '/home'
            }, {
                path: '/home',
                name: 'Home',              //每个路由规则中必须要有一个name属性
                component: Home
            }, {
                path: '/news',
                name: 'News',
                component: News
            }
        ]

        const router = VueRouter.createRouter({
            history: VueRouter.createWebHashHistory(),
            routes: routes
        })

        Vue.createApp({
            data() {
                return {
                    id:1,
                    name:'zhangsan'
                }
            }
        }).use(router).mount('#app');
    </script>
</body>

注意:

  1. 使用v-bind绑定to属性。
  2. to属性的值是一个json对象,此对象有两个属性:name属性和params属性。
  3. name属性就是要路由的对象。所以,在路由规则列表中,每一个路由规则都应用有一个name值。
  4. params属性就是要传递的参数。也是一个json对象。
  5. 组件接收参数时,使用 this.$route.params.参数名 的形式。

1.3.2.query形式传参

<body>
    <div id="app">
        <p>
            <router-link to="/home">home</router-link>&nbsp;
            <router-link :to="{path:'/news',query:{id:id,name:name}}">news</router-link>
        </p>
        <router-view></router-view>
    </div>

    <script src="https://unpkg.com/vue@3.0.11"></script>
    <script src="https://unpkg.com/vue-router@4.0.6"></script>

    <script type="text/javascript">
        const Home = {
            template: '<div>首页</div>'
        }
        const News = {
            template: `<div>新闻;  
                           参数1:{{$route.query.id}}; 
                           参数2:{{$route.query.name}}
                       </div>`
        }

        const routes = [
            {
                path: '/',
                redirect: '/home'
            }, {
                path: '/home',
                component: Home
            }, {
                path: '/news',
                component: News
            }
        ]

        const router = VueRouter.createRouter({
            history: VueRouter.createWebHashHistory(),
            routes: routes
        })

        Vue.createApp({
            data() {
                return {
                    id:1,
                    name:'zhangsan'
                }
            }
        }).use(router).mount('#app');
    </script>
</body>

注意:

  1. to属性的值仍然是一个josn对象,但是两个属性变了,一个是path,一个是query。
  2. path属性就是路由地址,对应路由规则中的path值。
  3. query属性就是要传递的参数。也是一个json对象。
  4. 组件接收参数时,使用 this.$route.query.参数名 的形式。

1.3.3.params方式与query方式的区别

query方式传值:

p05_02.png

params方式传值:

p05_03.png

总结:params方式与query方式的区别:

  1. query方式:类似于get方式,参数会在路由中显示,可以用做刷新后仍然存在的参数。利用路由规则中的path跳转。
  2. params方式:类似于post方式,参数不会在路由中显示,页面刷新后参数将不存在。利用路由规则中的name跳转。

1.3.4.restful风格传参

如果我们即想使用params方式传参,又不想在刷新时丢失参数,那么我们可以使用restful风格传参。

<body>
    <div id="app">
        <p>
            <router-link to="/home">home</router-link>&nbsp;
            <!--<router-link :to="{name:'News',params:{id:id,name:name}}">news</router-link>-->
            <router-link to="/news/100/lisi">news</router-link>
        </p>
        <router-view></router-view>
    </div>

    <script src="https://unpkg.com/vue@3.0.11"></script>
    <script src="https://unpkg.com/vue-router@4.0.6"></script>

    <script type="text/javascript">
        const Home = {
            template: '<div>首页</div>'
        }
        const News = {
            template: `<div>新闻;  
                        参数1:{{$route.params.id}}; 
                        参数2:{{$route.params.name}}
                    </div>`
        }

        const routes = [
            {
                path: '/',
                redirect: '/home'
            }, {
                path: '/home',
                name: 'Home',              //每个路由规则中必须要有一个name属性
                component: Home
            }, {
                path: '/news/:id/:name',
                name: 'News',
                component: News
            }
        ]

        const router = VueRouter.createRouter({
            history: VueRouter.createWebHashHistory(),
            routes: routes
        })

        Vue.createApp({
            data() {
                return {
                    id:1,
                    name:'zhangsan'
                }
            }
        }).use(router).mount('#app');
    </script>
</body>
  • 先在修改路由的path:’/news/:id/:name’
  • 在router-link标签中,就可以使用restful风格传参了:to=”/news/100/lisi”

2.编程式路由

2.1.利用JS实现路由跳转

router-link标签可以实现页面超链接形式的路由跳转。但是实际开发中,在很多情况下,需要通过某些逻辑判断来确定如何进行路由跳转。也就是说:需要在js代码中进行路由跳转。此时可以使用编程式路由。

  1. 使用this.$router.push方法可以实现路由跳转,方法的第一个参数可为string类型的路径,或者可以通过对象将相应参数传入。
  2. 通过this.$router.go(n)方法可以实现路由的前进后退,n表示跳转的个数,正数表示前进,负数表示后退。
  3. 如果只想实现前进后退可以使用this.05.Vue路由 - 图3%EF%BC%88%E5%89%8D%E8%BF%9B%E4%B8%80%E9%A1%B5%EF%BC%89%EF%BC%8C%E4%BB%A5%E5%8F%8Athis.#card=math&code=router.forward%28%29%EF%BC%88%E5%89%8D%E8%BF%9B%E4%B8%80%E9%A1%B5%EF%BC%89%EF%BC%8C%E4%BB%A5%E5%8F%8Athis.&id=cI0lp)router.back()(后退一页)。
<body>
    <div id="app">
        <p>
            <button @click="toHome">首页</button>
            <button @click="toNews">新闻</button>
            <button @click="toLogin">登陆</button>
            <button @click="doForward1">前进</button>
            <button @click="doForward2">前进</button>
            <button @click="doBack1">后退</button>
            <button @click="doBack2">后退</button>
        </p>
        <router-view></router-view>
    </div>

    <script src="https://unpkg.com/vue@3.0.11"></script>
    <script src="https://unpkg.com/vue-router@4.0.6"></script>

    <script type="text/javascript">
        const Home = {
            template: '<div>首页</div>'
        }
        const News = {
            template: '<div>新闻:{{$route.query.id}},{{$route.query.name}}</div>'
        }
        const Login = {
            template: '<div>登陆</div>'
        }

        const routes = [
            {
                path: '/',
                redirect: '/home'
            }, {
                path: '/home',
                component: Home
            }, {
                path: '/news',
                component: News
            }, {
                path: '/login',
                component: Login
            }
        ]

        const router = VueRouter.createRouter({
            history: VueRouter.createWebHashHistory(),
            routes: routes
        })

        Vue.createApp({
            data() {
                return {
                    id: 1,
                    name: 'zhangsan'
                }
            },
            methods: {
                toHome() {
                    //无参数时,push方法中直接写路由地址
                    this.$router.push('/home');
                },
                toNews() {
                    //有参数时,push方法中写一个json对象
                    this.$router.push({ path: '/news', query: { id:this.id, name:this.name } });
                },
                toLogin() {
                    this.$router.push('/login');
                },
                doForward1() {
                    this.$router.forward();
                },
                doForward2() {
                    this.$router.go(1);
                },
                doBack1() {
                    this.$router.back();
                },
                doBack2() {
                    this.$router.go(-1);
                }
            }
        }).use(router).mount('#app');
    </script>
</body>

2.2.通过watch实现路由监听

通过watch属性设置监听$route变化,达到监听路由跳转的目的。

在上面代码中添加watch监听:

watch: {
    // 监听路由跳转。
    $route(newRoute, oldRoute) {
        console.log('watch', newRoute, oldRoute)
    }
}

2.3.导航守卫

路由跳转前做一些验证,比如登录验证,是网站中的普遍需求。
对此,vue-route 提供了实现导航守卫(navigation-guards)的功能。

你可以使用 router.beforeEach()函数 注册一个全局前置守卫,每个守卫方法接收三个参数:

  1. to:即将要进入的目标路由对象(去哪里),可以使用 to.path 获取即将要进入路由地址。
  2. from:当前导航正要离开的路由对象(从哪来),可以使用 from.path 获取正要离开的路由地址。
  3. next:一个函数,表示继续执行下一个路由。(如果没有next,将不会进入到下一个路由)

下面例子中实现了如下功能:列举需要判断登录状态的 “路由集合”,当跳转至集合中的路由时:

  • 如果是“未登录状态”,则一律跳转到登录页面
  • 如果是“已登录状态”,则可以跳转到相应页面
<body>
    <div id="app">
        <p>
            <router-link to="/home">home</router-link>&nbsp;
            <router-link to="/news">news</router-link>&nbsp;
            <router-link to="/login">login</router-link>
        </p>
        <router-view></router-view>
    </div>

    <script src="https://unpkg.com/vue@3.0.11"></script>
    <script src="https://unpkg.com/vue-router@4.0.6"></script>

    <script type="text/javascript">
        const Home = {
            template: '<div>首页</div>'
        }
        const News = {
            template: '<div>新闻</div>'
        }
        const Login = {
            template: '<div>登录</div>'
        }

        const routes = [
            {
                path: '/',
                redirect: '/login'
            }, {
                path: '/home',
                component: Home
            }, {
                path: '/news',
                component: News
            }, {
                path: '/login',
                component: Login
            }
        ]

        const router = VueRouter.createRouter({
            history: VueRouter.createWebHashHistory(),
            routes: routes
        })

        Vue.createApp({
            data() {
                return { }
            }
        }).use(router).mount('#app');

        // 添加全局路由守卫
        router.beforeEach((to, from, next) => {
            //创建守卫规则集合(这里表示'/home'与'/news'路径是需要权限验证的)
            const nextRoute = ['/home', '/news'];
            // 使用isLogin来模拟是否登录
            let isLogin = false;
            // 判断to.path(要跳转的路径)是否是需要权限验证的
            if (nextRoute.indexOf(to.path) >= 0) {
                if (!isLogin) {
                    router.push({
                        path: '/login'
                    })
                    //location.reload(); 
                }
            }
            next(); //必须要有
        });
    </script>
</body>