目录
四.注册router-link 和router-view 组件
Vue Router 的基本结构和功能
-
路由器实例(Router 实例):Vue Router 提供了一个
VueRouter
类,用于创建路由器实例。路由器实例通常通过new VueRouter()
创建,并通过 Vue 实例的router
选项进行注册。 -
路由器插件(Router 插件):Vue Router 提供了一个
install
方法,使其可以作为 Vue.js 插件使用。通过在 Vue 实例上调用Vue.use(VueRouter)
,可以在应用程序中全局注册路由器。 -
路由表(Route Table):路由表定义了 URL 和组件之间的映射关系。它是一个包含路由配置的 JavaScript 对象或数组,每个路由配置项都定义了一个 URL 匹配规则和对应的组件。
-
路由模式(Router Mode):Vue Router 支持多种路由模式,包括 hash 模式、history 模式和 abstract 模式。这些模式决定了 URL 如何与路由器进行交互。
-
路由导航(Route Navigation):Vue Router 提供了一组导航方法,用于在不同的 URL 之间进行导航。它包括
router.push()
、router.replace()
、router.go()
等方法,以及<router-link>
组件用于声明式的导航。 -
导航守卫(Navigation Guards):Vue Router 提供了一组导航守卫,用于在路由导航过程中执行特定的逻辑。导航守卫包括全局前置守卫、路由独享守卫、组件内的守卫等。
-
动态路由和嵌套路由(Dynamic Routing and Nested Routing):Vue Router 支持动态路由和嵌套路由,允许在 URL 中包含动态参数,并且可以在组件中进行嵌套路由的声明。
-
路由状态管理(Router State Management):Vue Router 允许在路由器实例中定义和管理全局的路由状态,并通过
$route
对象和$router
实例提供了访问和修改路由状态的方法。
源码分析
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
由于在使用Router时 使用了Vue.use 并且Router为一个对象所以 Router里有一个install方法
路由的本质就是地址栏的切换渲染不同的内容
首先我们先新建一个vueRouter文件夹 在这个文件夹下新建一个index.js文件 我们对外暴露一个名字为Router的class 然后在里面写一个install方法
export default class Router{
static install(){
}
}
一. 编写install 方法
install 方法是默认就加载的我们把初始化逻辑在这里写
第一步判断是否注册过插件 用变量installed来判断
第二步 把vue构造函数记录到全局变量
第三步 混入
路由在每个页面都可以使用 原因就是源码中使用了混入mixin 在beforeCreate生命周期中在每个组件实例创建之前,将路由配置添加到了组件实例中
let _Vue = null;
export default class Router {
static install(Vue) {
//判断是否注册过插件
if (Router.install.installed) {
return;
}
Router.install.installed = true;
//把vue构造函数记录到全局变量
_Vue = Vue;
//混入
_Vue.mixin({
beforeCreate() {
//这里的this.$options是在Vue 2中,this.$options对象包含了创建Vue实例时传递的选项。
//当你在Vue实例中使用router选项来配置Vue Router时,它会被保存在this.$options对象中
if (this.$options.router) {
//把router挂在到_Vue原型上
_Vue.prototype.$router = this.$options.router;
}
},
});
}
}
二 .生命变量存储路由信息和当前路由
constructor(options){
//记录options信息
this.options = options
//存储路由信息
this.routeMap = {}
//记录当前的路由 默认是/
this.data = _Vue.observable({
current:'/'
})
}
三 .初始化路由 把路由信息记录在routeMap中
init(){
//初始话路由
this.createRouteMap(this.options.routes)
}
//这个方法里涉及到嵌套路由的遍历
createRouteMap(routes){
//遍历所有的路由规则包括嵌套路由 存储到routeMap中
routes.forEach(route=>{
// 判断当前路由是否有path和component
if (route.path && route.component) {
// 将path和component作为键值对存储到routeMap中
this.routeMap[route.path] = route.component;
}
// 判断当前路由是否有子路由
if (route.children && route.children.length > 0) {
// 递归遍历子路由
this.createRouteMap(route.children);
}
})
}
四.注册router-link 和router-view 组件
router-link 的实质就是一个a标签,我们需要组织默认行为.router-link 跳转的时候是根据to传的参数进行的,参数可以是String或者Object类型 我们进行如下写操作 :
//注册组件
initCompontent(Vue){
Vue.component('router-link',{
props:{
to:[String,Object]
},
render(h){
return h('a',{
attrs:{
href:this.resolveTo
},
on:{
click:this.clickHandler
},
},[this.$slots.default])
},
computed: {
resolveTo() {
// 判断传入的to是否是对象
if (typeof this.to === 'object') {
//这里也可以返回path自行判断
return this.to.name
} else {
// to不是对象,直接使用to作为href
return this.to;
}
},
},
methods:{
clickHandler(e){
//阻止默认事件
e.preventDefault()
if (typeof this.to === 'object') {
for (let key in this.to) {
if (this.to.hasOwnProperty(key)) {
//这里我默认写的是name去匹配 实际逻辑要多一点
history.pushState({}, '', this.to.name + this.to[key] );
this.$router.data.current = this.to.name;
return
}
}
}
//利用history.pushState去改变地址栏的路由 无刷新地向当前history插入一条历使状态
history.pushState({},'',this.to)
//渲染新的组件
this.$router.data.current = this.to
}
}
})
}
这时候在使用router-link 进行跳转时是没有问题的,但是当点击浏览器后退或者前进时,地址栏路径变了,页面却没有渲染出来
原因就是对应的页面没有update,我们还需要监听浏览器的popstate事件
新增方法initEvent 监听popstate事件,然后从新更新页面
initEvent(){
window.addEventListener('popstate',()=>{
//监听popstate事件 window.location.pathname获取路由地址复制给current从新渲染新的页面
this.data.current = window.location.pathname
})
}
在init里调用事件
//初始化
init(){
this.createRouteMap(this.options.routes)
this.initCompontent(_Vue)
this.initEvent()
}
在initCompontent方法里面注册router-view组件
//注册组件
initCompontent(Vue){
Vue.component('router-link',{
props:{
to:[String,Object]
},
render(h){
return h('a',{
attrs:{
href:this.resolveTo
},
on:{
click:this.clickHandler
},
},[this.$slots.default])
},
computed: {
resolveTo() {
// 判断传入的to是否是对象
if (typeof this.to === 'object') {
return this.to.name
} else {
// to不是对象,直接使用to作为href
return this.to;
}
},
},
methods:{
clickHandler(e){
//阻止默认事件
e.preventDefault()
if (typeof this.to === 'object') {
for (let key in this.to) {
if (this.to.hasOwnProperty(key)) {
history.pushState({}, '', this.to.name + this.to[key] );
this.$router.data.current = this.to.name;
return
}
}
}
history.pushState({},'',this.to)
this.$router.data.current = this.to
}
}
})
let self = this
Vue.component('router-view',{
render (h){
/这里只是一个简易版本 当有多个router-view时候会出问题
const compontent = self.routeMap[self.data.current]
return h(compontent)
}
})
}