欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

router路由器设置怎么清除(cisco路由器设置步骤图)

程序员文章站 2023-11-25 09:30:04
vue router 基础让我们先来了解下vue router的简单使用吧,先了解怎么使用,之后再去想办法怎么去实现1.简介路由:本质上是一种对应关系分类分为前端路由和后端路由后端路由比如node.j...

vue router 基础

让我们先来了解下vue router的简单使用吧,先了解怎么使用,之后再去想办法怎么去实现

1.简介

路由:本质上是一种对应关系

分类分为前端路由后端路由

后端路由

比如node.js 的路由是 url的请求地址和服务器上面的资源对应,根据不同的请求地址返回不同的资源

前端路由

spa(单页应用)中根据用户所触发的事件改变了url 在无需刷新的前提下 显示不同的页面内容,比如等下就要讲的vue router

2.vue-router最基础的使用步骤

2.1.引入vue-router文件

<!-- 使用vue router前提 vue 必不可少 -->
<script src="./js/vue.js"></script>
<!-- 引入vue-router文件 -->
<script src="./js/vue-router_3.0.2.js"></script>

2.2.在页面上添加 router-link 和 router-view

<!-- 添加路由 -->
<!-- 会被渲染为 <a href="#/home"></a> -->
<router-link to="/home">home</router-link>
<router-link to="/login">login</router-link>
<!-- 展示路由的内容 -->
<router-view></router-view>

2.3.创建路由组件

//创建路由组件
const home = {
 template: `
<div>欢迎来到{{name}}</div>
`,
  data() {
   return {
    name: '首页',
   }
  },
}

const login = {
 template: `
  <div>欢迎来到登录页</div>
	`,
}

2.4.配置路由规则

// 配置路由规则
const router = new vuerouter({
 routes: [
  //每一个路由规则都是一个对象
  //path 路由的 hash地址
  //component 路由的所展示的组件
  {
   path: '/',
   // 当访问 '/'的时候 路由重定向 到新的地址 '/home'
   redirect: '/home',
   },     
   {
     path: '/home',
     component: home,
    },
    {
     path: '/login',
     component: login,
    },
   ],
})

2.5.挂载路由

let vm = new vue({
 el: '#app',
 data: {},
 methods: {},
  // 挂载到vue 上面
 router,
 })
router路由器设置怎么清除(cisco路由器设置步骤图)

3.嵌套路由

这里的嵌套路由是基于上面的例子继续写的

3.1.在路由里面添加 子路由链接和 占位符

