编程式导航
1 .用在可复用的路由视图里面,比如所有的需要跳转到一个文章具体内容的路由,每一次跳转到新路由的时候,其实传的东西都是这个文章的id,在里面再次进行请求,别的东西都是一样的,还有就是在这个情况下返回。
2 .本质就是监听URL的变化,然后匹配路由规则,显示相应的页面,而且无需刷新。
代码实现方面
哈希路由
1 .通过location.hash获取到网址#号后面的网页位置标识符
2 .onhashchange事件来监听哈希的变化然后执行响应的回调函数
3 .不同的#值,表示不同的访问状态,向用户发出某个状态的访问链接,也就是说用所有的哈希值来索引到具体显示的某一个页面
4 .代码
// 对window事件进行包装和兼容
// 两种思路吧,一种就是直接使用next,pre这样的指针来指定,一种就是把所有的操作都放到一个栈里面。
const addEvent=(function(){
if(window.addEventListener){
return function(ele,event,handle,isBuble){
isBuble=isBuble||false
ele.addEventListener(event,handle,isBuble)
}
}else if(window.attachEvent){
return function(ele,event,handle){
ele.attachEvent('on'+event,handle)
}
}else{
return function(ele,event,handle){
return function(ele,event,handle){
ele['on'+event]=handle
}
}
}
})()
class Router{
constructor(){
// this.routers={}
// this.pre=this.pre||null
// this.next=this.next||null
// 不是必须
// 也就是说当定义一个和系统api相关的类的时候,
this.currentUrl=null
this.currentIndex=null
this.routerArr=[]
this.len=0
this.canpush=true
//一个是否记录的开关,现在就是go,和back的所有操作都不会记录,只会记录点击的历史记录,就是因为如果要记录go,和back的操作的话,可能会造成死循环。
window.addEventListener('load',()=>{
this.updateUrl()
})
// 这个是关键。这个类需要检测的东西也可以加在这里么?
//constructor里面竟然也可以加这个东西,真的奇怪,类这个东西还得探索一下其他的功能
window.addEventListener('hashchange',()=>{
if(this.canpush){
this.updateUrl()
}else{
console.log('')
}
})
}
// 初始化route,以及更新路由
updateUrl(){
this.currentUrl=window.location.hash.slice(1)||'/'
this.routerArr.push(this.currentUrl)
this.len=this.routerArr.length
this.currentIndex=this.routerArr.length-1
this.canpush=true
}
back(){
console.log('back')
console.log(this)
this.canpush=false
if(this.currentIndex==0){
window.location.hash='#'+this.routerArr[this.len-1]
this.currentIndex=this.len-1
}else{
window.location.hash='#'+this.routerArr[this.currentIndex-1]
this.currentIndex--
}
}
go(){
// 还需要计算下没有进行记录的时候做了前进点击
console.log('go')
console.log(this)
this.canpush=false
if(this.currentIndex==this.len-1){
window.location.hash='#'+this.routerArr[0]
this.currentIndex=0
}else{
window.location.hash='#'+this.routerArr[this.currentIndex+1]
this.currentIndex++
}
}
}
5 .但是这个只能做一些前后倒退的简单操作,想要一一对应起来,的需要key-value形式的数据结构
6 .或者是最后渲染出来的页面,真正自己构造路由的时候还是想要上面那种结果,感觉这个适合服务端渲染,每一个或者每一种情况渲染出来的页面对应着唯一的哈希url
7 .升级改造的话,其实数组里面应该是这样的,记录页面的访问次数,访问顺序。
{
hash:'/',
count:1
}
history路由
1 .缺点:pushState,replaceState只会对url进行改变,不会触发页面刷新,只是导致history对象发生变化,不能跨域。这就导致如果直接输入url,但是后端没有处理的话,直接是404,哈希路由可以定位到某个具体的dom
2 .代码
3 .怎么样升级下既能用上哈希的准确性,有可以方便使用的时候传入key-value的一个地址对应组件的操作
4 .点击一个地址的时候,现在我传入的数组里面找我是否定义了这个地址对应的组件,如果没有,可以选择定义新的地址然后加入,也可以弹出报错,把这个加入
5 .还是想做的就是go.back不记录到历史记录里面。
6 .整个思路其实是这样的,router-link之类的标签点击触发的时候,先在router传入的对象里面去寻找,是否有对应的组件来展示对应的路由地址
7 .那其实这个升级版的路由其实有两个功能,一个是存储历史记录,一个是显示对应的组件,上一个路由组件需要存储功能我们交给了history API,但是这个api是不会保存和组件有关的操作,所以我们需要把和组件有关的历史记录人为的添加到这个api里面
8 .代码
window.history的简单方法
1 .history.length:当前窗口访问过网址的数量
2 .history.state:history堆栈最上层的状态值
3 .history.back():前一个网址
4 .history.forward():移动到下一个网址。
5 .history.go(n):以当前网址为基准,进行移动。正数为前,负数为后。0的话相当于刷新当前页面。
6 .window.history.pushState(state,title,url)
7 .最后一个url必须是同一域名的url,不然会认为是有安全隐患
8 .pushState是不会触发刷新页面,只是导致history对象发生变化,地址栏会有反应
9 .history.replaceState()修改当前history的记录,参数和上个函数的一样
10 .特殊事件:popstate,用户点击前进,后退按钮,或者使用js调用History.back(),History.go(),History.forward()方法才会触发,而且这个事件只针对同一个文档,如果浏览历史发生了改变,导致加载了不同的文档,事件也不会触发,事件的回调参数就是以上两个方法传入的第一个参数。
window.onpopstate = function (event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
};
// 或者
window.addEventListener('popstate', function(event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
});
组件源码分析
Vue.use
1 .在Vue路由里面增加覆盖所有情况的候选资源,如果url匹配不到任何资源,返回一个404页面
2 .使用方法:每一个路由映射一个组件
3 .Vue.use(router):将组件插入到Vue中去,use方法会检测组件内是否有install方法,如果有的话,执行install方法
4 .对于路由注册来说,核心就是调用Vue.use(vueRouter),使VueRouter可以调用Vue,然后通过Vue来调用VueRouter的install函数,在该函数中,核心就是给组件混入钩子函数和全局注册两个路由组件
5 .全局混合方法来初始化VueRouter
6 .给当下所有组件注入route对象
install
1 .对插入组件在beforecreate钩子操作,在Vue声明周期阶段就可以调用到组件的相关方法
2 .通过Vue.prototype定义router,route属性,这样所有的组件都可以获取到这两个属性
3 .Vue上注册router-link,router-view组件
生成router实例
1 .根据配置数组生成路由配置记录表
2 .根据不同的模式生成监控路由变化的History对象
3 .创建一个路由匹配对象,根据mode来踩去不同的路由方式
生成Vue实例
1 .把router传入new Vue()中
2 .进入Vue的生命周期,执行第一步router对Vue混入beforeCreate钩子函数,判断实例化的options是否包含routr-为组件的router-view组件渲染提供$route,保证router.init只被调用一次
3 .初始化结束,界面将显示默认首页
4 .当根组件调用beforeCreate钩子函数的时候。初始化路由,因为只有根组件会有router属性
5 .在路由初始化的时候,核心就是i进行路由的跳转,改变URL然后渲染对应的组件。
路由更新方式
1 .router-link绑定的click方法,触发history.push或者history.replace,从而进行路由转换,更新路由。在BeforeCreate有劫持_route方法,当_route发生变化后,router-view自动变化。
2 .地址变化:HashHistory或者HTMLHistory会自动监控hashchange和popState来对路由变化做处理,从而执行上面那一套程序。
Vue插件机制
1 .
组件特殊方法
1 .this.route:返回当前路由
动态路由
1 .不仅仅是具体的歌曲页面可以使用这个功能,某种模式全需要匹配到一个路由,就好像一个带子可以把杂乱的路由全都梳理起来,只不过这个使用之后无法在每个具体的路径下面在添加子路径,所以这个一般才会用在最末端的路径。
2 ./user/:username/post/:post_id 他是可以分段约束的。这样有更大的灵活性。这样上面那个问题应该可以解决了。那这个可以随便用了,只不过还是需要一些约束,可以看下别人路由是如何设计的
3 .可以变化的那些路径参数都是可以在this.$router.params里面访问到,这个如果是汉字的路径参数的话,是可以直接在组件里面显示的,但是如果是英文的话,还需要一个映射表。
4 .这个还是有局限性,虽然路径是可以类似的正则匹配到,但是组件是只能对应一个的。所以还是还是需要拆开。他适用的是一套模板但是对应不同数据的情况,转到对应的界面之前,把请求的资源的标记存到vuex里面,到了这个组件里面开始ajax请求,填充页面内容。
5 .这样又出现个问题,路由参数变化的时候,对应组件势力会被复用,比起销毁在创建,复用更加高效。此时组件的生命周期是不会被调用的,那么mounted里面定义的函数可能不会初始化,所以需要watch监视下路由参数的响应,一旦发生变化,而且符合变化的条件,那就进行初始化。
6 .匹配优先级是按照定义的位置顺序来算的,先到先匹配
编程式导航
1 .this.$router.push('home') 它实现的效果和router-link实现的方式一样。
2 .适用场景:返回操作,只是知道下一步要怎么操作,但是没有具体的地址,或者在页面上不方便表现出来操作的UI,就需要在底层使用编程导航
3 .编程式导航的语法完全类似于window.history语法
4 .
命名路由
1 .在index里面给特殊的路由加上name,这样在跳转的时候就会很方便,比如跳转到首页以及重定向的时候。
2 .一张页面需要显示多个路由窗口的时候。类似iframe的效果
3 .这个时候使用编程式导航的时候传参数的时候非常方便
4 .链接的时候传的是数组,里面有name关键字一般的传的是path
5 .编程式导航的时候也是push这样的数组参数
6 .
命名视图
1 .components:{default:}默认参数
2 .router-view 里面有name属性。
3 .components:里面传入对应的组件数组 name:组件名
重定向,别名
1 .redirect:访问当前路由地址的时候,重定向到另一个路由地址下的视图
2 .alais:给任意一个路由加一个这参数,起个名字,然后直接router-link 的to就可以过来
3 .那别名和命名路由的区别是什么?看理论感觉用法差不多,还是实践下看看差距在哪里吧。
重要api
router-link
1 .无论是HTML history模式还是hash模式,行为表现一致。切换路由模式的时候,无需做任何的改动
2 .在html history模式下,roter-link会令守卫点击事件,让浏览器不在加载页面
3 .在hisitory下使用base选项之后,所有的to属性都不需要写(基路径)了。
4 .to:当路由目标被点击后,会立刻把to的值传到router.push(),这个值可以是一个字符串或者描述目标位置的对象,那这样我们在写组件的时候其实可以是任意一个值,只要他有to这个传入属性,那么就可以点击触发router.push函数进行路由转换
1 .可以使用v-bind:传入动态参数,比如动态渲染一个树形导航
2 .传入对象的时候,可以是path,name,path+query
5 .replace参数:设置replace的话,点击的对象,进行跳转的时候不记录历史记录
6 .append:相对于传入的路径前面添加一个基路径,这个基路径是什么?
7 .tag:‘router-link渲染出什么结果’,默认是a标签
8 .router-link-active:选中的时候自动给标签价加上这个样式,你可以把想定义的样式放在这个里面
9 .event:触发的事件,默认是click
10 .exact:这个看文档看不懂。
11 .exact-active-class:
router-view
1 .因为他也是个组件,所以可以配合transition,keepalive使用,如果两个一起使用,确保内层使用keep-alive
2 .name:router-view设置了名称,则会渲染对应路由配置中conponents中下的对应组件
index.js文件里面
routes
1 .path
2 .components
3 .component
4 .redirect
5 .alias
6 .props
7 .children
8 .meta
9 .beforeEnter
10 .caseSencetive:是否对匹配规则大小敏感
11 .pathToregexOptions:object编译匹配正则的选项
mode
1 .hash:使用URL hash值来做路由,支持所有浏览器
2 .history:依赖HTML History和服务器配置。需要后台支持,如果后台没有配置对应的路由,就会返回404,所以必须增加一个能够覆盖所有情况的候选资源,如果url匹配不到任何静态资源,返回一个默认的页面。这个需要前端和后端来把保证对url资源的共同配置。