二、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.useVueRouter) // 引入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 => hApp)
}).$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.beforeEachto, from, next) => {
  // 要访问 /about 且未登录需要去登录
  ifto.meta.auth && !window.isLogin){
    ifwindow.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 {
  beforeRouteEnterto, from, next) { },
  beforeRouteUpdateto, from, next) { },
  beforeRouteLeaveto, from, next) { }
}

vue-router 拓展

动态路由

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

// router.js  --伪码
// 返回数据可能是这样的
// [{
//   path: '/',
//   name: 'home',
//   component: 'Home'
// }]

// 异步获取路由
api.getRoutes).thenroutes => {
  const routeConfig = routes.maproute => mapComponentroute))
  router.addRoutesrouteConfig)
})

// 映射关系
const compMap = {
  'Home': ) => import"./view/Home.vue")
}

// 递归替换
function mapComponentroute) {
  route.component = compMap[route.component]
  ifroute.children) {
    route.children = route.children.mapchild => mapComponentchild))
  }
  return route
}

面包屑

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

// Breadcrumb.vue
watch: {
  $route: {
    handlerroute) {
      // [{name:'home',path:'/'},{name:'list',path:'/list'}] 
      console.logthis.$route.matched);
      // ['home','list'] 
      this.crumbData = this.$route.matched.mapm => 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.useVueRouter);

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.useVueRouter);
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 {
  constructoroptions) {
    this.$options = options

    // 创建一个路由path和route映射
    this.routeMap = {}

    // 将来当前路径current需要响应式
    // 利用Vue响应式原理可以做到这一点
    this.app = new Vue{
      data: {
        current: '/'
      }
    })
  }

  init) {
    // 绑定浏览器事件
    this.bindEvents)

    // 解析路由配置
    this.createRouteMapthis.$options)

    // 创建router-link和router-view
    this.initComponent)
  }

  bindEvents) {
    // 修正this执行,因为我们想在这里拿 router的实例
    window.addEventListener'hashchange', this.onHashChange.bindthis))
    window.addEventListener'load', this.onHashChange.bindthis))
  }
  onHashChange){
    // localhost/#/hash     slice1)可以拿出#后面的部分
    this.app.current = window.location.hash.slice1) || '/'
  }
  createRouteMapoptions){
    options.routes.forEachitem => {
      // ['/home']: {path: '/home', component: Home}
      this.routeMap[item.path] = item
    })
  }

  initComponent){
    // 声明两个全局组件
    Vue.component'router-link', {
      props: {
        to: String
      },
      renderh) {
        // 目标是:<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 hComp)
      }
    })
  }
}

// 把VueRouter变为插件,Vue插件只需要实现一个install方法就行了
VueRouter.install = function_Vue) {
  Vue = _Vue // 这里保存,上面使用

  // 混入任务
  Vue.mixin{ // 将来这个Vue被new的时候,会执行这里的代码
    beforeCreate) {
      // 这里的代码将来会在外面初始化的时候被调用
      // 这样外面就实现了Vue扩展
      // 这里this就是Vue组件实例
      // 但是这里只希望根组件执行一次
      ifthis.$options.router){
        Vue.prototype.$router = this.$options.router
        // 对路由器进行初始化
        this.$options.router.init)
      }
    }
  })
}

export default VueRouter

测试注意:

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

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注