当前位置: 首页 > Web前端 > vue.js

vue=-【轮子系列】写一个minivue-router

时间:2023-03-31 14:18:26 vue.js

minivuerouter实现一个插件需要做的事情:包含VueRouter类&必要的install方法;实现2个全局组件:router-link和router-view;监听url变化,实现hash方式跳转;实现嵌套子路由跳转显示;开始实现插件实现在使用vue-router时,我们使用Vue.use(VueRouter)注册路由,并通过newVueRouter(routes)接收路由配置,构造路由实例router并挂载到根实例option,这样所有的组件都可以通过:this.$routerletVue//保存路由配置选项classVueRouter{//newVueRouter(routes)constructor(options){this.$options=options//todo}}VueRouter.install=function(_Vue){//Vue=_Vue//接受宿主环境的VueVue.mixin({beforeCreate(){//把根组件放在Vue的router实例上挂载到Vue原型,那么所有的组件实例都会有$routerif(this.$options.router){Vue.prototype.$router=this.$options.router}},})//todo}exportdefaultVueRouterimplementsrouter-link.这个轮子使用的是hash模式,所以url中会有一个#。router-link组件的作用是点击跳转,思路可以简化为:About=>{this.$slots.default}:constLink=Vue.extend({props:{to:{type:String,required:true}},render(h){returnh('a',{attrs:{href:'#'+this.to}},[this.$slots.default])}})//注册路由器链接Vue.component('router-link',link)监听url变化,实现router-view响应view通过监听hashchange事件获取变化的URL标识,并在router中创建响应式匹配数组,记录URL变化匹配的路由;deeplymark每个router-view组件,然后根据当前访问深度(depth)从router的匹配数组中取出需要展示的组件,渲染到当前router-view中。思路如下图:代码实现如下:constructor(options){//响应式匹配的店铺按深度路由配置Vue.util.defineReactive(this,'matched',[])//当前this.current字符记录的URL标识符constinitPath=window.location.hash.slice(1)||'/'Vue.util.defineReactive(this,'current',initPath)//监听URL变化window.addEventListener('hashchange',this.onHashChange.bind(this))//页面加载时,也需要匹配当前URL中需要渲染的组件(1)||'/'this.matched=[]this.match()}match(routes){routes=routes||this.$options.routes//递归遍历并记录当前的URLRoutefor(constrouteofroutes){if(route.path==='/'&&this.current==='/'){//Homethis.matched.push(route)return}//about/infoif(route.path!=='/'&&~this.current.indexOf(route.path)){this.matched.push(route)if(route.children){this.match(route.children)}return}}}//1.标记深度constView=Vue.extend({render(h){this.$vnode.data.routerView=true//标记当前组件为router-viewletdepth=0//递归确认当前router-view深入组件树letparent=this.$parentwhile(parent){constvnodeData=parent.$vnode&&parent.$vnode.dataif(vnodeData){if(vnodeData.routerView){//描述是一个router-view++depth}}parent=parent.$parent}letcomponent=nullconst{matched}=this.$routerif(matched[depth]){component=matched[depth].component}console.log('currentdepth:',depth);console.log('当前匹配:',this.$router.matched);returnh(component)}})Vue.component('router-view',View)至此,一个简单的mini-vue-router就实现了,我们可以像使用官方vue一样引入自己的mini-vue-router-项目中使用了router路由器当然,官方库远比这个轮子复杂。这个轮子旨在理解vue-router的核心思想。进一步研究请阅读官方源码【》》传送门】这个轮子的效果如下:如何扩展和思考如何实现路由守卫如何实现路由缓存如何实现历史模式如何实现路由lazyloadingNotes这个wheel的完整代码://1.它是一个带有VueRouter类和install方法的插件//2.在根实例上挂载了一个新的VueRouter(options)实例,所有组件都可以通过它访问router实例this.$router//3.router-linkrouter-view两个全局组件router-link跳转,router-view显示内容//4.监听url变化和监听haschange||popstateevents//5.响应最新的url:创建一个响应式属性current获取对应的组件,当它发生变化时显示//6.子组件深度标记和macth()letVue//保存路由配置选项classVueRouter{//newVueRouter(routes)constructor(options){this.$options=options//todo缓存一个路由映射表//响应式按深度匹配存储路由配置Vue.util.defineReactive(this,'matched',[])//this.current当前URL标识符constinitPath=window.location.hash.slice(1)||'/'Vue.util.defineReactive(this,'current',initPath)//监听URL变化window.addEventListener('hashchange',this.onHashChange.bind(this))//当页面加载时,它也需要匹配当前url中需要渲染的组件window.addEventListener('load',this.onHashChange.bind(this))this.match()}onHashChange(){this.current=window.location.hash.slice(1)||'/'this.matched=[]this.match()}match(routes){routes=routes||this.$options.routes//递归遍历当前url下所有命中的路由for(constrouteofroutes){if(route.path==='/'&&this.current==='/'){//首页this.matched.push(route)return}//about/infoif(route.path!=='/'&&~this.current.indexOf(route.path)){this.matched.push(route)if(route.children){this.match(route.children)}return}}}}VueRouter.install=function(_Vue){//Vue=_Vue//接受宿主环境的VueVue.mixin({beforeCreate(){//将根组件上的router实例挂载到Vue实例原型上,那么所有的组件实例都会有$routerif(this.$options.router){Vue.原型。$路由器=这个。$选项。路由器}},})constLink=Vue.extend({props:{to:{type:String,required:true}},render(h){returnh('a',{attrs:{href:'#'+this.to}},[this.$slots.default])}})//1.标记深度constView=Vue.extend({render(h){this.$vnode.data.routerView=true//标记当前组件为router-viewletdepth=0//递归确认组件树中当前router-view的深度letparent=this.$parentwhile(parent){constvnodeData=parent.$vnode&&parent.$vnode.dataif(vnodeData){if(vnodeData.routerView){//描述是一个路由器视图++depth}}parent=parent.$parent}letcomponent=nullconst{matched}=this.$routerif(matched[depth]){component=matched[depth].component}console.log('当前深度:',depth);console.log('当前匹配:',this.$router.matched);returnh(component)}})Vue.component('router-link',Link)Vue.component('router-view',View)}导出默认VueRouter