美文网首页
vue2-vue-router

vue2-vue-router

作者: AAA前端 | 来源:发表于2021-05-22 14:02 被阅读0次

用法

    1. 使用vue-router插件 router.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
    1. 创建Router实例 router.js
export default new Router({...})
    1. 在跟组件上添加实例, main.js
import router from './router.js'
new Vue({
  router,
}).$mount('#app')

    1. 添加路由视图 APP.vue

<router-view></router-view>

实现原理

Vue.use(xxx)

那么 xxx要有install方法

创建我们自己的my-vue-router.js

let Vue 

// 实现插件
class VueRouter {
  constructor(){
    Vue
  }
}

// 插件要有一个install 方法 
// 接受一个构造函数 _Vue
VueRouter.install = function(_Vue){
  Vue = _Vue // 保存 _Vue
}

export default VueRouter

页面会有报错

image.png

说明需要有 两个组件 需要 注册 封闭是router-link 和 router-view
那么我们全家注册一下

let Vue 

// 实现插件
class VueRouter {
  constructor(){
    Vue
  }
}

// 插件要有一个install 方法 
// 接受一个构造函数 _Vue
VueRouter.install = function(_Vue){
  Vue = _Vue // 保存 _Vue

  // 注册router-view  router-link 组件
  Vue.component('router-view',{})
  Vue.component('router-link',{})
}

export default VueRouter

但是又产生了新的错误


image.png

组件 需要template或者render函数

由于我们写template 的时候,是处于runtime-only 阶段,是没有编译器的,对于template模板无法解析

  Vue.component('router-view',{
    template: '<div>router-view</div>'
  })
image.png

所有我们只能用render解决

let Vue 

// 实现插件
class VueRouter {
  constructor(){
    Vue
  }
}

// 插件要有一个install 方法 
// 接受一个构造函数 _Vue
VueRouter.install = function(_Vue){
  Vue = _Vue // 保存 _Vue

  // 注册router-view  router-link 组件
  Vue.component('router-view',{
    render(h){
      return h('div', 'router-view')
    }
  })
  Vue.component('router-link',{
    render(h){
      return h('div', 'router-link')
    }
  })
}

export default VueRouter

控制台终于不报错了。

接下来处理 router-link
<router-link to="/foo">Go to Foo</router-link>
我们使用 的是上面的形式, 在render中 第一个参数就 ‘router-link(a标签)’, 第二个参数里面就是跳转的地址, 第三个参数就是插槽的内容

 Vue.component('router-link', {
    // 必须传入一个 to 属性 
    props: {
      to: {
        type: String,
        required: true
      }
    },
    render(h) {
      // <router-link to="/foo">Go to Foo</router-link>
      // <a href="#/foo">Go to Foo</a>
      return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)
    }
  })

现在router-link其实已经变为了a标签,点击 浏览器的url也会相应变化了。
现在我们要处理 把router-link 当前对应组件的内容 拿出来 并放到 router-view中

意思就是我们从url 中找到对应的组件 ,router.js中就有这个映射关系

const routes = [
  { path: '/foo', component: foo },
  { path: '/bar', component: bar }
]

还有一个就是我们 组件 用h函数也能直接渲染
比如 我们在my-vue.router.js中直接 引入foo组件 并在 router-view中直接渲染

