路由
单页面应用路由实现原理是,切换url,监听url变化,从而渲染不同的页面组件
history
#
history.pushState
history.pushState(state,title,path)
state
:一个与指定网址相关的状态对象,popstate
事件触发时,该对象会传入回调函数。如果不需要可填null
title
:新页面的标题,但是所有浏览器目前都忽略这个值,可填null
path
:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个地址
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/#/repos
import 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