路由
单页面应用路由实现原理是,切换url,监听url变化,从而渲染不同的页面组件
history#
history.pushState
history.pushState(state,title,path)state:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要可填nulltitle:新页面的标题,但是所有浏览器目前都忽略这个值,可填nullpath:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个地址
history.replaceState
history.replaceState(state,title,path)这个方法会修改当前的history对象记录, history.length 的长度不会改变
popState 事件 监听路由
window.addEventListener('popstate',function(e){ /* 监听改变 */})同一个文档的 history 对象出现变化时,就会触发 popstate 事件 history.pushState 可以使浏览器地址改变,但是无需刷新页面。
⚠️:用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。 popstate 事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮或者调用 history.back()、history.forward()、history.go()方法。
hash#
window.location.hash
通过window.location.hash 属性获取和设置 hash 值
onhashchange 监听路由
window.addEventListener('hashchange',function(e){ /* 监听改变 */})url变化是history.pushState产生的,并不会触发popState方法,所以需要手动setState,触发组件更新
流程分析#
当地址栏改变url,组件的更新渲染都经历了什么?
以history模式为例。当url改变时,首先触发histoy,调用事件监听popstate事件, 触发回调函数handlePopState,触发history下面的setstate方法,产生新的location对象,然后通知Router组件更新location并通过context上下文传递,switch通过传递的更新流,匹配出符合的Route组件渲染,最后有Route组件取出context内容,传递给渲染页面,渲染更新。
当我们调用history.push方法,切换路由,组件的更新渲染又都经历了什么呢?
我们还是拿history模式作为参考,当我们调用history.push方法,首先调用history的push方法,通过history.pushState来改变当前url,接下来触发history下面的setState方法,接下来的步骤就和上面一模一样了,这里就不一一说了。
history
// 返回当前会话浏览过的页面数量window.history.length
// 接收一个整数作为参数,按照当前页面在会话浏览历史记录中的位置进行移动。// 如果参数为0、undefined、null、false 将刷新页面,相当于执行window.location.reload()方法。如果参数大于浏览器浏览的数量,或小于浏览器的数量的话,什么都不会做。window.history.go(?delta):
// 移动到上一页。相当于点击浏览器的后退按钮,等价于window.history.go(-1)window.history.back()
// 移动到下一页,相当于点击浏览器的前进按钮,等价于window.history.go(1)window.history.forward()
// h5// 该参数是只读的,表示与会话浏览历史的当前记录相关联的状态对象window.history.state
// 向历史记录中追加一条记录window.history.pushState(data, title, ?url)
// 替换当前页在历史记录中的信息window.history.replaceState(data, title, ?url)
执行上面两种方法后,url地址会发生改变。但是不会刷新页面。BrowserRouter#
使用 pushState 和 popState 事件构建路由
browserHistory需要 server 端支持- 浏览器从
/到/repos是会向server发送request的。所以server端是要做特殊配置的
提供onpopstate事件来监听历史栈的改变
const historyUpdatePage = () => {}
window.addEventListener('onpopstate', historyUpdatePage)HashRouter#
使用
window.location.hash和hashchange事件构建路由
主要实现原理是:hash 改变时,不会向服务器发出请求,所以不会刷新页面。并且每次hash值发生改变的时候,都会触发hashchange事件。因此我们可以通过监听该事件,来知道hash值发生了哪些变化。
const hashUpdatePage = () => {}
window.addEventListener('hashchange', hashUpdatePage)对比hash pushState
hash只能修改url的片段标识符的部分,并且必须从#号开始。而pushState能修改路径、查询参数和片段标识符。pushState比hash更符合前端路由的访问方式,因为不带#号hash必须和原先的值不同,才能新增会话浏览历史的记录,但是pushState新增相同的值也会增加会话浏览历史的记录
import React from 'react'import { render } from 'react-dom'import { Router, Route, hashHistory } from 'react-router'import App from './modules/App'import About from './modules/About'import Repos from './modules/Repos'
render(( <Router history={hashHistory}> <Route path="/" component={App}/> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Router>), document.getElementById('app'))http://localhost:8080/#/?_k=pigdrh- 从
/#/到/#/repos浏览器并不会去发送一次request,react-router自己根据url去render相应的模块
基本用法
import { Router, Route, hashHistory } from 'react-router';
render(( <Router history={hashHistory}> <Route path="/" component={App}/> </Router>), document.getElementById('app'));路由器Router就是React的一个组件
Router组件本身只是一个容器,真正的路由要通过Route组件定义。
嵌套路由
<Router history={hashHistory}> <Route path="/" component={App}> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Route></Router>用户访问/repos时,会先加载App组件,然后在它的内部再加载Repos组件。
import { BrowserRouter as Router } from "react-router-dom";
为了渲染出路由,我们需要导入 Route 组件
import { BrowserRouter as Router, Route } from "react-router-dom";
<Route path="/" render={() => <h1>Welcome!</h1>} /><Route path="/" component={Home} />Route组件有很多属性,在代码中,我们使用了 path, render属性
path: 页面的路径render: 对应的页面渲染的是什么component: 用来渲染 React 组件
使用Link组件实现页面之间的跳转 Link to
给Home组件添加 exact 只有path的值被完全匹配时才会渲染对应的组件
<Router> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/contact">Contact</Link></li> </ul>
<Route path="/" exact component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /></Router>
使用Switch包裹路由来告诉 React Router 一次仅加载一条路由
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
<Switch> <Route path="/" exact component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /></Switch>;
const name = 'react'
<li><Link to={`/about/${name}`}>About</Link></li>
<Switch> <Route path="/about/:name" component={About} /></Switch>About组件就可以通过props.match.params.name接受到路由传递过来的参数
const About = ({ match: { params: { name }, },}) => ( // props.match.params.name <Fragment> <h1>About {name}</h1> <FakeText /> </Fragment>);JS实现页面跳转
import { Router, Route, hashHistory } from 'react-router'
render(( <Router history={hashHistory}> <Route path="/" component={App}/> <Route path="/repos" component={Repos}/> <Route path="/about" component={About}/> </Router>), document.getElementById('app'))
localhost/#/reposimport React from 'react'import { Link } from 'react-router'
export default React.createClass({ render() { return ( <div> <h1>React Router Tutorial</h1> <ul role="nav"> <li><Link to="/about">About</Link></li> <li><Link to="/repos">Repos</Link></li> </ul> </div> ) }})单页面WEB应用 SPA
整个应用只有一个完整的页面
点击页面中的链接不会刷新页面。只会做页面的局部更新
数据都需要通过AJAX请求获取 并在前端异步展现
一个路由就是一个映射关系 key:value
key为路径。value可能是functon 或者 component
前端路由. history