0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

手写vue-router

修罗 2020-10-26 1702 0条评论 vue

首页 / 正文

手写vue-router

1、编写路由类 my-router.js

let Vue;
class MyRouter{
    
  constructor($options){
    // $options为 { routes = [ { path: '/', '路由': Index, name: '首页' }] }
    this.$options = $options

    // 定义响应式的属性current
    const initial = window.location.hash.slice(1) || '/'
    Vue.util.defineReactive(this, 'current', initial)

    // 监听hashchange事件
    window.addEventListener('hashchange', this.onHashChange.bind(this))

    // 路由表:缓存path和route映射关系
    this.routeMap = {}
    // 每个路由地址都对应自己的route
    this.$options.routes.forEach(route => {
      // '/': { path: '/', '路由': Index, name: '首页' }
      this.routeMap[route.path] = route
    });
  }
  onHashChange() {
    // 当前hash去掉#
    this.current = window.location.hash.slice(1)
  }
    
}

// 定义插件
MyRouter.install = function(vue){
  // 保存vue供上面响应式设置
  Vue = vue
  vue.mixin({
    // 每个实例都会调用beforeCreate
    beforeCreate(){
      // 判断,拿到main.js中new vue()传递的router对象
      if(this.$options.router){
        vue.prototype.$router = this.$options.router
      }
    }
  })

  // 全局组件注册
  Vue.component('router-link', {
    props: {
      to: {
        type: String,
        required: true
      }
    },
    // runtime-only不带编译器,预编译,打包的时候编译。这里不能用template
    // render函数只要用到响应式数据就会被依赖收集起来,等待重新调用
    render(h){
      return h(
        'a', 
        { attrs: { href: `#${this.to}` }, style: { display:'inline-block',padding:'0 10px' } }, 
        [this.$slots.default]
      )
    }
  })

  // current发生改变render函数重新渲染,实现了组件切换
  Vue.component('router-view', {
    render(h) {
      // 从router对象中拿到当前路由和路由表
      // 如果当前路由有children,即路由有router-view,
      // router-view会从current匹配当前路由,当前路由又是原来那个路由,无限循环
      // 比如:/about有router-view,about要渲染router-view,后面router-view又要渲染about
      const { routeMap, current } = this.$router
      // 拿到当前路由对应的组件
      const component = routeMap[current].component;
      return h(component);
    }      
  })
}

export default MyRouter

2、router/index.js引入路由,传递路由数组

import MyRouter from './my-router'
import vue from 'vue'
import About from '@/components/about'
import Index from '@/components/index'

vue.use(MyRouter)

const routes = [
  {
    path: '/',
    component : Index,
    name: '首页'
  },
  {
    path: '/about',
    component: About,
    name: '关于'
  }
]

const router = new MyRouter({ routes })

export default router

main.js挂载路由实例

1603710385574.png

使用自定义路由

1、编写组件app.vue

1603709933784.png

2、index.vue

<template>
  <div>index</div>
</template>

<script>
</script>
<style>
</style>

3、about.vue

<template>
  <div>
    <div>about</div> 
  </div>
</template>

<script>
</script>
<style>
</style>

效果

1603710498231.png

about路由加入children

  • 正如上面所提到的如果当前路由有children,即路由有router-view,router-view会从current匹配当前路由,当前路由又是原来那个路由,无限循环
  • 比如:/about有router-view,about要渲染router-view,后面router-view

info组件和上面about组件一样简单用div显示:info

1603710638798.png

更新my-router.js

let Vue;
class MyRouter{

  constructor($options){
    // $options为 { routes = [ { path: '/', '路由': Index, name: '首页' }] }
    this.$options = $options
    // 初始化当前页面hash
    this.current = window.location.hash.slice(1) || '/'
    // 定义响应式的属性matched,所有使用到matched的页面都会收集依赖,matched更新,页面更新
    Vue.util.defineReactive(this, 'matched', [])
    this.match()
    // 监听hashchange事件
    window.addEventListener('hashchange', this.onHashChange.bind(this))
  }

  onHashChange() {
    // 当前hash去掉#
    this.current = window.location.hash.slice(1)
    // 页面跳转重新获取匹配路由数组
    this.matched = []
    this.match()
  }

  match(routes){
    routes = routes || this.$options.routes
    // 递归遍历
    for(const route of routes){
      if(route.path === '/' && this.current === '/'){
        this.matched.push(route)
        return
      }
      // /about/info
      if(route.path !== '/' && this.current.indexOf(route.path) !== -1){
        this.matched.push(route)
        if(route.children){
          this.match(route.children)
        }
        return
      }
    }
  }
    
}

// 定义插件
MyRouter.install = function(vue){
  // 保存vue供上面响应式设置
  Vue = vue
  vue.mixin({
    // 每个实例都会调用beforeCreate
    beforeCreate(){
      // 判断,拿到main.js中new vue()传递的router对象
      if(this.$options.router){
        vue.prototype.$router = this.$options.router
      }
    }
  })

  // 全局组件注册
  Vue.component('router-link', {
    props: {
      to: {
        type: String,
        required: true
      }
    },
    // runtime-only不带编译器,预编译,打包的时候编译。这里不能用template
    // render函数只要用到响应式数据就会被依赖收集起来,等待重新调用
    render(h){
      return h(
        'a', 
        { attrs: { href: `#${this.to}` }, style: { display:'inline-block',padding:'0 10px' } }, 
        [this.$slots.default]
      )
    }
  })

  // current发生改变render函数重新渲染,实现了组件切换
  Vue.component('router-view', {
    render(h) {
      // 当前是个router-view
      this.$vnode.data.routerView = true
      // 标记当前router-view深度
      let depth = 0;
      let parent = this.$parent
      while(parent){
        const vnodeData = parent.$vnode && parent.$vnode.data
        if(vnodeData){
          if(vnodeData.routerView){
            // 说明当前parent是一个router-view
            // 有多少父router-view,自己深度+多少
            depth++
          }
        }
        parent = parent.$parent
      }

      // 获取path对应的component
      let component = null;
      const route = this.$router.matched[depth]
      if(route){
        component = route.component
      }
      return h(component)
    }      
  })
}

export default MyRouter

测试没毛病了

1603710865096.png

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis