这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
单页面应用程序中,url发生变化的时候,不能刷新页面但是对于的视图内容会发生变化
1. 要手写咱们先来说说vue-router的使用步骤
- 在router.js文件里面使用vue-router插件
import Router from 'vue-router Vue.use(Router)
- 在router.js文件里面创建Router实例并导出
export default new Router({...})
- 在main.js文件里面在根实例上添加该实例
import router from './router new Vue({ router, }).$mount("#app");
- 在App.vue文件里面添加路由视图以及路由跳转链接
<router-link to="/home"></router-link> <!--路由出口--> <router-view></router-view>
this.$router.push('/') this.$router.push('/home')
2. 通过使用步骤咱们来思考几个问题,带着这些疑问咱们去做,疑问解决了也就懂了
- Vue.use(Router) 这个use干什么用的为啥要use一下(是否Vue插件都要这么处理呢?)
- new Vue({ router, }).$mount("#app");这里为什么要将router实例放到new Vue的options里面
- <router-link to="/home"></router-link><router-view></router-view>这二个组件为什么可以直接使用
- this.router这个路由器实例
3. 我们的需求
- SPA页面不能刷新页面
- hash对应来切换 ==> hashchange事件
- h5的History api来实现
- 根据url来显示的内容要做的事
- 实现组件router-view与router-link
- 数据响应式处理,我的hash变了,就应该动态的重新渲染页面的内容 render
4. 根据需求以及疑问我们要完成的事情也就有了
- 实现一个插件为主线
- 实现VueRouter类
- 处理路由器选项(options)
- 监控url变化,hashChange
- 响应这个变化,渲染新的内容
- 实现install方法(所有的vue插件都必须要有install方法),调用时机就是在Vue.use的时候会触发
- $router注册到Vue实例上去
- 实现router-view 与 router-link全局组件
- 实现VueRouter类
5. 代码实现
let Vue;
class VueRouter {
constructor(options){
this.$options = options;
this.$routerMap = {};
// current的做成响应式的
// 实现方法呢? ==>
// 1.用new Vue()来实现
// 2.用Vue.util.defineReactive()来实现
// this.current = window.location.hash.slice(1) || '/';
Vue.util.defineReactive(this, 'current' ,window.location.hash.slice(1) || '/')
window.addEventListener('hashchange', () => {
// 这里虽然改变了current但是这个current并不是响应式的,
// 所以不会触发组件更新(如果不用Vue.util.defineReactive处理的话)
this.current = window.location.hash.slice(1)
})
// 存储一份路由表对象后面就不需要去遍历了
this.$options.routes.forEach(item =>{
this.$routerMap[item.path] = item
})
}
}
VueRouter.install = function(_vue){
// 所以vue-router对Vue是强引用
Vue = _vue;
// $router注册到Vue实例里面
// 但是我们此时不能获得到Vue实例那我们需要在某个时机去做这个注册的事 ===> 混入Vue.mixin
Vue.mixin({
beforeCreate() {
// 这里拿到的this就是vue实例
console.log(this)
if(this.$options.router){
//根组件 实例 才会有router在实例化的时候传入的
Vue.prototype.$router = this.$options.router;
console.log(this.$options.router)
}
}
})
// // 实现二个全局组件 router-link 与 router-view
Vue.component('router-link',{
// 用法<router-link to="/home">主页</router-link>所以href就可以从props属性来获取
props: {
to: {
type: String,
require: true
}
},
render(h){
// this.$slots.default就是下载router-link标签里面的内容例如:主页
return h('a', {attrs: { href: '#' + this.to }}, this.$slots.default)
}
})
Vue.component('router-view', {
// render的执行时机
// 1.init初始化的时候
// 2.依赖的值发生变化的时候会执行
render(h){
// 这个组件就是根据url来渲染对应的组件内容配置项
// {
// path: '/',
// name: 'app',
// component: Home
// }
// 1. 获取hash部分 #/home 如何获取呢 通过window.location.hash
// 我们的current现在是响应式了
// 所以this.$router.current这个是动态变化的就会触发render的更新
// 2. 根据上面的地址获取对应组件配置home组件,那么我们如何获取到路由的配置选项呢?
// this.$router.$routerMap就是路由映射表
// 3. h(Home)
console.log(this.$router.$options)
console.log(this.$router.current)
console.log(this.$router.$routerMap);
let Component;
Component = this.$router.$routerMap[this.$router.current].component
return h(Component)
}})
}
export default VueRouter
6. 这是最最简单一种vue-router的实现(利用hash来做的)
7. 还有一些问题以及思考
1. 嵌套路由不支持要怎么处理也就是配置项里面有children选项的时候代码怎么写
2. 在创建路由配置项的时候是可以传递一个属性:mode:'history', history模式的代码怎么实现
3. 为什么Vue要在install的时候传入而不是在里面通过import引入呢(减少依赖不用打包)