目录
Vue Router 的基本结构和功能
源码分析
一. 编写install 方法
二 .生命变量存储路由信息和当前路由
三 .初始化路由 把路由信息记录在routeMap中
四.注册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和componentif (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作为hrefreturn 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作为hrefreturn 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 = thisVue.component('router-view',{render (h){/这里只是一个简易版本 当有多个router-view时候会出问题const compontent = self.routeMap[self.data.current]return h(compontent)}})}