//创建路由组件
const home = {
    template: `
    <div>
    欢迎来到首页
    <br>
    <!-- 子路由链接 -->
    <router-link to="/tab1">tab1</router-link>
    <router-link to="/tab2">tab2</router-link>

    <!-- 子路由展示 -->
    <router-view></router-view>
    </div>
}
复制代码

3.2.添加路由组件

// 创建两个子路由组件
const tab1 = {
    template: `
    <div>
    子路由1
    </div>
    `,
}
const tab2 = {
    template: `
    <div>
    子路由2
    </div>
    `,
}
复制代码

3.3.配置路由规则

// 配置路由规则
const router = new vuerouter({
    routes: [
        {
            path: '/home',
            component: home,
            //children 表示子路由规则
            children: [
                { path: '/tab1', component: tab1 },
                { path: '/tab2', component: tab2 },
            ],
        },
    ],
})
复制代码
router路由器设置怎么清除(cisco路由器设置步骤图)

4.动态路由

path属性加上/:id 使用route对象的params.id获取动态参数

比如现在有这么多个路由,如果自己也配置多个路由,岂不是有点。。。多余

<div id="app">
    <!-- 添加路由 -->
    <!-- 会被渲染为 <a href="#/home"></a> -->
    <router-link to="/goods/1">goods1</router-link>
    <router-link to="/goods/2">goods2</router-link>
    <router-link to="/goods/3">goods3</router-link>
    <router-link to="/goods/4">goods4</router-link>
    <!-- 展示路由的内容 -->
    <router-view></router-view>
</div>

然后这里就可以使用 动态路由来解决

<script>
    //创建路由组件
    const goods = {
        // this.$route.parms.id 可以省略 this
        template: `
        <div>欢迎来到商品 {{$route.params.id}}页</div>
        `,
        }
    // 配置路由规则
    const router = new vuerouter({
        routes: [
            {
                // 加上`/:id`
                path: '/goods/:id',
                component: goods,
            },
        ],
    })
    let vm = new vue({
        el: '#app',
        data: {},
        methods: {},
        // 挂载到vue 上面
        router,
    })
</script>
router路由器设置怎么清除(cisco路由器设置步骤图)

最后提一下还可以用query进行传参.

// 比如
<router-link to="/goods?id=1">goods</router-link>
复制代码

然后使用this.$route.query.id就可以在路由组件中获取到id

添加动态路由

使用 this.$router.addroutes([]) 可以添加动态路由,里面传递是一个数组 和 routes里面一样

5.路由传参

我们可以使用 props 进行传值

为啥要用 props 进行传值,route不香了吗,确实route 不够灵活

props 值有三种情况

5.1.布尔值类型

//创建路由组件
const goods = {
    // 使用props接收
    props: ['id'],
    template: `
    <div>欢迎来到商品 {{id}}页</div>
    `,
}
// 配置路由规则
const router = new vuerouter({
    routes: [
        {
            path: '/goods/:id',
            component: goods,
            //props为true, route.params将会被设置为组件属性
            props: true,
        },
    ],
})
复制代码

5.2.对象类型

但是这里就获取不到 id 了,会报错

这里的id 需要 $route.params.id 获取

const goods = {
    // 使用props接收
    props: ['name', 'info', 'id'],
    // 这里的 id 是获取不到的
    template: `
    <div>{{info}}来到{{name}} {{id}}页</div>
    `,
}
// 配置路由规则
const router = new vuerouter({
    routes: [
        {
            path: '/goods/:id',
            component: goods,
            //props为对象 就会把这个对象传递的路由组件
            //路由组件使用props接收
            props: {
                name: '商品',
                info: '欢迎',
            },
        },
    ],
})
复制代码

5.3.函数

const goods = {
    // 使用props接收
    props: ['name', 'info', 'id'],
    template: `
    <div>{{info}}来到{{name}} {{id}}页</div>
    `,
}
// 配置路由规则
const router = new vuerouter({
    routes: [
        {
            path: '/goods/:id',
            component: goods,
            //prop是一个函数的话 就可以组合传值
            props: (route) => {
                return {
                    name: '商品',
                    info: '欢迎',
                    id: route.params.id,
                }
            },
        },
    ],
})
复制代码

6.route 和 router

在上面提到了route 那么和 router有什么区别呢

  • route当前router跳转对象里面可以获取pathparamshashqueryfullpathmatchedname
  • routervuerouter实例new vuerouter创建的实例,想要导航到不同url,则使用router.push方法
  • routesrouter路由实例用来配置路由对象(顺带提一下)

7.命名路由

路由组件

//创建路由组件
const goods = {
    // 使用props接收
    props: ['id'],
    template: `
    <div>商品{{id}}页</div>
    `,
}
复制代码

路由配置

//配置路由
const router = new vuerouter({
    routes: [
        {
            path: '/goods/:id',
            // 命名路由
            name: 'goods',
            component: goods,
        },
    ],
})
复制代码

绑定 :to 通过name找到定义的路由 还可以使用 params 传递参数

<router-link :to="{name: 'goods', params: { id: 1 } }">goods1</router-link>
<!-- 展示路由的内容 -->
<router-view></router-view>
复制代码

8.编程式导航

8.1.声明式导航

既然提到了编程式导航,那么先简单说下声明式导航

上面所展示的都是声明是导航 比如router-link

<router-link to=”/goods/1″>goods1</router-link>

还有a标签

<a href=”#/goods/1″>goods1</a>

8.2.编程式导航

使用javascript来控制路由跳转

在普通的网页中使用 loaction.href window.open 等等进行跳转

现在我要介绍的是vue router中的编程式导航

我们平时都是用router.push() **router.go(n)**方法进行跳转

//字符串
this.$router.push('/home')

//对象
this.$ruter.push({path:'/home'})

//比如这个 /goods?id=1
this.$router.push({path:'/goods',query:{id:'1'}})

//命名路由 /goods/1
this.$router.push({name:'goods',params:{id:1}})

//后退
this.$router.go(-1)
复制代码

9.路由守卫

9.1.全局守卫

router.beforeeach 全局守卫 对所有的路由都起作用

router.beforeeach((to, from, next) => { 
      next();//使用时,千万不能漏写next!!!
    }).catch(()=>{
      //跳转失败页面
      next({ path: '/error', replace: true, query: { back: false }}
    )
})
复制代码

全局的守卫的三个参数

to: 即将要进入的目标 路由对象

from: 当前导航正要离开 路由对象

next: 参数不同做的事也不同

next() 直接进入下一个钩子

next(false) 停止当前导航

next(‘/路径’) 跳转到path路由地址 当然这里面也可以写成对象形式 next({path : ‘/路径’}) next(error): 如果传入参数是一个 error 实例,则导航会被终止且该错误会被传递给 router.onerror()

9.2.路由独享的守卫

beforeenter 路由对象独享的守卫写在routes里面

const router = new vuerouter({
  routes: [
    {
      path: '/goods',
      component: goods,
      beforeenter: (to, from, next) => {
        // 一样的用法
      }
    }
  ]
})
复制代码

9.3.组件内的守卫(了解)

组件内的守卫 写在组件内部 下面是官方介绍

  • beforerouteenter 进入路由前,组件还没有被实例化所以这里无法获取到this
  • beforerouteupdate (2.2) 这个阶段可以获取this,在路由复用同一个组件时触发
  • beforerouteleave 这个阶段可以获取this,当离开组件对应的路由时,此时可以用来保存数据,或数据初始化,或关闭定时器等等
const goods = {
  template: `<div>goods</div>`,
  beforerouteenter (to, from, next) {
    // 具体逻辑
  },
  beforerouteupdate (to, from, next) {
    // 具体逻辑
  },
  beforerouteleave (to, from, next) {
    // 具体逻辑
  }
}
复制代码

10.组件缓存keep-alive

页面重新加载会重新渲染页面比如回退的时候等等,我们有的组件它不是一个活动的(数据不变)不希望它被重新渲染,所以这里就可以使用 <keep-alive> </keep-alive> 包裹起来,这样就不会触发created钩子

应用场景:获取一个商品的详情然后回退在前进的时候就使用缓存,提高性能

10.1.不使用 keep-alive例子

这里home 组件在created进行打印当前的时间

<div id="app">
    <router-link to="/home">home</router-link>
	<router-link to="/login">login</router-link>

	<router-view></router-view>
</div>
复制代码
<script>
      const login = {
        template: `
        <div>login</div>
        `,
      }
      const home = {
        template: `
        <div>home</div>
        `,
        created() {
          console.log(new date())
        },
      }
      const router = new vuerouter({
        routes: [
          {
            path: '/',
            redirect: '/home',
          },
          {
            path: '/home',
            component: home,
          },
          {
            path: '/login',
            component: login,
          },
        ],
      })
      let vm = new vue({
        el: '#app',
        data: {},
        methods: {},
        router,
      })
  </script>
复制代码
router路由器设置怎么清除(cisco路由器设置步骤图)

如上,每切换home 的路由 组件就会重新渲染,打印当前的时间

如果使用 keep-alive 会有什么效果呢

10.2.使用keep-alive

这里只需简单的包裹起来就行了

<div id="app">
    <router-link to="/home">home</router-link>
    <router-link to="/login">login</router-link>

    <keep-alive>
        <router-view></router-view>
    </keep-alive>
</div>
复制代码
router路由器设置怎么清除(cisco路由器设置步骤图)

可以看到的是只打印一次,说明切换了路由它并没有重新渲染组件

当然可以在 组件内取个name名字 keep-alive 标签里面添加 include 属性就可以对相应的组件进行缓存

const login = {
    name: login,
    template: `
    <div>login</div>
    `,
}
const home = {
    name: home,
    template: `
    <div>home</div>
    `,
    created() {
    	console.log(new date())
    },
}
复制代码
<div id="app">
    <router-link to="/home">home</router-link>
    <router-link to="/login">login</router-link>

    <keep-alive include="login,home">
        <router-view></router-view>
    </keep-alive>
</div>
复制代码

10.3.activated 和 deactivated

keep-alive 生命周期执行顺序

第一次访问路由时:

  • created–>mounted –>activated
  • deactivated在退出后触发

以后进入只会触发 activated

11.hash 和 history 模式

11.1.hash模式

在vue-router中默认使用的是 hash 模式

hash是url中的锚点就是**#,通过锚点作为路由地址,我们通常改变的是改变#**后面部分,实现浏览器渲染指定的组件.,锚点发生改变会触发 onhashchange 事件

11.2.history模式

history 模式就是平时正常的地址,使用方面需要服务器支持

如果访问的路径资源没有 直接就是 404

html5后新增了两个api

pushstate(): ie10后支持

replacestate()

在vue-router中如果要使用 history 模式需要指定

const router = new vuerouter({
  mode: 'history'
})
复制代码

实现一个基础 vue router

复习上面的路由的基础那么我们不如来写个vue router吧

实现的这个 vue router是基于 history模式

所有的步骤都放到代码的注释中,每一行都写个注释

这个简单的没有按照vue router源码来写主要是一些基础功能的实现

为后面的按照源码写打基础

1.注册全局vue router

首先就是先注册自己的 vue router

判断是否注册了组件

在vue实例创建完成进行注册

// 保存一个全局变量 vue
let _vue = null

// 默认导出自己写的 vuerouter
export default class myvuerouter {
  // 实现install 注册 myvuerouter vue提供install可供我们开发新的插件及全局注册组件等
  // 把vue传进去
  static install(vue) {
    // 定义一个标识判断是否注册了 myvuerouter ,注册了就不用下一步了
    if (myvuerouter.install.installed) return
    // 没有就进行下面的,把标识改变true
    myvuerouter.install.installed = true
    // 把全局变量 _vue 保存
    _vue = vue
    // 为了获取vue中的this执行这里使用 混入
    _vue.mixin({
      // 在vue实例创建好的时候进行操做
      beforecreate() {
        // 判断是否是实例创建还是组件创建 ,可以判断是否挂载 了router
        if (this.$options.router) {
          // 把router注册到 _vue上
          _vue.prototype.$router = this.$options.router
        }
      },
    })
  }
}
复制代码

2.实现 构造方法

optoins 保存传入的规则

routermap 确定地址和组件的关系

current 表示当前的地址是响应式的之后渲染组件和它相关

export default class myvuerouter {
  ...
  //实现构造
  constructor(optoins) {
    // 这个保存的是  routes
    this.optoins = optoins
    // routermap 保存路由和 组件之间的关系
    this.routermap = {}
    // 用来记录数据 这里面的数据都是 响应式
    this.data = _vue.observable({
      // 当前路由的地址
      current: '/',
    })
  }
}
复制代码

3.解析路由规则

传入的路由规则拿到一个对象里 地址 和 组件一一匹配

export default class myvuerouter {
  ...
  // 解析路由规则
  createroutermap() {
    // 把之前构造函数的中的传入的 routes 规则进行遍历
    this.optoins.routes.foreach((item) => {
      // 把路由 和 组件的对应关系添加到 routermap中
      this.routermap[item.path] = item.component
    })
  }
}
复制代码

4.实现 router-link 组件

router-link就是页面上所展示的路由链接

因为一般使用的基本都是运行版的vue 所以自己把组件转为 虚拟dom

还有就是链接会刷新的问题

自己写个函数进行跳转阻止默认事件

还得注意对应的路由所要渲染的组件

export default class myvuerouter {
  ...
  // 实现组件
  initcomponents(vue) {
    // 实现 router-link组件
    vue.component('router-link', {
      props: {
        // router-link上面的to属性将访问的地址
        to: string,
      },
      // 由于运行版的vue不能渲染template所以这里重新写个render 这里h 也是个函数
      // template: `<a :href="to"><slot></slot></a>`,
      render(h) {
        // 第一个参数是标签
        return h(
          'a',
          // 第二个参数是对象是 tag 里面的属性
          {
            // 设置属性
            attrs: {
              href: this.to,
            },
            // 绑定事件
            on: {
              // 重新复写点击事件,不写的话会点击会向服务器发送请求刷新页面
              click: this.myclick,
            },
          },
          // 这个是标签里面的内容 这里渲染是 默认插槽
          [this.$slots.default]
        )
      },
      methods: {
        //router-link的点击事件
        myclick(e) {
          // 因为我这里是模拟是 history的路由所以用pushstate ,hash路由可以这里用 push
          // 使用history修改浏览器上面的地址
          // pushstate 第一个参数是传递的参数,第二个是标题,第三个是链接
          history.pushstate({}, '', this.to)
          // 渲染相应的组件
          // 渲染的页面也需要改变 data中的current是响应式的 router-view是根据current来渲染的
          this.$router.data.current = this.to
          // 阻止默认跳转事件
          e.preventdefault()
        },
      },
    })
复制代码

5.实现 router-view 组件

这里从之前解析的规则里面拿到当前的对应的组件进行转为虚拟dom

最后router-view占位渲染到页面上

export default class myvuerouter {
  ...
  // 实现组件
  initcomponents(vue) {
    // 实现 router-view组件
    vue.component('router-view', {
      render(h) {
        // 获取的当前路径所对应的组件
        // 因为当前this是vue,this.$router才是myvuerouter
        const component = this.$router.routermap[this.$router.data.current]
        // 转化为虚拟dom
        return h(component)
      },
    })
  }
}
复制代码

6.前进和后退

在完成之前的编写还是不够的,因为在浏览器点后退和前进虽然改变了浏览器的地址,但是组件却没有刷新,下面就来解决这个问题

export default class myvuerouter {
  ...
  // 初始化事件
  initevent() {
    // 监听浏览器地址的改变
    window.addeventlistener('popstate', () => {
      // 改变vuerouter的当前的地址 重新渲染组件
      this.data.current = window.location.pathname
    })
  }
}
复制代码

7.在router挂载后进行初始化

最后写个函数进行初始化

在router注册到vue之后进行 初始化

export default class myvuerouter {
  // 初始化
  init() {
    // 解析路由规则
    this.createroutermap()
    // 初始化组件
    this.initcomponents(_vue)
    // 初始化事件
    this.initevent()
  }
    
  static install(vue) {
    if (myvuerouter.install.installed) return
    myvuerouter.install.installed = true
    _vue = vue
    _vue.mixin({
      beforecreate() {
        if (this.$options.router) {
          _vue.prototype.$router = this.$options.router
          // 注册完router后进行初始化
          this.$options.router.init()
        }
      },
    })  
  }
  ...
}

复制代码

8.放上完整的 index.js

// 保存一个全局变量 vue
let _vue = null

export default class myvuerouter {
  // 实现install 注册 myvuerouter vue提供install可供我们开发新的插件及全局注册组件等
  // 把vue传进去
  static install(vue) {
    // 定义一个标识判断是否注册了 myvuerouter ,注册了就不用下一步了
    if (myvuerouter.install.installed) return
    // 没有就进行下面的,把标识改变true
    myvuerouter.install.installed = true
    // 把全局变量 _vue 保存
    _vue = vue
    // 为了获取vue中的this执行这里使用 混入
    _vue.mixin({
      // 在vue实例创建好的时候进行操做
      beforecreate() {
        // 判断是否是实例创建还是组件创建 ,可以判断是否挂载 了router
        if (this.$options.router) {
          // 把router注册到 _vue上
          _vue.prototype.$router = this.$options.router
          // 注册完router后进行初始化
          this.$options.router.init()
        }
      },
    })
    // 判断是否挂载
  }
  // 实现构造方法
  constructor(optoins) {
    // 这个保存的是  routes
    this.optoins = optoins
    // routermap 保存路由和 组件之间的关系
    this.routermap = {}
    // 用来记录数据 这里面的数据都是 响应式
    this.data = _vue.observable({
      // 当前路由的地址
      current: '/',
    })
  }
  // 解析路由规则
  createroutermap() {
    // 把之前构造函数的中的传入的 routes 规则进行遍历
    this.optoins.routes.foreach((item) => {
      // routes中的每一项都是一个对象 { path: '/xxx', component: xxx}
      // 把路由 和 组件的对应关系添加到 routermap中
      this.routermap[item.path] = item.component
    })
  }
  // 实现组件
  initcomponents(vue) {
    // 实现 router-link组件
    vue.component('router-link', {
      props: {
        // router-link上面的to属性将访问的地址
        to: string,
      },
      // 由于运行版的vue不能渲染template所以这里重新写个render 这里h 也是个函数
      // template: `<a :href="to"><slot></slot></a>`,
      render(h) {
        // 第一个参数是标签
        return h(
          'a',
          // 第二个参数是对象是 tag 里面的属性
          {
            // 设置属性
            attrs: {
              href: this.to,
            },
            // 绑定事件
            on: {
              // 重新复写点击事件,不写的话会点击会向服务器发送请求刷新页面
              click: this.myclick,
            },
          },
          // 这个是标签里面的内容 这里渲染是 默认插槽
          // 比如<router-link to="/">首页</router-link>
          // 插槽就是给首页两个字留位置,当前这只是个例子
          [this.$slots.default]
        )
      },
      methods: {
        //router-link的点击事件
        myclick(e) {
          // 因为我这里是模拟是 history的路由所以用pushstate ,hash路由可以这里用 push
          // 使用history修改浏览器上面的地址
          // pushstate 第一个参数是传递的参数,第二个是标题,第三个是链接
          history.pushstate({}, '', this.to)
          // 渲染相应的组件
          // 渲染的页面也需要改变 data中的current是响应式的 router-view是根据current来渲染的
          this.$router.data.current = this.to
          // 阻止默认跳转事件
          e.preventdefault()
        },
      },
    })
    // 实现 router-view组件
    vue.component('router-view', {
      render(h) {
        // 获取的当前路径所对应的组件
        // 因为当前this是vue,this.$router才是myvuerouter
        const component = this.$router.routermap[this.$router.data.current]
        // 转化为虚拟dom
        return h(component)
      },
    })
  }
  // 初始化事件
  initevent() {
    // 监听浏览器地址的改变
    window.addeventlistener('popstate', () => {
      // 改变vuerouter的当前的地址 重新渲染组件
      this.data.current = window.location.pathname
    })
  }

  // 初始化
  init() {
    // 解析路由规则
    this.createroutermap()
    // 初始化组件
    this.initcomponents(_vue)
    // 初始化事件
    this.initevent()
  }
}
复制代码

到了这里基础的实现功能差不多了,上面的例子是为了下面打基础,所有的功能实现基本都是在一个文件下很不严谨,下面就严格按照vue router 源码来实现自己 vue router

vue router实现

经过上面简单的实现,现在我们按照vue router源码的方式进行编写

1.首先是vue router 构造

/* index.js */

// 导出自己写的 vuerouter
export default class vuerouter {
  // 实现构造函数功能
  constructor(options) {
    // 获取options中的routes路由规则 没有就为空数组
    this._options = options.routes || []
  }
  // 初始化
  init(vue) {}
}
复制代码

2.注册组件 install

install.js 对自己写的vue-router进行全局的注册

之后还会在这里创建 router∗∗∗∗router** **router∗∗∗∗route

还有注册 router-link router-view

/* install.js */

// 定义一个全局 的vue
export let _vue = null

// 导出 install方法
export default function install(vue) {
  // 保存到全局的vue
  _vue = vue
  // 混入
  _vue.mixin({
    // vue实例创建完毕之后操做
    beforecreate() {
      // 这里是new vue
      if (this.$options.router) {
        // 保存 vue
        this._routerroot = this
        // 保存 vue router 的实例,以后可以通过vue router构造的一些方法
        this._router = this.$options.router
        // 调用vue router的init(vue) 初始化操做
        this._router.init(this)
      } else {
        // 这里是创建 vue的组件等等
        // 判断是否有父组件 ,有的话就把父组件的 _roterroot(也就是vue)给 子组件
        // 没有父组件就把 this 这是也是(vue) 给子组件
        this._routerroot = (this.$parent && this.$parent._routerroot) || this
      }
    },
  })
}
复制代码

然后在 index.js中导入install 进行为构造添加 install

// 导入 install
import install from './install'

// 导出自己写的 vuerouter
export default class vuerouter {
	...
}
    
// 为vuerouter 添加 install方法
vuerouter.install = install
复制代码

3.编写 create-route-map.js

这个主要的作用就是用来解析传递过来的路由 需要导出然后在 create-matcher.js进行使用

具体的细节都写了注释

/* create-route-map.js */

// 导出具体的路由解析
/**
 *
 * @param {*} routes 路由规则
 * @param {*} oldpathlist 路由列表
 * @param {*} oldpathmap 路由和组件的对应关系
 */
export default function createroutemap(routes, oldpathlist, oldpathmap) {
  // 传入了就是添加动态路由 没有传入就默认为空
  const pathlist = oldpathlist || []
  const pathmap = oldpathmap || []

  // 遍历规则操作
  routes.foreach((route) => {
    // 记录路由 也是核心的解析路由 为了分工明确写的外面
    addrouterecord(route, pathlist, pathmap)
  })

  // 返回新的路由列表 和 路由对应关系
  return {
    pathlist,
    pathmap,
  }
}

/**
 *
 * @param {*} route 路由规则
 * @param {*} pathlist 路由列表
 * @param {*} pathmap 路由和组件之间的对应关系
 * @param {*} parentrecord  父路由
 */
function addrouterecord(route, pathlist, pathmap, parentrecord) {
  // 路由地址 判断是否存在父级的路由 有的话拼接父级路由和当前路由的path 没有就是当前route.path
  const path = parentrecord ? `${parentrecord.path}/${route.path}` : route.path
  // record作为一个路由记录 记录了路由地址,组件,父级路由   用于路由对应关系去对应相对应的path
  const record = {
    path,
    component: route.component,
    parent: parentrecord,
  }
  // 判断是否在路由列表中 存在当前路由,不存在进行添加当前路由,更新路由列表
  if (!pathlist[path]) {
    // 向路由列表中添加路由
    pathlist.push(path)
    // 向路由对应关系中 添加path 相对应的记录
    pathmap[path] = record
  }
  // 判断当前的 路由是否有子路由,有的话进行递归
  if (route.children) {
    route.children.foreach((childroute) => {
      // 就简单说下最后一个参数 就是父级路由记录
      addrouterecord(childroute, pathlist, pathmap, record)
    })
  }
}
复制代码

4.编写 create-matcher.js

这个模块的意义也是解析路由不过这个是个指挥家,上面实现的是具体解析操作

在这个模块里进行调用上面的具体解析路由的方法就行了

有了上面面具体的路由解析,这个create-matcher.js就容易实现了,只需要简单的调用它即可

这个模块返回了两个方法

match : 根据路由路径创建路由规则对象,之后就可以通过 规则对象获取到所有的路由信息然后拿到所有的组件进行创建

addroutes : 添加动态路由

/* create-matcher.js */

// 导入具体的路由解析规则
import createroutemap from './create-route-map'

// 导出解析路由规则 传入的是规则
export default function creatematcher(router) {
  // pathlist 路由的列表  pathmap 路由与组件的对应关系 namemap这里没有考虑,先完成个简单的
  // 具体的解析规则是使用  createroutemap
  const { pathlist, pathmap } = createroutemap(router)
  // match是 从pathmap 根据path获取 相应的路由记录
  function match(path) {
      //待实现
  }
  // 添加动态路由
  function addroutes(router) {
    // 添加动态路由肯定也要解析路由规则
    createroutemap(router, pathlist, pathmap)
  }
  // 返回match 和 addroutes
  return {
    match,
    addroutes,
  }
}
复制代码

然后在index.js也就是vue router的构造中使用 creatematcher. 使用this.matcher接收

// 导入 install
import install from './install'
// 导入解析路由
import creatematcher from './create-matcher'

// 导出自己写的 vuerouter
export default class vuerouter {
  // 实现构造函数功能
  constructor(options) {
    // 获取options中的routes路由规则 没有就为空数组
    this._routes = options.routes || []
    // 解析路由 传入规则 这里还返回了两个方法 match,addroutes 用matcher接收一下之后有用
    this.matcher = creatematcher(this._routes)
  }
  // 初始化
  init(vue) {}
}
// 为vuerouter 添加 install方法
vuerouter.install = install

复制代码

5.编写 creatematcher

看见上面在 creatematcher中定义了 一个match了吗,

match是 从pathmap 根据path获取 相应的路由记录

上面还没有去实现,现在来实现它

需要实现它的话还需要编写个 createroute 方法,我这里写在 uitl/route.js模块里

/* util/route.js */

// 导出 createroute
/**
 *
 * @param {*} record 传过来的记录
 * @param {*} path 路由地址
 * @returns
 */
export default function createroute(record, path) {
  // 保存路由的记录 里面可能有多个路由 是这种模式保存 [parentrecord,childrecord]
  const matched = []
  // 判断是否是子路由
  // 下面 record = record.parent 在不断向上找parent有继续执行
  // 没有就直接return 下面的对象
  while (record) {
    // 循环得到的 record不断插入到 数组的最前面
    matched.unshift(record)
    // 把父记录给当前record 继续循环
    record = record.parent
  }
  // 返回path 和 matched 以便之后 router-view渲染
  return {
    path,
    matched,
  }
}
复制代码

上面编写了 createroute方法我们就可以在 create-mathcer.js 调用 来获取到记录了

然后再 create-mathcer.js中继续 完善 match方法

/* create-matcher.js */

// 导入具体的路由解析规则
import createroutemap from './create-route-map'
// 导入 createroute
import createroute from './util/route'

// 导出解析路由规则 传入的是规则
export default function creatematcher(router) {
  // pathlist 路由的列表  pathmap 路由与组件的对应关系 namemap这里没有考虑,先完成个简单的
  // 具体的解析规则是使用  createroutemap
  const { pathlist, pathmap } = createroutemap(router)
  // match是 从pathmap 根据path获取 相应的路由记录
  function match(path) {
    // 取出path对应的记录
    const record = pathmap[path]
    // 判断记录是否存在
    if (record) {
      return createroute(record, path)
    }
    return createroute(null, path)
  }
  // 添加动态路由
  function addroutes(router) {
    // 添加动态路由肯定也要解析路由规则
    createroutemap(router, pathlist, pathmap)
  }
  // 返回match 和 addroutes
  return {
    match,
    addroutes,
  }
}
复制代码

6.历史记录的处理 history

history目录下新建一个 base模块用来编写 父类

这个父类有 hash 模式 和 history(html5) 模式共同的方法

这里就主要演示下 hash 模式的代码

/* history/base.js */

// 导入 我们上面写好的 createroute
import createroute from '../util/route'

// 导出 history
export default class history {
  // router 是路由对象 也就是 vue-router的一个实例
  constructor(router) {
    // 赋值给自己的 router
    this.router = router
    // 默认的的当前路径为 /
    this.current = createroute(null, '/')
  }
  // 将要跳转的链接
  // path 是路由的地址, oncomplete是一个回调
  transitionto(path, oncomplete) {
    // 获取当前的应该跳转的路由  调用的是 vue-router中 this.matcher中收到的match方法
    // 在这里 this.router就是 vue-router的一个实例 所以写成
    // this.router.matcher.match(path)
    this.current = this.router.matcher.match(path)
    // 回调存在触发回调
    oncomplete && oncomplete()
  }
}
复制代码

编写 hashhistory 模式 继承 history

/* /history/hash */

// 导入 base中的 history
import history from './base'

// 继承了 history
export default class hashhistory extends history {
  constructor(router) {
    super(router)
    // 确保第一次访问的时候路由加上 #/
    ensuerslash()
  }
  // 监听url的改变 设置当前的current
  setuplistener() {
    // 监听 hash的变化
    window.addeventlistener('hashchange', () => {
      // 改变 this.current
      this.transitionto(this.getcurrentlocation())
    })
  }
  // 获取当前的url的hash 当然这里要去除 #
  getcurrentlocation() {
    // 这里不建议写成这个 return window.location.hash.slice(1) 有兼容问题
    let href = window.location.href
    const index = href.indexof('#')
    // 当没有 #的时候 直接返回 空字符串
    if (index < 0) return ''
    // 获取 #后面的地址
    href = href.slice(index + 1)
    return href
  }
}

// 确保第一次加上 #/
function ensuerslash() {
  // 如果存在 hash的话就不行加 /
  if (window.location.hash) {
    return
  }
  // 如果没有hash值 只要给 hash 加上一个 / 它会自动加上 /#/
  window.location.hash = '/'
}
复制代码

关于 html5模式 这里 就没写了

然后回到 index.js 就是自己写的 vue router中继续编写模式判断

最后就是 初始化 init方法

/* index.js */

// 导入 install
import install from './install'
// 导入解析路由
import creatematcher from './create-matcher'
// 导入 hashhistory
import hashhistory from './history/hash'
// 导入 html5history
import html5history from './history/html5'

// 导出自己写的 vuerouter
export default class vuerouter {
  // 实现构造函数功能
  constructor(options) {
    // 获取options中的routes路由规则 没有就为空数组
    this._routes = options.routes || []
    // 解析路由 传入规则 这里还返回了两个方法 match,addroutes 用matcher接收一下之后有用
    this.matcher = creatematcher(this._routes)
    // 获取模式 没有就默认为 hash 模式
    this.mode = options.mode || 'hash'
    // 使用 if 或者 分支都行 根据不同的模式执行不同的路由跳转功能等等
    switch (this.mode) {
      case 'history':
        this.history = new html5history(this)
        break
      case 'hash':
        // 模式的实例使用 this.history接收等下用的上
        // 传入的this是 vuerouter
        this.history = new hashhistory(this)
        break
      default:
        throw new error('该模式不存在')
    }
  }
  // 初始化
  init(vue) {
    // 拿到模式的实例
    const history = this.history
    // 进行跳转  第一个参数是path ,第二个是回调函数
    history.transitionto(history.getcurrentlocation, () =>
      // 监听url的改变 设置当前的 this.current
      history.setuplistener()
    )
  }
}
// 为vuerouter 添加 install方法
vuerouter.install = install
复制代码

7.定义一个响应值 _route

渲染不同路由页面有个前提的就是需要一个表示 当前路由 响应式的属性

所以我们来到 install.js 添加一个响应式的 属性**_route**

和这个无关的代码 …省略

/* install.js */

export let _vue = null

export default function install(vue) {
  _vue = vue
  vue.mixin({
    beforecreate() {
      if (this.$options.router) {
        ...
        // 创建一个代表当前路由 响应式的值_route
        // 其实不建议使用 definereactive直接创建。。
        // 第一个参数是绑定在谁身上,第二是值名称,第二个是值
        vue.util.definereactive(this, '_route', this._router.history.current)
      } else {
        ...
      }
    },
  })
}
复制代码

然后得回到 history下面的 base 添加一个修改响应式 _route的值的回调 this.cb

/* history/base.js */

import createroute from '../util/route'

export default class history {
  constructor(router) {
    ...
    // cb 一个回调函数,它的作用就是修改 响应式路由的值_route ,对应的视图然后就刷新
    this.cb = null
  }
  // 通过 listen来修改 cb的值
  listen(cb) {
    this.cb = cb
  }

  transitionto(path, oncomplete) {
	...
    // cb 存在就修改响应式路由的值
    this.cb && this.cb(this.current)
	...
  }
}
复制代码

最后在 index.jsinit 调用 listen 方法 传入回调修改 响应式值**_route**

/* index.js */

...

export default class vuerouter {
  ...
  init(vue) {
    ...
    // 修改 响应式的 route
    history.listen((route) => {
      vue._route = route
    })
  }
}
...

复制代码

8.添加 $router 和 $route

我们知道在 vue router 提供了 $router (这个是路由对象是**vue router**的实例) 还有 $route(路由规则对象)

我们自己可以来到 install.js 中进行 添加这两个属性

/* install.js */

...
export default function install(vue) {
  ...
  // 添加 $router 路由对象  object.defineproperty 参数分别是 为谁添加,属性名,属性值
  object.defineproperty(vue.prototype, '$router', {
    get() {
      // this._routerroot代表的是 vue ,他的_router是 vue router实例
      // 可以回过去看看第二点
      return this._routerroot._router
    },
  })
  // 添加 $route
  object.defineproperty(vue.prototype, '$route', {
    get() {
      // 他的_route是就是刚才添加 响应式 的当前 路由
      return this._routerroot._route
    },
  })
}
复制代码

9.router-link

基本的介绍就不多说了,之前也是有介绍的。然后现在重新来实现下

components 文件下新建 link.js

/* ./components/link.js */

// 导出 link
export default {
  props: {
    to: {
      type: string,
      required: true,
    },
  },
  // 渲染
  render(h) {
    // 转化为虚拟dom
    return h(
      // 标签名
      'a',
      // 标签属性
      {
        domprops: {
          href: '#' + this.to,
        },
      },
      // 标签里面的内容 这里是 默认插槽
      [this.$slots.default]
    )
  },
}
复制代码

10.router-view

components 文件下新建 view.js 具体步骤干了什么都写在注释里了

/* ./components/link.js */

// 导出 view
export default {
  render(h) {
    // 获取路由规则对象
    const route = this.$route
    // 定义一个变量,用来等下 取 matched 中的值
    let depth = 0
    // 该组件为 router-view
    this.routerview = true
    // 尝试去获取父组件
    let parent = this.$parent
    // 判断是否有父组件
    while (parent) {
      // 判断该组件是否为 routerview
      if (parent.routerview) {
        depth++
      }
      // 继续向上判断还有无父组件
      parent = parent.$parent
    }
    // 这里的route是 this.$route 就是 _route 响应式值,也就是 current
    // 当初 current 是 调用了 match方法 获取到的 返回值是 matched 和 path
    // matched 里面是多个路由对象 是这种模式保存 [parentrecord,childrecord]
    // 通过 变量depth取出来 举个栗子 ['/login','/login/tab']
    // 因为使用的unshif添加后面的父组件添加到前面
    // depth 一直加 ,直接取出后面即可
    const record = route.matched[depth]
    // 没有记录直接渲染
    if (!record) {
      return h()
    }
    // 有的话就获取记录中的组件
    const component = record.component
    // 最后把组件渲染
    return h(component)
  },
}

复制代码

好了到了这里 vue router的第二次编写就完成了,虽然和官方的差距很大。。额,因为这里是简化写的

11.文件目录

忘了最后贴上文件的目录

router路由器设置怎么清除(cisco路由器设置步骤图)

这个模拟vue routerdemo 放在了 github,有需要的可以这里 myvuerouter

到了这里也只是仅仅实现了 vuerouter的一小部分功能

但是大体上的功能都差不多实现了,嵌套路由 添加动态路由也实现了

其实我觉得到这里了也可以了,不过还是得继续加油学习