可以思考下平常在使用vue-router时经历了那几个步骤?
- 步骤一:使用vue-router插件 router.js
import Router from 'vue-router'
Vue.use(Router)
- 步骤二:创建Router实列,router.js
export default new Router({...})
- 步骤三:在更组件上添加改实例 main.js
import router from './router'
new Vue({
router,
}).mount("#app");
- 步骤四:添加路由视图,App.vue
<router-view></router-view>
- 导航
<router-link to='/'>Home</router-link>
<router-link to='/about'>About</router-link>
思考问题:
为什么Router要use一下?它里面做了什么事情?
创建的router实例,为什么要添加到更组件里面?目的是什么?
<router-view> 为什么可以直接使用,他的作用的什么?
<router-link> 为什么可以使用它做导航?它的导航到底是怎么实现的?
vue-router 的源码实现
- 单页面应用程序中 url发生变化时候,不能刷新,显示对应的视图内容
需求分析
- spa页面不能刷新
hash #/about
History api/about
- 根据url显示对应的内容
router-view
数据响应式:current变量持有url地址,一旦变化,动态重新执行render
任务
- 实现一个插件
实现VueRouter类
1.处理路由选项
2.监控url变化,hashChange
3.响应这个变化
实现install方法
1.$router注册 (将来能在任何一个组件里使用$router.push()实现路由跳转 等。。。。)
2.两个全局组件(router-view router-link)
代码实现
- vue-router.js
/*
vue 插件的编写
实现一个install方法
*/
let Vue;
class VueRouter {
constructor(options){
// console.log(Vue);
this.$options = options
// console.log(options);
// 保存当前hash到current
// current 必须是响应式的,
// Vue.set(this,current,'/') 不行,set要求传入的对象为响应式对象
// Object.defineProperty() 不行,defineProperty 只是拦截,并没有建立依赖关系
// 方法一 让vue替我们去做current 的响应式工作,但是这样比较繁琐
/* new Vue({
data(){
return{
current:'/'
}
}
}) */
// 方法二 vue隐藏API 作用 :给指定对象定义一个响应式属性
Vue.util.defineReactive(this,'current',window.location.hash.slice(1)||'/')
// this.current = '/'
// 监控hashChange
window.addEventListener('hashchange',()=>{
// #/about => /about
this.current = window.location.hash.slice(1)
})
}
}
// 形参1是vue的构造函数 install.call(VueRouter,Vue) 方便在插件内部对Vue做一些深加工处理 目的是:便于扩展
VueRouter.install = function (_Vue) {
Vue = _Vue
// 1. 将$router注册一下
// 如果直接注册会访问不到vue实列,因为install方法在use的时候就执行了
// Vue.$router =
// 所有必须将$router注册推迟到将来的某一时刻:跟实列创建前
Vue.mixin({
beforeCreate(){
// 只需要跟实列时注册一次
if (this.$options.router) {
Vue.prototype.$router = this.$options.router
}
}
})
//2. 注册两个全局组件,router-view router-link
Vue.component('router-link',{
props:{
to:String,
require:true
},
// template:"<div>touter-link</div>"
// h就说createdElement() 作用返回一个虚拟DOM
// <router-link to="/about">asdf</router-link>
// 获取插槽内容:this.$slots.default
render(h){
// console.log(this.$slots.default);
// console.log(h);
return h('a',{
attrs:{
href:'#'+this.to//值未组件上传进来的to属性的值
}//a标签的特性
},this.$slots.default)
}
})
Vue.component('router-view',{
// template:"<a>touter-view</a>"
render(h){
// 可以·传入一个组件直接渲染
// 思路:可以根据url的hash部分动态匹配这个渲染的组件。从而实现动态渲染
// window.location.hash 与路由映射表(routers)里面的pash进行对比
// console.log(this.$router.$options.routes);
// console.log(this.$router.current);
const router = this.$router.$options.routes.find(router=>router.path===this.$router.current)
let {component} = router
return h(component)
}
})
}
export default VueRouter