zoukankan      html  css  js  c++  java
  • Vue-Router常规使用

    背景

    很多时候我们已经熟悉了框架的运用,但是有时候就是忘了怎么用

    所以这里想记下大部分的框架使用方法,方便使用的时候拷贝

    注:大部分文案以及示例拷贝自 Vue-Router 官方文档

    一、安装

    npm 方式

    npm install vue-router --save
    

    yarn 方式

    yarn add vue-router
    

    二、功能

    • 嵌套的路由/视图表

    • 模块化的、基于组件的路由配置

    • 路由参数、查询、通配符

    • 基于 Vue.js 过渡系统的视图过渡效果

    • 细粒度的导航控制

    • 带有自动激活的 CSS class 的链接

    • HTML5 历史模式或 hash 模式,在 IE9 中自动降级

    • 自定义的滚动条行为


    三、用法

    使用注意点(非常!非常!非常!重要的点!!!!!!!)

    路由匹配有时候会匹配到多个路由

    则匹配优先级为:先定义,先匹配

    以 / 开头的嵌套路径会被当作根路径

    一、基本

    1、引入以及安装

    import Vue from 'vue'
    import Router from 'vue-router'
    Vue.use(Router)
    

    2、平常用法

    // router.js
    import Vue from 'vue'
    import Router from 'vue-router'
    Vue.use(Router)
    export default new Router({
        routes: [
            {
                path: '/',
                name: 'dashboard',
                meta: { title: '首页', auth: true },
                component: () => import(`@/pages/project/views/dashborad/index.vue`)
            }
        ]
    })
    
    // ******************************************************
    
    // main.js
    import router from './router.js'
    const app = new Vue({
        router
    }).$mount('#app')
    

    3、嵌套路由

    export default new Router({
        routes: [
            {
                path: '/',
                name: 'user',
                meta: { title: '用户', auth: true },
                component: () => import(`@/pages/project/views/user/index.vue`)
                children: [
                    // 当路由没有匹配时候,可以设置一个 path 为 '',来进行渲染
                    {
                        path: '',
                        component: () => import(`@/pages/project/views/components/empty/index.vue`)
                    },
                    // this.$router.push('/user/zhangsan')
                    {
                        path: 'zangsan',
                        name: 'zhangsan',
                        meta: { title: '用户信息', auth: true },
                        component: () => import(`@/pages/project/views/components/post/index.vue`)
                    },
                    // 以 / 开头的嵌套路径会被当作根路径
                    // this.$router.push('/lisi')
                    {
                        path: '/lisi',
                        name: 'lisi',
                        meta: { title: '用户信息', auth: true },
                        component: () => import(`@/pages/project/views/components/post/index.vue`)
                    }
                ]
            }
        ]
    })
    

    4、动态匹配

    export default new Router({
        routes: [
            {
                // 匹配路径:
                // /User/zhangsan
                // /User/lisi
                path: '/User/:name',
                name: 'user',
                meta: { title: '用户信息页', auth: true },
    
                // 在 component 中,可以通过 `this.$route.params` 获取到动态参数 `:name` 的值
    
                // 如上面两个路径,取值方式为: 
                // /User/zhangsan
                // console.log(this.$route.params.name) // zhangsan
    
                // /User/lisi
                // console.log(this.$route.params.name) // lisi
                component: () => import(`@/pages/project/views/user/index.vue`)
            }
        ]
    })
    

    此种动态路由,在动态变化的时候,原来的组件实例会被复用,此时组件生命周期不会再被调用

    可以使用以下两种方式实现监听

    • 1、监听路由变化
    export default {
        watch: {
            '$route'(to, from) {
                // code
            }
        }
    }
    
    • 2、路由更新
    export default {
        // 在动态匹配路由场景下,复用的组件中才会生效
        beforeRouteUpdate(to, from, next) {
            // code
            // 注意,要记得使用 next(),否则程序无法执行下去
        }
    }
    

    5、路由模式

    • 默认 hash 模式
    export default new Router({
        mode: 'hash', // 默认  http://test.com/#/user
        mode: 'history', // http://test.com/user
        routes: []
    })
    
    • history 模式,需要后端配合

    Nginx 相应配置

    location / {
      try_files $uri $uri/ /index.html;
    }
    
    • 还需要配置 匹配不到路由 时候的情况
    export default new Router({
        routes: [
            {
                path: '*', // 匹配不到路由时,将会在这里匹配到,进而渲染
                component: NotFindComponent,
            }
        ]
    })
    

    6、路由懒加载

    export default new Router({
        routes: [
            {
                
                path: '/user/:id',
                component: import(/* webpackChunkName: "group-foo" */ './Foo.vue')
            }
        ]
    })
    

    特殊写法,使用 命名 chunk 将组件按组打包在一起

    import(/* webpackChunkName: "group-foo" */ './Foo.vue')
    import(/* webpackChunkName: "group-foo" */ './Bar.vue')
    import(/* webpackChunkName: "group-foo" */ './Baz.vue')
    

    Foo、Bar、Baz 将被打包到 group-foo 文件中


    7、滚动行为

    `注意: 这个功能只在支持 history.pushState 的浏览器中可用`
    export default new Router({
        routes: [],
        scrollBehavior (to, from, savedPosition) {
            // return 期望滚动到哪个的位置
            return { x: Number, y: Number } // x 横向滚动 y 纵向滚动
            return savedPosition // 滚动到之前保存的位置
            return { 
                selector: string, 
                offset: { x: Number, y: Number } // offset 只在 2.6.0+ 支持
            }
            if (to.hash) {  // 滚动到锚点
                return {
                    selector: to.hash
                }
            }
        }
    })
    

    savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用


    8、异步行为(2.8.0 新增)

    export default new Router({
        routes: [],
        scrollBehavior (to, from, savedPosition) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve({ x: 0, y: 0 })
                }, 500)
            })
        }
    })
    

    二、进阶

    1、匹配不到 404 路由

    export default new Router({
        routes: [
            {
                // 会匹配所有路径
                // 注意要放在最后一条路由,保证前面都没有匹配到的时候,这里匹配到进而展示提示用户 404
                path: '*',
                name: 'NotFound',
                meta: { title: '404' },
                component: () => import(`@/pages/project/views/404/index.vue`)
            }
        ]
    })
    

    使用通配符后 $route.params 会自动添加一个 pathMatch 参数用来记录 URL 被匹配到的部分

    // 1、
    { path: '*' }
    this.$router.push('/non-existing')
    this.$route.params.pathMatch // /non-existing
    
    // 2、
    { path: '/user/name-*' }
    this.$router.push('/user/name-zhangsan')
    this.$route.params.pathMatch // zhangsan
    
    

    2、重定向和别名

    • 2.1 重定向 (redirect)
    export default new Router({
        routes: [
            {
                
                path: '/b',
                redirect: '/c',
                redirect: {}, // 路由对象
                redirect: to => {
                    // 接收目标路由对象 to 作为参数
                    // return 字符串路径/一个路由对象
                }
    
            }
        ]
    })
    

    redirect 与 导航守卫

    
    例子:
    
    当前有三个路由 a、 b、 c
    
    b 配置了重定向 c
    
    当前路由为 a
    
    1、从 a 跳转到 b,
    
    2、因为 b 配置了重定向 c
    
    3、所以最后跳转到 c
    
    在这个过程中,a - b 的过程不会被导航守卫捕获
    
    最后跳转到 c 的过程,会被导航守卫捕获
    

    备注官方说明:注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上

    // a - b - c
    router.beforeEach((to, from, next) => {
        console.log('to :', to) 
        // 打印 to:{ fullPath: b }, 
        // a - b - c 跳转过程,并不会打印跳转 b 过程
        // 没研究具体跳转逻辑,可能内部根本没跳转 b ,只做了判断,直接跳转 c
        next()
    })
    
    • 2.2 别名

    备注官方说明: /a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

    export default new Router({
        routes: [
            {
                
                path: '/a',
                alias: '/b'
            }
        ]
    })
    

    3、路由组件传参

    • 目的是使组件与 $route 解耦

    例如一个组件需要获取 路由参数 $route.params.id

    那么就需要在组件的 template 模板中或者 script 中 使用 $route 对象

    这样该组件无法在 不使用路由或者不使用 $route 的情况下复用

    那么既然参数是从 $route.params 中取值,现在提供 路由组件传参 的方式代替他

    • props 属性

    路由对象增加一个 props 属性,将路由参数 $route.params 设置到需要复用组件的 组件属性

    不用在组件中使用 $route.params/$route.query ,最终实现组件复用

    备注官网说明:请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 Vue 才可以对状态变化做出反应

    export default new Router({
        routes: [
            {
                
                path: '/user/:id',
                component: User,
                // 布尔模式
                // 在组件中可以直接使用 this.id
                props: true // true: 将路由对象中 params 参数 id 设置到组件属性中 
                // 对象模式
                // { mode: 'static', id: 1 } 对象会被按原样设置为组件属性. 当 props 是静态的时候有用
                // 在组件中可以直接使用 this.mode  this.id
                props: { mode: 'static', id: 1 } 
                // 函数模式
                // 在组件中可以直接使用 this.params  this.query
                props: (route) => ({ 
                        params: route.params.id, 
                        query: route.query.name 
                    }) 
                }
            }
        ]
    })
    

    4、路由动效

    • 使用 transition 配合 router-view 组件实现动效

    动态的或者静态的 动画/过度 name

    <transition :name="transitionName">
      <router-view></router-view>
    </transition>
    
    /* 进入状态变化:
    
    1、元素插入前: 
    .transitionName-enter 生效,给定元素一个初始状态
    .transitionName-enter-active 生效,设置过度状态的时间
    
    2、元素插入后,动画/过度变化状态
    .transitionName-enter 被删除
    .transitionName-enter-active 生效,过度时间持续生效
    .transitionName-enter-to 生效,设置过度的目标状态
    
    3、动画/过度结束状态
    .transitionName-enter-active 被删除
    .transitionName-enter-to 被删除
    
    
    备注:
    .transitionName-enter,.transitionName-enter-active 一开始觉得两者作用有点重复,
    但是 .transitionName-enter 的使命是给与元素一个初始状态,元素被插入之后,该类名就会被删除;
    如果用 .transitionName-enter-active 来给定元素初始状态,
    在动画/过度过程中,.transitionName-enter-active 没有被删除,会一直存在,持续生效;
    这时 .transitionName-enter-to 生效,设置元素过度的目标状态;
    那么要保证 .transitionName-enter-to 的作用优先级要比 .transitionName-enter-active 大才行。
    
    为了明确各自的作用,建议还是使用 三种类名,来区分生效作用 */
    
    /* 1、元素插入前状态 */
    .transitionName-enter {
      opacity: 0;
    }
    /* 2、元素插入动画/过度状态 */
    .transitionName-enter-active, {
      transition: opacity .5s;
    }
    /* 3、元素插入完成时状态 2.1.8+版本 */
    .transitionName-enter-to, {
      opacity: 1;
    }
    
    
    /* 
    离开状态变化
    
    1、元素移除前: 
    .transitionName-leave 生效,给定元素一个初始状态
    .transitionName-leave-active 生效,设置过度状态的时间
    
    2、元素移除前,动画/过度变化状态
    .transitionName-leave 被删除
    .transitionName-leave-active 生效,过度时间持续生效
    .transitionName-leave-to 生效,设置过度的目标状态
    
    3、动画/过度结束状态,元素被移除
    .transitionName-leave-active 被删除
    .transitionName-leave-to 被删除 */
    
    /* 元素移除前状态 */
    .transitionName-leave {
      opacity: 1;
    }
    /* 元素移除动画/过度状态 */
    .transitionName-leave-active {
      transition: opacity .5s;
    }
    /* 元素移除完成时状态 2.1.8+版本 */
    .transitionName-leave-to, {
      opacity: 0;
    }
    

    四、组件内常用操作

    一、this.$router

    • this.$router.push/this.$router.replace

    对应 window.history.pushState/window.history.replaceState

    // 字符串
    this.$router.push('/user')      // /user
    
    // 对象
    this.$router.push({             // /user
        path: '/user'
    })
    
    // 命名路由
    this.$router.push({             // /user/123
        name: 'user',
        params: {
            userId: '123'
        }
    })
    
    // 带查询参数
    this.$router.push({             // /user?userId=123
        path: '/user',
        query: {
            userId: '123'
        }
    })
    
    // 带查询参数,
    // 若存在 path,则 params 会失效 
    this.$router.push({             // /user
        path: '/user',
        params: { // 不起作用
            userId: '123'
        }
    })
    

    replacepush 的操作一致

    push 会向浏览器 history 添加一条记录,replace 不会添加,会直接替换当前记录

    • 两个可选参数

    pushreplace 还支持其他两个可选参数

    this.$router.push(location, onComplete?, onAbort?)
    this.$router.replace(location, onComplete?, onAbort?)
    

    onComplete 在导航完成时执行(所有异步钩子被解析)

    onAbort 在导航终止时执行(导航到相同路由、在导航完成之前导航到另外一个不同的路由)

    • 跳转建议:
    this.$router.push({             // /user/123
        name: 'user',
        params: {
            userId: '123'
        }
    })
    

    建议使用 name 跳转,因为 name 不会变,但是路径可能会变

    路径变了,若我们使用 path 跳转,那么我们的代码也要修改

    但是如果我们使用 name 进行跳转,那么代码就无需改动

    • go/back

    对应 window.history.go/window.history.back

    // 前进
    this.$router.go()
    this.$router.go(1)
    this.$router.go(-1)  === this.$router.back()
    
    // 后退
    this.$router.back()
    this.$router.back(1)
    this.$router.back(-1) === this.$router.go()
    
    // history 记录不够用,则失败,不做任何操作
    this.$router.go(100)
    this.$router.back(100)
    
    • 完整的导航解析流程

    导航被触发。

    在失活的组件里调用离开守卫。

    调用全局的 beforeEach 守卫。

    在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。

    在路由配置里调用 beforeEnter。

    解析异步路由组件。

    在被激活的组件里调用 beforeRouteEnter。

    调用全局的 beforeResolve 守卫 (2.5+)。

    导航被确认。

    调用全局的 afterEach 钩子。

    触发 DOM 更新。

    用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。


    二、this.$route

    • this.$route.params
    this.$router.push({             // /user/123
        name: 'user',
        params: {
            userId: '123'
        }
    })
    
    • this.$route.query

    以下例子若不刷新跳转,下一个路由接收到的 userId 是个数值类型: 123

    如果跳转过去后,刷新页面,那么 userId 将变成 字符串类型: '123'

    // 带查询参数
    this.$router.push({             // /user?userId=123
        path: '/user',
        query: {
            userId: 123
        }
    })
    
    • 监听路由变化,多用于动态路由匹配,解决页面复用,导致生命周期不执行的问题
    export default {
        watch: {
            '$route'(to, from) {
                // code
            }
        }
    }
    

    三、beforeRouteEnter

    使用 beforeRouteEnter 实现获取数据后,再渲染dom

    beforeRouteEnter(to, from, next) {
        // 在当前无法使用组件实例
        // 获取详情
        getDetail(to.query.id - 0)
            .then(res => {
                console.log('beforeRouteEnter>>>then :', res)
                next(vm => {
                    // 这里可以使用组件实例 vm
                    vm.setDetail(res, vm)
                })
            })
            .catch(e => {
                console.log('beforeRouteEnter>>>error :', e)
            })
    },
    

    四、beforeRouteUpdate

    export default {
        // 在动态匹配路由场景下,复用的组件中才会生效
        beforeRouteUpdate(to, from, next) {
            // code
            // 注意,要记得使用 next(),否则程序无法执行下去
        }
    }
    

    使用 router-link 导航

    简单用法使用 to 属性来指定路径

    <router-link to="/dashboard"></router-link>
    

    复杂用法使用 to 绑定一个 配置对象

    <router-link :to="{ path: '/dashboard', query: { id: 2}}"></router-link>
    

    默认会被渲染为一个 a 标签,可以使用 tag 属性来指定渲染的标签类型

    <router-link :to="{ path: '/dashboard', query: { id: 2}}" tag="div"></router-link>
    

    匹配到路由后,渲染后的标签将会添加类名 router-link-active


    2、router-view

    使用 router-view 来给配置的路由组件占位

    匹配到路由后,会被替换掉,其实是一个占位标签

    路由对象

    // router
    export default new Router({
        routes: [
            {
                path: '/',
                // 注意区分 component 和  components
    
                // component: commonComponent // 匹配单一组件
    
                components: { // 匹配多组件
                    default: defaultComponent, // router-view
                    a: aComponent, // router-view name="a"
                    b: bComponent, // router-view name="b"
                }
            }
        ]
    })
    

    html 结构

    <div id="app">
        <!-- 渲染 defaultComponent -->
        <router-view class="view one"></router-view>
        <!-- 渲染 aComponent -->
        <router-view class="view two" name="a"></router-view>
        <!-- 渲染 bComponent -->
        <router-view class="view three" name="b"></router-view>
    </div>
    

    同个组件,多个 router-view,可以通过指定具名组件,来渲染对应的 router-view

    匹配多个组件,路由对象 中使用 components 对象来存储,而不是 component


    没有 name 属性,默认渲染-->匹配到的路由对象components 对象中-->属性名为 default 的组件

    <!-- template -->
    <router-view class="view one"></router-view>
    

    name 属性为 a,渲染-->匹配到的路由对象中 components 对象中-->属性名为 a 的组件

    <router-view class="view two" name="a"></router-view>
    

    name 属性为 b,渲染-->匹配到的路由对象中 components 对象中-->属性名为 b 的组件

    <router-view class="view three" name="b"></router-view>
    
    都读到最后了、留下个建议如何
  • 相关阅读:
    不可编辑属性
    按钮事件--嵌套事件(冒泡)--带参数事件--双向数据绑定
    微信-文件组成-模块作用--定义全局方法变量---数据绑定--wx:for循环列表
    自定义指令----focus获取焦点事件
    处理字符串
    Vue-过滤器filters--数据处理--主要用于时间格式化
    MVC系列教材 (二)- 结合Servlet和JSP 实现查询功能
    MVC系列教材 (一)- 教程
    JSP系列教材 (十一)- EL表达式语言
    JSP系列教材 (十)- JSTL Java Standard Tag Library 标准标签库
  • 原文地址:https://www.cnblogs.com/linjunfu/p/12177592.html
Copyright © 2011-2022 走看看