路由:
映射关系:url:UI(key:value) 即 location(path):Component
路由分类:
后端路由api - router - response;
前端路由url:UI.(核心:前端路由的URL不能触发网络请求。
我们学的当然是前端路由,两种方式:
- 基于hash:例如#a=1。带井号。改变不会触发地址的请求,但可以去加载当前的组件。
- history:api。
原理分析:
url -> 监听url -> 匹配路由规则 -> 显示对应的组件
hash:
http:test1.com -> http:test1.com/#b
输入URL之后触发hashChange事件,在事件中进行url解析,拿到hash后面的锚点/路由地址,根据路由规则{key:value}寻找对应的component组件。成功替换页面(DOM替换)。
history:(H5新API)
popState pushState onpopstate onpushstate replace重定向
无#
本质:栈在维护一个页面的跳转数据。
加栈:http:test1.com -> http:test1.com/b 触发pushState,事件onPopState
弹栈:http:test1.com/b -> http:test1.com 触发popState,事件自定义
所以可以在前进/回退时的定义接口进行解析URL操作了。拿到path后对应路由规则就可以更换组件了。
路由简单实现
hash模式:
- 写好跳转链接
<ul>
<li>
<a href="#/about">about</a>
</li>
<li>
<a href="#/inbox">inbox</a>
</li>
<li>
<a href="#/home">home</a>
</li>
</ul>
- 点击跳转链接时,使用hashchange监听变化
window.addEventListener('hashchange',()=>{
console.log(window.location.hash) //获取到hash值
})
- 将获取到的hash值解析成我们可以匹配的形式,也就是进行字符串截取。
window.addEventListener('hashchange',()=>{
console.log(window.location.hash.substring(1)) //获取到hash值
})
substring()有两个参数,第一个是起始index,第二个为终止Index,当终止index被省略时,默认从起始Index位置一直截取到末尾处。
所以如果是#/home,截取到的为/home。
- 书写路由匹配规则(key-value)
const routes = {
'':Home,
'/about':About,
'/inbox':Inbox,
"/home":Home,
}
将截取到的字符串与键值对进行匹配,获取到对应的组件
- 组件
const About =()=> <div>About Component</div>
const Inbox =()=> <div>Inbox Component</div>
const Home =()=> <div>Home Component</div>
- 将页面中的DOM进行组件替换。
在底部有一个可以切换的组件,每次点击都会根据锚点进行刷新。也就是说将path所对应的组件每次重新赋给它。
let path = this.state.path;
console.log(path,"path");
let Content = routes[path]
<.Content/> 就是可以根据锚点切换的组件。
这里是输出的path:
未完待续,续更。
history模式:
是HTML5规范提供的接口。
先在render中打印一下window.history,检查一下API
可以看到有window.history.pushState方法,replace重定向方法。
popstate事件则是默认存在于componentDidMount中,当浏览器url执行popstate会触发onpopstate事件的
测试onpopstate
<li>
<a href="#/about">about</a>
</li>
componentDidMount(){
window.onpopstate =()=>{
console.log("onpopstate");
}
}
上例,当url改变时会自动触发onpopstate:
但当浏览器执行pushstate时不会执行onpushstate,所以要进行自己重写pushState方法,即添加回调。
测试onpushstate
手写onpushstate:
定义在constructor中一个上来就执行的方法initPushState:
constructor(props){
super(props)
this.initPushState()
}
//重写pushState
initPushState = () =>{
let oldPushState = window.history.pushState
//重写pushState,使其重写为携带3个参数的方法
window.history.pushState = function(state,title,pathname){
// 执行原生的pushState功能
let result = oldPushState.apply(window.history,arguments)
//在window添加回调onpushstate 并且执行回调
if(typeof window.onpushstate === "function"){
window.onpushstate({state,title,pathname,type:"pushstate"})
}
return result;
}
}
查看此时重写后的pushState,已经携带成功带有参数
然后用按钮进行测试:当点击时,跳转到指定argument伪数组中的pathname中。
init=()=>{
window.history.pushState({page:"111"},{title:"111"},'/page1')
}
<button onClick={this.init}>=====</button>
componentDidMount(){
//默认执行onpopstate
window.onpopstate =()=>{
console.log("onpopstate");
}
//默认没有onpushstate
window.onpushstate = ()=>{
console.log("onpushState")
}
}
点击之后,url路径发生了变化,自定义回调onpushstate也发生了变化也执行了。
路径发生改变,那就可以通过window.location.pathname获取到后缀,实现找到对应组件,完成路由
- 写好链接进行跳转
home=()=>{
window.history.pushState({page:"111"},{title:"111"},'/home')
}
inbox=()=>{
window.history.pushState({page:"111"},{title:"111"},'/inbox')
}
about=()=>{
window.history.pushState({page:"111"},{title:"111"},'/about')
}
<ul>
<li>
<button onClick={this.about}>about</button>
</li>
<li>
<button onClick={this.inbox}>inbox</button>
</li>
<li>
<button onClick={this.home}>home</button>
</li>
</ul>
- 点击跳转链接时,监听路由的变化
先将path的值初始至state中:
this.state={
path:window.location.pathname
}
当点击链接时,触发onpushstate监听方法,同时,将setState实时更新path。
window.onpushstate = (e)=>{
console.log("onpushstate");
this.setState({
path:e.pathname
})
}
点击三次按钮,更新三次pathname,触发三次onpushstate。
- 书写路由匹配规则
const About =()=> <div>About Component</div>
const Inbox =()=> <div>Inbox Component</div>
const Home =()=> <div>Home Component</div>
const routes = {
'/':Home,
'/about':About,
'/inbox':Inbox,
'/home':Home
}
- 将获取到的path进行字符串匹配,键值对的key-value形式一一对应
let path = this.state.path
let Content = routes[path]
<Content/> /在底部显示
实现点击按钮切换路由实时压栈。
当点击回退与前进按钮时,url地址栏的路由会发生改变
都会触发onpopstate方法,但是路由的组件并不会切换。
这是就是弹栈。
进阶:实现前进回退也切换路由组件
解决方法:在onpopstate事件中将state中的path也实施修改。
- 获取e.target中location的pathname,使得在触发onpopstate时,将获取的pathname也进行更新。
window.onpopstate=()=>{
console.log(e.target.location.pathname)
}
可以看到回退时的url后缀了
- 将回退/前进时获取到的路由更新到state中,实现组件切换
window.onpopstate=()=>{
console.log(e.target.location.pathname)
this.setState({
path:e.target.location.pathname
})
}
replace重定向,forward维护,back后退,Go指定
replace重定向,会替换栈,当点击replace时会替换上一个进栈的路由。
所以在回退时,回退得到的是,越过上一个的上一个。
replace=()=>{
window.history.replaceState({page:"111"},{title:"111"},'/inbox')
}
<li>
<button onClick={this.replace}>replace</button>
</li>
也就是说,上一个进入的如果是home,那inbox就会取代home成为它。
forward栈维护,当点击forward时会前进。也相当于维护栈。
back后退,顾名思义
forward=()=>{
window.history.forward() //栈维护
}
back=()=>{
window.history.back()
}
go可以实现前进/后退到指定页面:
go=()=>{
window.history.go(1)
window.history.go(-1)
}
<li>
<button onClick={this.go}>go</button>
</li>
栈
未完待续