16-react-router

216 阅读5分钟

路由:

映射关系:url:UI(key:value) 即 location(path):Component

路由分类:

后端路由api - router - response;

前端路由url:UI.(核心:前端路由的URL不能触发网络请求。

我们学的当然是前端路由,两种方式:

  1. 基于hash:例如#a=1。带井号。改变不会触发地址的请求,但可以去加载当前的组件。
  2. 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后对应路由规则就可以更换组件了。

image.png

路由简单实现

hash模式:

  1. 写好跳转链接
 <ul>
         <li>
          <a href="#/about">about</a>
         </li>
         <li>
          <a href="#/inbox">inbox</a>
         </li>
         <li>
           <a href="#/home">home</a>
          </li>
      </ul>
  1. 点击跳转链接时,使用hashchange监听变化
 window.addEventListener('hashchange',()=>{
     console.log(window.location.hash) //获取到hash值
    })
  1. 将获取到的hash值解析成我们可以匹配的形式,也就是进行字符串截取。
 window.addEventListener('hashchange',()=>{
     console.log(window.location.hash.substring(1)) //获取到hash值
    })

substring()有两个参数,第一个是起始index,第二个为终止Index,当终止index被省略时,默认从起始Index位置一直截取到末尾处。

所以如果是#/home,截取到的为/home。

  1. 书写路由匹配规则(key-value)
const routes = {
  '':Home,
  '/about':About,
  '/inbox':Inbox,
  "/home":Home,
}

将截取到的字符串与键值对进行匹配,获取到对应的组件

  1. 组件
const About =()=> <div>About Component</div>
const Inbox =()=> <div>Inbox Component</div>
const Home =()=> <div>Home Component</div>

  1. 将页面中的DOM进行组件替换。

在底部有一个可以切换的组件,每次点击都会根据锚点进行刷新。也就是说将path所对应的组件每次重新赋给它。

 let path = this.state.path;
    console.log(path,"path");
    let Content = routes[path]

<.Content/> 就是可以根据锚点切换的组件。

这里是输出的path:

image.png


未完待续,续更。

history模式:

是HTML5规范提供的接口。

先在render中打印一下window.history,检查一下API

image.png

可以看到有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:

image.png

但当浏览器执行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,已经携带成功带有参数

image.png

然后用按钮进行测试:当点击时,跳转到指定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")
    }
  }
   

image.png

点击之后,url路径发生了变化,自定义回调onpushstate也发生了变化也执行了。

路径发生改变,那就可以通过window.location.pathname获取到后缀,实现找到对应组件,完成路由

  1. 写好链接进行跳转
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>
  1. 点击跳转链接时,监听路由的变化

先将path的值初始至state中:

  this.state={
      path:window.location.pathname
    }

当点击链接时,触发onpushstate监听方法,同时,将setState实时更新path。

 window.onpushstate = (e)=>{
      console.log("onpushstate");
      this.setState({
        path:e.pathname
      })
    }

image.png

点击三次按钮,更新三次pathname,触发三次onpushstate。

  1. 书写路由匹配规则
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
}
  1. 将获取到的path进行字符串匹配,键值对的key-value形式一一对应
let path = this.state.path
let Content = routes[path]

<Content/>  /在底部显示

实现点击按钮切换路由实时压栈。

image.png

当点击回退与前进按钮时,url地址栏的路由会发生改变

image.png

都会触发onpopstate方法,但是路由的组件并不会切换。

image.png

这是就是弹栈

进阶:实现前进回退也切换路由组件

解决方法:在onpopstate事件中将state中的path也实施修改。

  1. 获取e.target中location的pathname,使得在触发onpopstate时,将获取的pathname也进行更新。
window.onpopstate=()=>{
    console.log(e.target.location.pathname)
}

可以看到回退时的url后缀了

image.png

  1. 将回退/前进时获取到的路由更新到state中,实现组件切换
window.onpopstate=()=>{
    console.log(e.target.location.pathname)
     this.setState({
        path:e.target.location.pathname
      })
}

image.png

image.png

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>


未完待续