实现简易路由前置知识点
路由模式
hash模式
- 通过锚点(也就是hash)进行跳转
- 带
#
号 - URL会更改
- 浏览器可以前进后退,当时浏览器不会刷新
- 不合服务端交流
-
history模式
无锚点 无hash
- 无
#
- 是一个正常URL
- 会和服务端交流
- 路径名保存在
location.pathname
上路由信息
$router
路由实例对象
可读可写
路由信息保存再History下的对象current中$route
只读,路由信息对象,数据是从$router.history.current下的数据的数据路由组件
router-link
组件支持用户在具有路由功能的应用中router-view
渲染路径匹配到的视图组件路由的切换//页面的跳转
hash模式
页面的跳转也就是hash的改变
可以监听hashchange
事件 该事件会再hash改变时触发
当一次打开页面视图组件啥都不显示
此时需要使用DomcontentLoaded
事件(该事件会在dom内容加载完毕时触发)将其hash值修改为首页的hash值原理
就是监听hashchange
事件当页面改变通过获取hash值来找到对应的组件来渲染页面注意
当为a元素时,直接添加将路径添加到href上就行啦
当不是a元素时,通过监听单击事件来改变hash值history模式
所有的元素都是通过单击事件来触发页面的跳转
触发单击事件时使用history.pushState()
来实现页面的跳转
history的路径信息是存储在location.pathname
中
但是页面的前进关于回退会失效
可是使用popstate
事件(当浏览器前进与后退时触发)将location.pathname
中的值修改
当一次打开页面视图组件啥都不显示
此时需要使用DomcontentLoaded
事件(该事件会在dom内容加载完毕时触发)将其location.pathname
值修改为首页的hash值原理
同过元素的单击事件时将其路由路径传入,通过history.pushState()
来实现的页面的跳转,在通过popstate
来实现也面的前进与后退,第一次代开页面 通过DomcontentLoaded
来渲染首页use方法
如上面代码Vue.use(VueRouter)
use 方法是干嘛的
use方法就是桥梁,开发一个在vue上使用的工具或插件它必须要使用vue的构造函数或vue类,通过use将vue传递给这个工具或插件,在被使用的类上必须要用一个install方法,只要被vue.use(类)就会自然而然的去调用类中的install方法
利用代码实现简易路由
app.vue
<template>
<div id="app">
<div class="nav-box">
<div class="logo">zxt</div>
<div class="nav-list">
<router-link tag="div" to="/">首页</router-link>
<router-link tag="div" to="/learn">课程学习</router-link>
<router-link tag="div" to="/show">学员展示</router-link>
<router-link tag="div" to="/about">关于</router-link>
<router-link tag="div" to="/community">社区</router-link>
</div>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
}
.nav-box{
width: 100%;
height: 60px;
display: flex;
justify-content: space-around;
align-items: center;
background: rgba(0, 0, 200, .5);
}
.logo{
color: #fff;
font-size: 30px;
font-weight: bold;
}
.nav-list{
display: flex;
width: 60%;
justify-content: space-between;
align-items: center;
}
.nav-list a{
text-decoration: none;
color: #000;
font-size: 18px;
}
.nav-list a.router-link-exact-active{
font-weight: bold;
color: #fff;
}
.content{
margin-top: 40px;
font-size: 20px;
}
</style>
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
// 挂载路由
router,
render: h => h(App),
}).$mount('#app')
route.js
import Vue from 'vue'
import VueRouter from './vue-router'
// 桥梁
// 想要别vue使用的工具或插件必须通过use方法
// use方法会调用要被vue使用的工具或插件上的instal方法,同时会将vue传递过去
// 毕竟要被vue使用的工具或插件要用的vue
Vue.use(VueRouter)
Vue.config.productionTip = false
// 定义路由
// 路由表 将其当做router的配置参数
const routes = [
{
path: '/',
// 异步加载组件,当标签被点击才会加载组件
component: () => import('./components/views/Home.vue')
},
{
path: '/learn',
component: () => import('./components/views/Learn.vue')
},
{
path: '/show',
component: () => import('./components/views/Show.vue')
},
{
path: '/about',
component: () => import('./components/views/About.vue')
},
{
path: '/community',
component: () => import('./components/views/community.vue')
},
]
// 创建实例
export default new VueRouter({
mode: 'history',
routes,
})
vue-router 的代码
idnex.js
import History from './history'
import install from './install'
class VueRouter {
constructor(option){
//接受路由表
this.routes = option.routes;
// 将其路由表格式成容易操作的对象
// key : 路径
// value: 子组件
this.routeMap = this.createRouterMap(this.routes) || []
this.history = new History()
this.mode = option.mode || 'hash'
this.init()
}
/**
* 将其路由表格式化为对象
* 格式为: {路径:子组件 ...}
* @param { Array } routes 路由表
* @returns 返回一个对象
*/
createRouterMap(routes){
let routeMap = {}
for (let i = 0; i < routes.length; i++) {
let routesItem = routes[i]
routeMap[routesItem.path] = routesItem.component
}
return routeMap
}
init(){
if(this.mode === 'hash'){
location.hash ? '' : location.hash = '/'
// 第一次页面加载的时候视图是空的
document.addEventListener('DOMContentLoaded', () => {
this.history.current.path = location.hash.slice(1)
})
// 监听hash是否改变
window.addEventListener('hashchange', () => {
this.history.current.path = location.hash.slice(1)
})
}else{
// 第一次页面加载的时候视图是空的
document.addEventListener('DOMContentLoaded', () => {
this.history.current.path = location.pathname
})
// 修复浏览器无法前进与后退
window.addEventListener('popstate',() =>{
this.history.current.path = location.pathname
})
}
}
}
// use 会默认调用函数中的install方法
VueRouter.install = install;
export default VueRouter
history.js
/**
*
* $route中存放当前路由信息来自$.router.history下的current
* 同时$router上也存放着
*/
class History {
constructor() {
this.current = {
path:null,
}
}
}
export default History
install.js
import link from './link';
import view from './view'
export default function install(Vue) {
Vue.mixin({
beforeCreate(){
// console.log(this.$options.router)
/**
* 找到根实例上的router
*/
if(this.$options.router){
this._router = this.$options.router
// this._route = this._router.history.current;
// 一旦属性值改变啦页面就会进行重新渲染
Vue.util.defineReactive(this,'_route',this._router.history.current)
}
}
})
/**
* Object.defineProperty(Vue.prototype, '$router', {}
* 中的this指向 由于有属性描述get会将this传入,那吗他的this 就指向Vue的实例
*/
Object.defineProperty(Vue.prototype, '$router', {
get() {
return this.$root._router;
}
})
Object.defineProperty(Vue.prototype,'$route',{
get(){
return this.$root._route;
}
})
// 会显示找不到router-link 与 router-view 两个组件
Vue.component('router-link', link )
Vue.component('router-view', view )
}
link.js
/**
* 等效于
* Vue.component('router-link',{})
*/
export default {
// 接受元素传递过来的hash路径,以及元素名字
props: {
to:{
type: String,
required: true,
},
tag:{
type:String,
default:'a'
}
},
methods:{
handleClick(){
const mode = this.mode;
if(mode === 'hash'){
location.hash = this.to;
}else{
history.pushState(null,null,this.to)
this.$router.history.current.path = this.to;
}
}
},
// 渲染
render(h) {
const to = this.to;
const tag = this.tag;
const data = {}
const mode = this.mode;
// 当为其a元素时 使用的hash来修改的路由,
// 当不是a元素时,通过history模式(就是当触发单击事件时,将其路由修改)
if(tag === 'a' && mode === 'hash'){
const href = '#' + to
data.attrs = { href }
}else{
data.on = { click: this.handleClick }
}
// 渲染元素, 特性, 以及需要显示的信息
return h(this.tag, data, this.$slots.default)
}
}
view.js
/**
* 函数是组件
* 通过父级找到存放路由信息的对象
* 通过路径找到需要渲染的组件
* 将其渲染
*/
export default {
functional: true,
render(h, {parent}) {
let routeMap = parent.$router.routeMap;
let path = parent.$route.path
return h(routeMap[path])
}
}
思维导图
做的不怎嘛滴
vue-router.xmind
vue-router.xmind