前言还是老样子。如果你知道如何使用一个公共库,你必须了解它的原理或如何模拟它。今天来实现vue-router。这篇文章中提到的一些知识我都有,这里就不一步步详细写了。请看我手写的简单Vuex基本框架。Vue中使用插件的方式是Vue.use(plugin),其用法贴在这里:installVue.jsplugin。如果插件是对象,则必须提供安装方法。如果插件是一个函数,它将被用作安装方法。在调用install方法时,会将Vue作为参数传入。该方法的第一个参数是Vue构造函数,第二个参数是一个可选的选项对象。全局mixins使用Vue.mixin(mixin)全局注册一个mixin,影响注册后创建的每个Vue实例。mixin可用于将自定义行为注入组件,这将影响随后创建的每个Vue实例。路由用法如简单//路由数组constroutes=[{path:'/',name:'Page1',component:Page1,},{path:'/page2',name:'Page2',component:Page2,},]constrouter=newVueRouter({mode:'history',//moderoutes,})在mode和routes中传入,我们实现的时候需要在VueRouter的构造函数中接收。使用路由标题时是这样的:
GotoFooGotoBar
所以我们需要用Vue.component(id,[definition])注册一个全局组件。知道了它,我们可以写一个基本的框架letVue=nullclassVueRouter{constructor(options){this.mode=options.mode||'散列'this.routes=options.routes||[]}}Vue路由器。install=function(_Vue){Vue=_VueVue.mixin({beforeCreate(){//根组件if(this.$options&&this.$options.router){this._root=this//保存当前的vue实例goto_rootthis._router=this.$options.router//在_router上挂载路由器实例}elseif(this.$parent&&this.$parent._root){//子组件去继承父组件的实例,让所有组件共享一个路由器实例this._root=this.$parent&&this.$parent._root}},})Vue.component('router-link',{props:{to:{type:[String,Object],required:true,},tag:{type:String,default:'a',//router-link默认渲染为标签},},render(h){lettag=this.tag||'a'返回
{this.$slots.default}},})Vue.component('router-view',{render(h){returnh('h1',{},'显示视图的位置')//暂时设置为h1标签,稍后会更改},})}exportdefaultVueRoutermodevue-路由器有两种模式,默认是hash模式,history模式使用window.history.pushStateAPI添加浏览器历史记录,然后通过监听popState事件加载相应的内容,即监听变化历史记录。popstate事件popstate事件在活动历史条目更改时触发。如果激活的历史条目是通过调用history.pushState()创建的,或者受到调用history.replaceState()的影响,则popstate事件的状态属性包含历史条目状态对象的副本。history.pushState()方法window.history.pushState(state,title,url)该方法用于在历史记录中添加一条记录,接收三个参数,顺序为:state:与添加记录关联的状态对象,主要用于对于popstate事件。当事件触发时,该对象被传递给回调函数。也就是说,浏览器会将该对象序列化并保存在本地,当页面重新加载时,就可以获取到该对象。如果不需要这个对象,这里可以填null。title:新页面的标题。不过现在所有的浏览器都忽略了这个参数,所以这里可以填一个空字符串。url:新的URL,必须与当前页面同域。您浏览器的地址栏将显示此URL。哈希模式使用URL的哈希来模拟完整的URL,通过监听hashchange事件,然后根据hash值(可以通过window.location.hash属性读取)加载相应的内容。继续添加代码,让Vue=nullclassHistoryRoute{constructor(){this.current=null//当前路由}}classVueRouter{constructor(options){this.mode=options.mode||'散列'this.routes=options.routes||[]this.routesMap=this.createMap(this.routes)this.history=newHistoryRoute()//当前路由this.initRoute()//初始化路由函数}createMap(routes){returnroutes.reduce((pre,current)=>{pre[current.path]=current.componentreturnpre},{})}initRoute(){if(this.mode==='hash'){//先判断用户打开如果有是不是没有hash值,跳转到#/location.hash?'':(location.hash='/')window.addEventListener('load',()=>{this.history.current=location.hash.slice(1)})window.addEventListener('hashchange',()=>{this.history.current=location.hash.slice(1)})}else{//历史模式location.pathname?'':(location.pathname='/')window.addEventListener('load',()=>{this.history.current=location.pathname})window.addEventListener('popstate',()=>{this.history.current=location.pathname})}}}VueRouter.install=function(_Vue){Vue=_VueVue.mixin({beforeCreate(){if(this.$options&&this.$options.router){this._root=thisthis._router=this.$options.routerVue.util.defineReactive(this,'_route',this._router.history)//监听历史路径变化}elseif(this.$parent&&this.$parent._root){this._root=this.$parent&&this.$parent._root}//当访问this.$router时返回路由器实例Object.defineProperty(this,'$router',{get(){returnthis._root._router},})页面路由信息Object.defineProperty(this,'$route',{get(){returnthis._root._router.history.current},})},})}exportdefaultVueRouterrouter-link和router-view组件VueRouter.install=function(_Vue){Vue=_VueVue.component('router-link',{props:{to:{type:[String,Object],required:true,},tag:{type:String,default:'a',},},methods:{handleClick(event){//阻止a标签默认跳转event&&event.preventDefault&&event.preventDefault()letmode=this._self._root._router.modeletpath=this.tothis._self._root._router.history.current=pathif(mode==='hash'){window.history.pushState(null,'','#/'+path.slice(1))}else{window.history.pushState(null,'',path.slice(1))}},},render(h){letmode=this._self._root._router.modelettag=this.tag||'a'letto=mode==='hash'?'#'+this.to:this.toconsole.log('render',this.to)return(<标签on-click={this.handleClick}href={to}>{this.$slots.default})//返回h(tag,{attrs:{href:to},on:{click:this.handleClick}},this.$slots.default)},})Vue.component('router-view',{render(h){letcurrent=this._self._root._router.history.current//current已经是动态响应letroutesMap=this._self._root._router.routesMapreturnh(routesMap[current])//动态渲染对应的组件},})}至此,一个简单的vue-router实现完成,附上案例完整代码:mini-vue-routerps:个人技术博文Github仓库,觉得不错欢迎star,给我继续写下去的鼓励~