import foo from './components/foo.vue'
// 插件要有一个install 方法 
// 接受一个构造函数 _Vue
VueRouter.install = function (_Vue) {
  Vue = _Vue // 保存 _Vue

  // 注册router-view  router-link 组件
  Vue.component('router-view', {
    render(h) {
      // 从url 中 找到对应的 component  
      return h(foo)
      // return h('div', 'router-view')
    }
  })

image.png

页面直接就渲染出了foo 组件的内容

好,现在我们去拿到url和 routes 里面 的映射表

其中url 通过window.location.hash 就可以拿到

但是 routes 在my-vue-router中就麻烦一下。
router.js

import Vue from 'vue'
import VueRouter from './my-vue-router'
import foo from './components/foo.vue'
import bar from './components/bar.vue'

Vue.use(VueRouter)

const routes = [
  { path: '/foo', component: foo },
  { path: '/bar', component: bar }
]


export default new VueRouter({
  routes
})

我们在VueRouter 实例化的时候 把参数routes存起来
my-vue-router.js

// 实现插件
class VueRouter {
  constructor(options) {
    this.options = options  // new VueRouter 传入的配置
  }
}

在router.js中我们是先 use 调用 install方法 ,然后再new VueRouter传入的routes。所有我们要获取routes,
需要再install方法中全局混入beforeCreate的生命周期(只有再new Vue的时候才执行), 这样我们就可以在执行的时候获取到配置的router并给Vue.prototype.$router绑定上, 让所有的组件都可以访问到。

VueRouter.install = function (_Vue) {
  Vue = _Vue // 保存 _Vue

  // 全局混入 beforeCreare生命周期
  Vue.mixin({
    // 延迟执行 ,只有再new Vue的时候才执行 这个时候就可以拿到 router了
    beforeCreate(){
      // 只有 在根组件中存在 ,所有只执行一次
      // 获取 main.js  new Vue({ render: h => h(App), router }).$mount('#app')  中的router
      if(this.$options.router){
        // 让所有的组件实例 可以用到router
        console.log('this.$options', this.$options);
        Vue.prototype.$router = this.$options.router
      }
    }
  })

现在我们监控url的变化
my-vue-route.js

class VueRouter {
  constructor(options) {
    this.options = options // new VueRouter 传入的配置
    this.current = window.location.hash.slice(1) || '/' // url 上hash 的变化
    // 监课url变化 
    window.addEventListener('hashchange', ()=>{
      this.current = window.location.hash.slice(1) // 去掉#
    })
  }
}

我们此时看一下 根组件的 this.$options


image.png

这个时候我通过 组件 上的router就能访问到当前 path 和 routes配置项了

然后我们到 router-view组件处理

  Vue.component('router-view', {
    render(h) {
      // 从url 中 找到对应的 component
      let component = null
      // 获取 current 和 options
      const { current, options } = this.$router;
      // 拿出 options 中 routes 配置项 和current 进行对比
      // 获取对应的 组件
      const route = options.routes.find(route => route.path === current)
      if (route) {
        component = route.component
      }

      return h(component)
    }
  })

这个时候的页面我们再点击 route-link的时候 url会变化 ,但是router-view却不会刷新,我们刷新一下页面。router-view才能变化。

原因是 current 不是响应式的,把current 变为响应式后,使用它的组件都会重新render. 这样就会重新渲染了

Vue.util.defineReactive(this, 'current', window.location.hash.slice(1) || '/')

class VueRouter {
  constructor(options) {
    this.options = options // new VueRouter 传入的配置
    // this.current = window.location.hash.slice(1) || '/' // url 上hash 的变化
    Vue.util.defineReactive(this, 'current', window.location.hash.slice(1) || '/')
    // 监课url变化 
    window.addEventListener('hashchange', () => {
      this.current = window.location.hash.slice(1) // 去掉#
    })
  }
}

或者我们用new Vue生成响应式 ,由于new Vue 中data是一个对象,所有我们把current 包装一下

// 实现插件
class VueRouter {
  constructor(options) {
    this.options = options // new VueRouter 传入的配置
    // this.current = window.location.hash.slice(1) || '/' // url 上hash 的变化
    // Vue.util.defineReactive(this, 'current', window.location.hash.slice(1) || '/')
    this.obj  = new Vue({
      data: { current: window.location.hash.slice(1) || '/'}
    })
    // 监课url变化 
    window.addEventListener('hashchange', () => {
      this.obj.current = window.location.hash.slice(1) // 去掉#
    })
  }
}
// 此时 current 就变为响应式的了
....
  // 注册router-view  router-link 组件
  Vue.component('router-view', {
    render(h) {
      // 从url 中 找到对应的 component
      let component = null
      // 获取 current 和 options
      const { obj: {current}, options } = this.$router;
      // 拿出 options 中 routes 配置项 和current 进行对比
      // 获取对应的 组件
      const route = options.routes.find(route => route.path === current)
      if (route) {
        component = route.component
      }

      return h(component)
    }
  })

完整的my-vue.router.js

let Vue

// 实现插件
class VueRouter {
  constructor(options) {
    this.options = options // new VueRouter 传入的配置
    // this.current = window.location.hash.slice(1) || '/' // url 上hash 的变化
    Vue.util.defineReactive(this, 'current', window.location.hash.slice(1) || '/')
    // 监课url变化 
    window.addEventListener('hashchange', () => {
      this.current = window.location.hash.slice(1) // 去掉#
    })
  }
}
// 插件要有一个install 方法 
// 接受一个构造函数 _Vue
VueRouter.install = function (_Vue) {
  Vue = _Vue // 保存 _Vue

  // 全局混入 beforeCreare生命周期
  Vue.mixin({
    // 延迟执行 ,只有再new Vue的时候才执行 这个时候就可以拿到 router了
    beforeCreate() {
      // 只有 在根组件中存在 ,所有只执行一次
      // 获取 main.js  new Vue({ render: h => h(App), router }).$mount('#app')  中的router
      if (this.$options.router) {
        // 让所有的组件实例 可以用到router
        console.log('this.$options', this.$options);
        Vue.prototype.$router = this.$options.router
      }
    }
  })

  // 注册router-view  router-link 组件
  Vue.component('router-view', {
    render(h) {
      // 从url 中 找到对应的 component
      let component = null
      // 获取 current 和 options
      const { current, options } = this.$router;
      // 拿出 options 中 routes 配置项 和current 进行对比
      // 获取对应的 组件
      const route = options.routes.find(route => route.path === current)
      if (route) {
        component = route.component
      }

      return h(component)
    }
  })
  Vue.component('router-link', {
    // 必须传入一个 to 属性 
    props: {
      to: {
        type: String,
        required: true
      }
    },
    render(h) {
      // <router-link to="/foo">Go to Foo</router-link>
      // <a href="#/foo">Go to Foo</a>
      return h('a', { attrs: { 'href': '#' + this.to } }, this.$slots.default)
    }
  })
}

export default VueRouter


相关文章

  • vue2-vue-router

    用法 使用vue-router插件 router.js 创建Router实例 router.js 在跟组件上添加实...

网友评论

      本文标题:vue2-vue-router

      本文链接:https://www.haomeiwen.com/subject/mqbnjltx.html