1.Vue-Router本质
根据"不同的hash值"或者"不同的路径地址",将不同的内容渲染到router-view中,所以实现VueRouter的核心关键点就在于如何监听"hash"或"路径"的变化,再将不同的内容写到router-view中.
基本使用
<body>
<!-- <a href="#/home">首页</a>
<a href="#/about">关于</a>
<div id="html"></div> -->
<a onclick="go('/home')">首页</a>
<a onclick="go('/about')">关于</a>
<div id="html"></div>
<script>
// // 原生监听哈希的变化
// window.addEventListener('hashchange', () => {
// // console.log('当前的hash值发生了变化');
// let currentHash = location.hash.slice(1)
// // console.log(location.hash);// #/home
// // console.log(currentHash);// /home
// document.querySelector('#html').innerHTML = currentHash
// })
// //原生加载完毕就获取哈希
// window.addEventListener('load', () => {
// let currentHash = location.hash.slice(1)
// document.querySelector('#html').innerHTML = currentHash
// })
function go(path) {
// console.log(path);
// history.pushState() 方法向当前浏览器会话的历史堆栈中添加一个状态(state)
// 参数:对象,标题(但是会被所有浏览器忽略),追加的路径
history.pushState(null,null,path)
document.querySelector('#html').innerHTML=path
}
// 点击前进后退执行,当活动历史记录条目更改时,将触发popstate事件
window.addEventListener('popstate', () => {
// pathname 属性是一个可读可写的字符串,可设置或返回当前 URL 的路径部分
console.log('点击了前进或者后退',location.pathname);
document.querySelector('#html').innerHTML=location.pathname
})
</script>
</body>
2.提取路由信息
路由是一个插件
router/index.js
import Vue from 'vue'
// import VueRouter from 'vue-router'
import SueRouter from './Sue-Router'
import Home from '../views/Home.vue'
// Vue.use(VueRouter)
Vue.use(SueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
// const router = new VueRouter({
const router = new SueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
自定义路由js
class SueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
//提取路由信息
// 格式:{
// '/home':hasOwnMetadata,
// '/about':About
// }
this.routesMap = this.createRoutesMap()
}
createRoutesMap() {
// map是空对象,route是遍历出来的内容
return this.routes.reduce((map, route) => {
// 追加内容,path为key,component为值
map[route.path] = route.component
return map
}, {})
}
}
SueRouter.install = (Vue, options) => {
}
export default SueRouter
初始化路由信息
创建单独的类用来保存当前路由,初始化默认的路由信息
// ==============================更新==================
// 保存组件注入
class NewRouteInfo {
constructor() {
// 保存当前路由
this.currentPath = null
}
}
// this.$router对应
class SueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
//提取路由信息
// 格式:{
// '/home':hasOwnMetadata,
// '/about':About
// }
this.routesMap = this.createRoutesMap()
// ======================更新===============
// 用于赋值
this.routeInfo = new NewRouteInfo()
// 初始化默认的路由信息,监听,设置
this.initDefault()
}
// =============================更新=================
initDefault() {
// 区分哈希还是history
if (this.mode === 'hash') {
if (!location.hash) {
location.hash = '#/'
}
// 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.hash.slice(1)
})
//变化的时候保存
window.addEventListener('hashchange', () => {
this.routeInfo.currentPath = location.hash.slice(1)
console.log(this.routeInfo);
})
} else {
if (!location.pathname) {
location.pathname = '/'
} // 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.pathname
})
//变化的时候保存
window.addEventListener('popstate', () => {
this.routeInfo.currentPath = location.pathname
console.log(this.routeInfo);
})
}
}
createRoutesMap() {
// map是空对象,route是遍历出来的内容
return this.routes.reduce((map, route) => {
// 追加内容,path为key,component为值
map[route.path] = route.component
return map
}, {})
}
}
SueRouter.install = (Vue, options) => {
}
export default SueRouter
效果
添加全局$router属性
在index使用use调用组件时,就会执行install方法,可以在里面添加mixin方法,因为只要创建一个组件就会执行一次mixin方法,这样就可以达到给每个实例都添加$router
// 保存组件注入
class NewRouteInfo {
constructor() {
// 保存当前路由
this.currentPath = null
}
}
// this.$router对应
class SueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
//提取路由信息
// 格式:{
// '/home':hasOwnMetadata,
// '/about':About
// }
this.routesMap = this.createRoutesMap()
// 用于赋值
this.routeInfo = new NewRouteInfo()
// 初始化默认的路由信息,监听,设置
this.initDefault()
}
initDefault() {
// 区分哈希还是history
if (this.mode === 'hash') {
if (!location.hash) {
location.hash = '#/'
}
// 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.hash.slice(1)
})
//变化的时候保存
window.addEventListener('hashchange', () => {
this.routeInfo.currentPath = location.hash.slice(1)
console.log(this.routeInfo);
})
} else {
if (!location.pathname) {
location.pathname = '/'
} // 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.pathname
})
//变化的时候保存
window.addEventListener('popstate', () => {
this.routeInfo.currentPath = location.pathname
console.log(this.routeInfo);
})
}
}
createRoutesMap() {
// map是空对象,route是遍历出来的内容
return this.routes.reduce((map, route) => {
// 追加内容,path为key,component为值
map[route.path] = route.component
return map
}, {})
}
}
// ======================更新=========================
SueRouter.install = (Vue, options) => {
// 给每个实例都添加$router与route属性
// 在install方法中调用mixin,mixin只要创建组件就会执行
Vue.mixin({
// 重写beforeCreate方法
beforeCreate() {
// 会先创建根组件,再创建子组件,根组件就是main.js new出来的组件
// 在创建的时候已经有router,所以只需要将router变成$router即可
// 如果创建的时候传递了参数
if (this.$options && this.$options.router) {
this.$router = this.$options.router
// console.log(this.$router);
// console.log(this.$router.routeInfo);
// route是上面SueRouter创建的实例,而该实例保存在routeInfo当中
this.$route = this.$router.routeInfo
} else {
// 如果在创建的时候没有传递参数,代表是子组件,就将父组件赋值给子组件
this.$router = this.$parent.$router
// console.log(this.$router.routeInfo);
this.$route = this.$router.routeInfo
}
}
})
}
export default SueRouter
效果
实现router-link
只要使用vue实例,就需要提供两个全局组件给外界使用,而使用时会调用install,所以在install里面可以写全局组件
// 保存组件注入
class NewRouteInfo {
constructor() {
// 保存当前路由
this.currentPath = null
}
}
// this.$router对应
class SueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
//提取路由信息
// 格式:{
// '/home':hasOwnMetadata,
// '/about':About
// }
this.routesMap = this.createRoutesMap()
// 用于赋值
this.routeInfo = new NewRouteInfo()
// 初始化默认的路由信息,监听,设置
this.initDefault()
}
initDefault() {
// 区分哈希还是history
if (this.mode === 'hash') {
if (!location.hash) {
location.hash = '#/'
}
// 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.hash.slice(1)
})
//变化的时候保存
window.addEventListener('hashchange', () => {
this.routeInfo.currentPath = location.hash.slice(1)
console.log(this.routeInfo);
})
} else {
if (!location.pathname) {
location.pathname = '/'
} // 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.pathname
})
//变化的时候保存
window.addEventListener('popstate', () => {
this.routeInfo.currentPath = location.pathname
console.log(this.routeInfo);
})
}
}
createRoutesMap() {
// map是空对象,route是遍历出来的内容
return this.routes.reduce((map, route) => {
// 追加内容,path为key,component为值
map[route.path] = route.component
return map
}, {})
}
}
SueRouter.install = (Vue, options) => {
// 给每个实例都添加$router与route属性
// 在install方法中调用mixin,mixin只要创建组件就会执行
Vue.mixin({
// 重写beforeCreate方法
beforeCreate() {
// 会先创建根组件,再创建子组件,根组件就是main.js new出来的组件
// 在创建的时候已经有router,所以只需要将router变成$router即可
// 如果创建的时候传递了参数
if (this.$options && this.$options.router) {
this.$router = this.$options.router
// console.log(this.$router);
// console.log(this.$router.routeInfo);
// route是上面SueRouter创建的实例,而该实例保存在routeInfo当中
this.$route = this.$router.routeInfo
} else {
// 如果在创建的时候没有传递参数,代表是子组件,就将父组件赋值给子组件
this.$router = this.$parent.$router
// console.log(this.$router.routeInfo);
this.$route = this.$router.routeInfo
}
}
})
// ===============================更新===================================
// 只要外界使用了Vue-Router,那么就必须提供两个自定义的组件给外界使用
// 只要外界通过Vue.use注册了Vue-Router,就代表外界使用了Vue-Router
Vue.component('router-link', {
// 外界会通过"to"来传递参数,子组件可以通过props接收父组件传递的参数
props: {
to: String
},
render() {
// 不能直接修改to
let path = this.to
if (this._self.$router.mode === 'hash') {
path = "#" + path
}
// 返回的是a标签,代表外界使用router-link就是a标签
// 再把to的参数放到a标签
return <a href={path}>{this.$slots.default}</a>
}
})
}
export default SueRouter
效果
实现router-view
注册router-view全局组件给外界使用
// 保存组件注入
class NewRouteInfo {
constructor() {
// 保存当前路由
this.currentPath = null
}
}
// this.$router对应
class SueRouter {
constructor(options) {
this.mode = options.mode || 'hash'
this.routes = options.routes || []
//提取路由信息
// 格式:{
// '/home':hasOwnMetadata,
// '/about':About
// }
this.routesMap = this.createRoutesMap()
// 用于赋值
this.routeInfo = new NewRouteInfo()
// 初始化默认的路由信息,监听,设置
this.initDefault()
}
initDefault() {
// 区分哈希还是history
if (this.mode === 'hash') {
if (!location.hash) {
location.hash = '#/'
}
// 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.hash.slice(1)
})
//变化的时候保存
window.addEventListener('hashchange', () => {
this.routeInfo.currentPath = location.hash.slice(1)
console.log(this.routeInfo);
})
} else {
if (!location.pathname) {
location.pathname = '/'
} // 加载完毕就保存路由地址
window.addEventListener('load', () => {
this.routeInfo.currentPath = location.pathname
})
//变化的时候保存
window.addEventListener('popstate', () => {
this.routeInfo.currentPath = location.pathname
console.log(this.routeInfo);
})
}
}
createRoutesMap() {
// map是空对象,route是遍历出来的内容
return this.routes.reduce((map, route) => {
// 追加内容,path为key,component为值
map[route.path] = route.component
return map
}, {})
}
}
SueRouter.install = (Vue, options) => {
// 给每个实例都添加$router与route属性
// 在install方法中调用mixin,mixin只要创建组件就会执行
Vue.mixin({
// 重写beforeCreate方法
beforeCreate() {
// 会先创建根组件,再创建子组件,根组件就是main.js new出来的组件
// 在创建的时候已经有router,所以只需要将router变成$router即可
// 如果创建的时候传递了参数
if (this.$options && this.$options.router) {
this.$router = this.$options.router
// console.log(this.$router);
// console.log(this.$router.routeInfo);
// route是上面SueRouter创建的实例,而该实例保存在routeInfo当中
this.$route = this.$router.routeInfo
// =====================更新===============================
// 变成双向数据绑定
Vue.util.defineReactive(this,'xxx',this.$router)
} else {
// 如果在创建的时候没有传递参数,代表是子组件,就将父组件赋值给子组件
this.$router = this.$parent.$router
// console.log(this.$router.routeInfo);
this.$route = this.$router.routeInfo
}
}
})
// 只要外界使用了Vue-Router,那么就必须提供两个自定义的组件给外界使用
// 只要外界通过Vue.use注册了Vue-Router,就代表外界使用了Vue-Router
Vue.component('router-link', {
// 外界会通过"to"来传递参数,子组件可以通过props接收父组件传递的参数
props: {
to: String
},
render() {
// 不能直接修改to
let path = this.to
if (this._self.$router.mode === 'hash') {
path = "#" + path
}
// 返回的是a标签,代表外界使用router-link就是a标签
// 再把to的参数放到a标签
return <a href={path}>{this.$slots.default}</a>
}
})
// =========================更新==================================
// 注册全局组件给外界使用
Vue.component('router-view', {
// 根据当前的路由地址,取出对应的组件,并将组件渲染到router-view
// 注意点:渲染的时候会先渲染组件,所有组件渲染完时才渲染load,导致无法渲染对应的组件
// 解决:只要把route变为双向绑定
render(h) {
// 调用渲染函数,把我们渲染的组件传递过去
// 根据路由地址与路由信息去取出对应的组件
let routesMap=this._self.$router.routesMap
let currentPath=this._self.$route.currentPath
let currentComponent=routesMap[currentPath]
return h(currentComponent)
}
})
}
export default SueRouter
效果