zoukankan      html  css  js  c++  java
  • 二、VueRouter ---kkb

    vue-router

    安装:

    vue add router

    配置

    // router.js
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Home from '../views/Home.vue'
    
    Vue.use(VueRouter) // 引入Router插件
    
    const routes = [
      {
        path: '/',
        name: 'home',
        component: () => import('../components/form')
      },
      {
        path: '/test',
        name: 'test',
        // 懒加载
        component: () => import('../components/formTest')
      }
    ]
    const router = new VueRouter({
      mode: 'history', // 模式:hash | history | abstract
      base: process.env.BASE_URL, // http://localhost:8080/cart
      routes
    })
    
    export default router

    指定路由器

    // main.js
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')

    路由视图

    <router-view/>

    导航链接

    <router-link to="/">Home</router-link> 
    <router-link to="/about">About</router-link>

    路由嵌套

      应用界面通常由多层嵌套的组件组合而成。同样的,URL中各段动态路径也按某种结构对应嵌套的各层组件。

    配置嵌套路由,router.js

    {
      path: "/",
      component: Home,
      children: [{ path: "/list", name: "list", component: List }]
    }

    动态路由

    我们经常需要把某种模式匹配到的所有路由,全部映射到同一个组件

    详情页路由配置,router.js

    {
      path: '/',
      name: 'home',
      component: () => import('../components/form'),
      children: [
        { path: '/detail/:id', component: Detail }
      ]
    }

    获取动态路由的参数

    <template>
      <div>
        <h2>商品详情</h2>
        <p>{{$route.params.id}}</p>
      </div>
    </template>

    另一种传参方式:传递路由组件参数 props

    { path: "detail/:id", component: Detail, props: true }

    组件中以属性方式获取:

    export default { props: ['id'] }

    路由守卫

    路由导航过程中有若干生命周期钩子,可以在这里实现逻辑控制

    • 全局守卫,router.js
    // 路由配置
    {
      path: "/about",
      name: "about",
      meta: { auth: true }, // 需要认证
      component: () => import('../components/About.vue')
    }
    
    // 全局守卫
    router.beforeEach((to, from, next) => {
      // 要访问 /about 且未登录需要去登录
      if(to.meta.auth && !window.isLogin){
        if(window.confirm("请登录")){
          window.isLogin = true
          next() // 登录成功,继续
        } else {
          next('/') // 放弃登录,回首页
        }
      } else {
        next() // 不需登录,继续
      }
    })
    • 路由独享守卫
    {
      path: '/test',
      name: 'test',
      component: () => import('../components/formTest'),
      beforeEnter: (to, from, next) => {
        // 路由内部知道自己需要认证
        if(!window.isLogin){
          // ...
        }else{
          next()
        }
      }
    }
    • 组件内的守卫
    export default {
      beforeRouteEnter(to, from, next) { },
      beforeRouteUpdate(to, from, next) { },
      beforeRouteLeave(to, from, next) { }
    }

    vue-router 拓展

    动态路由

    利用 $router.addRoutes() 可以实现动态路由添加,常用于用户权限控制。

    // router.js  --伪码
    // 返回数据可能是这样的
    // [{
    //   path: '/',
    //   name: 'home',
    //   component: 'Home'
    // }]
    
    // 异步获取路由
    api.getRoutes().then(routes => {
      const routeConfig = routes.map(route => mapComponent(route))
      router.addRoutes(routeConfig)
    })
    
    // 映射关系
    const compMap = {
      'Home': () => import("./view/Home.vue")
    }
    
    // 递归替换
    function mapComponent(route) {
      route.component = compMap[route.component]
      if(route.children) {
        route.children = route.children.map(child => mapComponent(child))
      }
      return route
    }

    面包屑

    利用 $route.matched 可得到路由匹配数组,按顺序解析可得路由层次关系

    // Breadcrumb.vue
    watch: {
      $route: {
        handler(route) {
          // [{name:'home',path:'/'},{name:'list',path:'/list'}] 
          console.log(this.$route.matched);
          // ['home','list'] 
          this.crumbData = this.$route.matched.map(m => m.name || m.redirect);
        },
        immediate: true // 这⼀一⾏行行要加上,让它⼀一开始执⾏行行⼀一次 
      }
    }

    vue-router 源码实现

    通常用法

    // krouter.js
    import Home from "./views/Home";
    import About from "./views/About";
    import VueRouter from "./kvue-router";
    
    Vue.use(VueRouter);
    
    export default new VueRouter({
      routes: [
        { path: "/", component: Home },
        { path: "/about", component: About }
      ]
    });
    // main.js
    import router from './krouter'

    分析一下需要完成的任务:

    1、要能解析 routes 配置,变成一个key为path,value为component的map

    2、要能监控url变化事件,把最新的hash值保存到current路由

    3、要定义两个全局组件:router-view用于显示匹配组件的内容,router-link用于修改hash

    4、current应该是响应式的,这样可以触发 router-view 的重写渲染

    具体实现:

    创建 krouter.js -- 测试代码,相当于 平常使用的 router.js

    // krouter.js 
    import Vue from 'vue'
    import Home from "./views/Home";
    import About from "./views/About";
    import VueRouter from "./kvue-router";
    
    Vue.use(VueRouter);
    export default new VueRouter({
      routes: [
        { path: "/", component: Home },
        { path: "/about", component: About }
      ]
    });
    // main.js import router from './krouter'

    kvue-router.js,相当于  import VueRouter from 'vue-router'  中的 vue-router插件

    let Vue
    class VueRouter {
      constructor(options) {
        this.$options = options
    
        // 创建一个路由path和route映射
        this.routeMap = {}
    
        // 将来当前路径current需要响应式
        // 利用Vue响应式原理可以做到这一点
        this.app = new Vue({
          data: {
            current: '/'
          }
        })
      }
    
      init() {
        // 绑定浏览器事件
        this.bindEvents()
    
        // 解析路由配置
        this.createRouteMap(this.$options)
    
        // 创建router-link和router-view
        this.initComponent()
      }
    
      bindEvents() {
        // 修正this执行,因为我们想在这里拿 router的实例
        window.addEventListener('hashchange', this.onHashChange.bind(this))
        window.addEventListener('load', this.onHashChange.bind(this))
      }
      onHashChange(){
        // localhost/#/hash     slice(1)可以拿出#后面的部分
        this.app.current = window.location.hash.slice(1) || '/'
      }
      createRouteMap(options){
        options.routes.forEach(item => {
          // ['/home']: {path: '/home', component: Home}
          this.routeMap[item.path] = item
        })
      }
    
      initComponent(){
        // 声明两个全局组件
        Vue.component('router-link', {
          props: {
            to: String
          },
          render(h) {
            // 目标是:<a :href="to">xxx</a>
            return h('a', {attrs: {href: '#' + this.to}}, this.$slots.default)
            // jsx的写法
            // return <a href={this.to}>{this.$slots.default}</a>
          }
        })
    
        Vue.component('router-view', {
          // 箭头函数能保留this指向,这里指向VueRouter
          render: h => {
            const Comp = this.routeMap[this.app.current].component
            return h(Comp)
          }
        })
      }
    }
    
    // 把VueRouter变为插件,Vue插件只需要实现一个install方法就行了
    VueRouter.install = function(_Vue) {
      Vue = _Vue // 这里保存,上面使用
    
      // 混入任务
      Vue.mixin({ // 将来这个Vue被new的时候,会执行这里的代码
        beforeCreate() {
          // 这里的代码将来会在外面初始化的时候被调用
          // 这样外面就实现了Vue扩展
          // 这里this就是Vue组件实例
          // 但是这里只希望根组件执行一次
          if(this.$options.router){
            Vue.prototype.$router = this.$options.router
            // 对路由器进行初始化
            this.$options.router.init()
          }
        }
      })
    }
    
    export default VueRouter

    测试注意:

    不要出现 view-router 嵌套,因为这里的代码没做嵌套处理

  • 相关阅读:
    关于我成为电脑维修社团一员那些事
    [算法相关]二进制分组
    [题解]陌上花开
    [题解]UVA10917 Walk Through the Forest
    [字符串相关]后缀自动机(SAM)- 一
    [字符串相关]后缀数组
    [字符串相关]Aho-Corasick 自动机
    [算法入门]线性基
    [数据结构]可持久化并查集
    [数据结构]可持久化线段树
  • 原文地址:https://www.cnblogs.com/haishen/p/11770845.html
Copyright © 2011-2022 走看看