1.Vue-Router本质
根据"不同的hash值
“或者”不同的路径地址
", 将不同的内容渲染到router-view
中
所以实现VueRouter的核心关键点就在于如何监听’hash’或’路径’的变化, 再将不同的内容写到router-view中
hash
<a href="#/home">首页</a>
<a href="#/about">关于</a>
<div id="html"></div>
// hashchange监听hash的变化
window.addEventListener('hashchange', ()=>{
// console.log('当前的hash值发生了变化');
let currentHash = location.hash.slice(1);
console.log(location,currentHash);
document.querySelector('#html').innerHTML = currentHash;
})
// 路径一开始就有hash,load 监听网络加载完成
window.addEventListener('load', ()=>{
let currentHash = location.hash.slice(1);
document.querySelector('#html').innerHTML = currentHash;
})
路径
<a onclick="go('/home')">首页</a>
<a onclick="go('/about')">关于</a>
<div id="html"></div>
function go(path) {
console.log(path);
history.pushState(null, null, path);
document.querySelector('#html').innerHTML = path;
}
// popstate监听前进或者后退
window.addEventListener('popstate', ()=>{
console.log('点击了前进或者后退', location.pathname);
document.querySelector('#html').innerHTML = location.pathname;
})
history.pushState(参数1, 参数2,参数3)
参数1:对象
参数2:当前网络标题(会自动被所有浏览器忽略)
参数3:path(需要追加的路径)
手写vue-router
// 保存相关信息,方便后面注入(this.$router, this.$route)
class SueRouteInfo {
constructor(){
// 保存当前地址
this.currentPath = null
}
}
class SueRouter {
constructor(options){
this.mode = options.mode || 'hash' // 路由模式,未指定则默认为hash
this.routes = options.routes || []
/**
* 改造配置路由
* 可以根据路由地址获取到对应的组件
* key:路由地址
* value:组件
*/
// 提取路由信息
/**
* {
* '/home': Home,
* '/about': About
* }
*/
this.routesMap = this.createRoutesMap()
console.log(this.routes,this.routesMap);
this.routeInfo = new SueRouteInfo()
// 初始化默认的路由信息
this.initDefault()
}
initDefault(){
if(this.mode === 'hash'){
console.log('location.hash', location.hash);
// 1.判断打开的界面有没有hash, 如果没有就跳转到#/
if(!location.hash){
location.hash = '/'
}
// 2.加载完成之后和hash发生变化之后都需要保存当前的地址
window.addEventListener('load', ()=>{
this.routeInfo.currentPath = location.hash.slice(1)
})
window.addEventListener('hashchange', ()=>{
this.routeInfo.currentPath = location.hash.slice(1)
})
}else {
console.log('location.pathname', location.pathname);
// 判断打开的界面有没有路径, 如果没有就跳转到/
if(!location.pathname){
location.pathname = '/'
}
// 加载完成之后和history发生变化之后都需要保存当前的地址
window.addEventListener('load', ()=>{
this.routeInfo.currentPath = location.pathname
})
window.addEventListener('popstate', ()=>{
this.routeInfo.currentPath = location.pathname
})
}
}
createRoutesMap(){
return this.routes.reduce((map, route)=>{
map[route.path] = route.component
return map
},{})
}
}
SueRouter.install = (Vue, options) => {
Vue.mixin({
beforeCreate(){
console.log('this.$options', this.$options);
/**
* import router from './router'
* this.$options.router
* new Vue({
router,
render: h => h(App)
}).$mount('#app')
*/
if(this.$options && this.$options.router){
this.$router = this.$options.router
this.$route = this.$router.routeInfo
Vue.util.defineReactive(this, 'xxx', this.$router);
// 使用Vue.util.defineReactive的原因是 render在load之前执行
// currentPath 双向绑定
}else {
this.$router = this.$parent.$router
this.$route = this.$router.routeInfo
}
}
})
/*
只要外界使用了Vue-Router, 那么我们就必须提供两个自定义的组件给外界使用
只要外界通过Vue.use注册了Vue-Router, 就代表外界使用了Vue-Router
只要接通通过Vue.use注册了Vue-Router, 就会调用插件的install方法
所以我们只需要在install方法中注册两个全局组件(router-link router-view)给外界使用即可 //
* */
Vue.component('router-link',{
props:{
to: String
},
render(){
/**
* 注意点: render方法中的this并不是当前实例对象, 而是一个代理对象
如果我们想拿到当前实例对象, 那么可以通过this._self获取
*/
/**
* this._self 才能拿到当前Vue实例
*/
// history / hash:this._self.$router.mode
let path = this.to // this.to不能直接修改
if(this._self.$router.mode === 'hash'){
path = '#' + path
}
return <a href={path}>{this.$slots.default}</a> // 获取内容 this.$slots.default
}
})
Vue.component('router-view', {
render(h){
let routesMap = this._self.$router.routesMap;
let currentPath = this._self.$route.currentPath;
let currentComponent = routesMap[currentPath]
return h(currentComponent)
}
})
}
export default SueRouter;
学习记录❥(^_-)