本文章主要用于本人观看
// 报错Cannot call a class as a function:用class不行,所以得换成函数
// class myRouter {
// constructor(){
// console.log(1)
// }
// }
/*
我们需要一个路由状态,需要通过这个状态的变化更改组件。
当hash变化会触发onhashchange去改变这个路由状态,defineProperty去监控这个路由状态
*/
class History{
constructor(){
/*
current就是路由的状态{current:'/home'},当这个状态变化的时候,会触发defineProperty,
拿到current的状态值,改变组件
*/
this.current = null
}
}
function MyRouter(opt){
// 脚手架一上来就会运行一次,都挂好后再运行一次,运行两次
console.log(this) // this第一次没有
if(this){
// console.log(opt)
// 默认hash,
this.mode = opt.mode || 'hash';
this.routes = opt.routes || [];
this.routeMap = this.createMap(this.routes)
// 把路由状态挂载出来
this.history = new History
// 初始化路由
this.init()
console.log(this.routeMap)
}
}
// 当给prototype赋址一个对象时必须要手动改变this指向
MyRouter.prototype = {
// 改变this指向
constructor:MyRouter,
createMap(routes){ // routes数组拉平,拉成一个对象,直接用数组也可以
return routes.reduce((prev,next) => {
prev[next.path] = next.component
return prev
},{})
},
init(){
// 初始化路由的时候看看mode是什么,走什么路由
if(this.mode === 'hash'){ // hash路由
// 没有hash就走/
if(!location.hash)location.hash = '/'
// 一开始就
window.addEventListener('load',() => {
// console.log('load')
this.history.current = location.hash.slice(1)
console.log(this.history.current)
})
// 当hash发生变化的时候改变current值
window.addEventListener('hashchange',() => {
this.history.current = location.hash.slice(1)
console.log(this.history.current)
})
}else if(this.mode === 'history'){ // history路由
// 没有pathname就走/
if(!location.pathname)location.hash = '/'
window.addEventListener('load',() => {
this.history.current = location.pathname
console.log(this.history.current)
})
// 当hash发生变化的时候改变current值
window.addEventListener('popstate',() => {
this.history.current = location.pathname
})
}
},
push(path){
if(this.mode === 'hash'){
location.hash = '#' + path
}else if(this.mode === 'history'){
location.pathname = path
}
}
}
// 只要是用了Vue.use(xxx),就会调用xxx.install方法,就能拿到方法,并且再use的时候会把Vue的实例当作参数传进来
MyRouter.install = function(Vue){
console.log(this)
// 每个组件都能拿到mixin里的内容
Vue.mixin({
beforeCreate () {
// console.log('beforeCreate')
/*
这里的this代表每个组件
$options;可以看到所有vue实例上的所有的自定义的属性
在main.js中可以看到new Vue上挂了一个router的属性
*/
console.log(this)
// this.$options.router 组件的$options的身上有router的属性说明是根组件
// 让每个组件都能拿到MyRoute的实例,用来拿到MyRoute上的属性和方法
if(this.$options && this.$options.router){ // 根组件
console.log(this)
this._root = this;
this._router = this.$options.router
//深度监听current
Vue.util.defineReactive(this,'xxx',this._router.history)
console.log(this._router)
}else{ // 不是根组件,是子组件
// console.log(this.$parent)
if(this.$parent){
this._root = this.$parent._root // this._root = this.$parent._root
this._router = this.$parent._router
console.log(this._router)
}
}
// 用definePrototype给每一个组件添加一个$route和$router的属性
Object.defineProperty(this,'$route',{
get:() => {
console.log(this._router.history)
return {
current:this._router.history.current
}
}
})
Object.defineProperty(this,'$router',{ // defineProperty
get:() => {
console.log(this._router)
return this._router
}
})
}
})
Vue.component('router-view',{
render(h){
console.log(this)
const {routeMap,history:{current}} = this._self._router
console.log(this._self._router.history)
return h(
routeMap[current]
)
}
})
Vue.component('router-link',{
props:['to','tag'],
methods: {
click(ev,mode){
console.log(ev)
if(mode === 'hash'){
location.hash = ev.target.getAttribute('href').split('#')[1];
}else if(mode === 'history'){
location.pathname = ev.target.getAttribute('href')
}
}
},
render(){
const {mode} = this._self._router
const {to} = this
// console.log(this)
// 默认插槽就是内容
// console.log(this.tag)
if(this.tag){
return <this.tag on-click={(ev) => {this.click(ev,mode)}} href={mode === 'hash'?`#${to}`:to}>{this.$slots.default}</this.tag>
}
return <a href={mode === 'hash'?`#${to}`:to}>{this.$slots.default}</a>
}
})
}
export default MyRouter