一、前端路由
1、从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下): ??????①记录当前页面的状态(保存或分享当前页的url,再次打开该url时,网页还是保存(分享)时的状态); ??????②可以使用浏览器的前进后退功能(如点击后退按钮,可以使页面回到使用ajax更新页面之前的状态,url也回到之前的状态)。 2、作为开发者,要实现这两个功能,我们需要做到: ??????改变url且不让浏览器向服务器发出请求; ??????监测 url 的变化; ??????截获 url 地址,并解析出需要的信息来匹配路由规则。 3、前端路由的本质是监听url变化 ,然后匹配路由规则 ,无需刷新就可以显示相应的页面,目前单页面路由主要有以下两种方式。 我们路由常用的hash模式 和history模式 实际上就是实现了上面的功能。
二、hash模式
??????这里的 hash 就是指 url 尾巴后的 # 号以及后面的字符 。这里的 # 和 css 里的 # 是一个意思。hash也称作锚点 ,本身是用来做页面定位的,它可以使对应 id 的元素显示在可视区域内。 ??????由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件,浏览器的进后退也能对其进行控制,所以人们在 html5 的 history 出现前,基本都是使用 hash 来实现前端路由的。 例如使用到的api:
window.location.hash = 'qq'
var hash = window.location.hash
window.addEventListener('hashchange', function(){
})
三、history模式
??????hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。 ??????hash 的传参是基于 url的,如果要传递复杂的数据,会有体积 的限制,而history 模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中,可以监听浏览器的前进、后退事件(back、forward、go)。 ??????主要通过history.pushState/replceState向当前历史记录中插入状态对象state,在浏览器前进、回退、跳转等动作发生时触发popState事件,此时可以通过解析popState事件回调函数的event参数中的state对象,或者解析当前页面url来实现路由。 ??????建议解析url方式实现路由。如果没有在页面首次加载的时候设置pushState/replaceState,那么首页一般是没有state对象的,在执行浏览器动作时,如果回退到首页,那么当前history对象的state属性不存在,导致解析state报错 相关API:
window.history.pushState(state, title, url)
例如: ??????当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, ‘./qq/’),则变成 https://www.baidu.com/a/qq/,执行history.pushState(null, null, ‘/qq/’),则变成 https://www.baidu.com/qq/。
window.history.replaceState(state, title, url)
window.addEventListener("popstate", function() {
});
window.history.back()
window.history.forward()
window.history.go(1)
四、两种路由方式的差异
(1)方式的异同 ①页面手动刷新,hash模式不会向服务器发送请求,history模式会; ②hash模式下url中的哈希值不会发送到服务器,history模式url全部会发送至服务器; ③设置location.hash和pushState都不会导致浏览器刷新; ④设置location.hash的时候会触发hashchange事件和popstate事件; ⑤仅当pushState函数设置url参数的值为hash格式时,浏览器动作发生会触发hashchange事件,尽管location.hash值为空; ⑥a标签锚点跳转可以设置hash,触发hashchange事件。
(2)注意的问题 ??????如果pushState的url为跨域网址,那么会报错,这样设计的目的是防止恶意代码让用户以为他们是在另一个网站上。
五、React的页面路由模块:react-router-dom的使用
1、安装
npm install react-router-dom
2、实现路由模式的组件(决定路由模式)
??????其实就是路由的hash和history两种模式,并且这两个组件是路由的容器,必须在最外层 。 (1)HashRouter组件:实现hash模式的路由
ReactDOM.render(
<HashRouter>
<Route path="/" component={Home}/>
</HashRouter>
)
(2)BrowserRouter组件:实现history模式的路由
ReactDOM.render(
<BrowserRouter>
<Route path="/" component={Home} />
</BrowserRouter>
)
3、Route组件
??????路由的一个原材料,是控制路径对应显示的组件,即为实现路径和显示组件之间的映射。 Route的参数: path: 跳转的路径 component: 对应路径显示的组件 render: 可以自己写render函数返回具体的dom,而不需要去设置component location: 传递route对象,和当前的route对象对比,如果匹配则跳转 exact: 匹配规则,默认值为false,true的时候则表示精确匹配。 注意: 不同版本的react-router-dom,Route的属性也不同 react-router-dom6.0以下的版本:
<Route path="/users" component={组件} render={返回dom} location="route对象" exact="匹配规则"/>
react-router-dom6.0(含6.0)以上的版本:
<Route path="/users" element={组件} render={返回dom} location="route对象" exact="匹配规则"/>
4、Router组件
??????低级路由 ,适用于任何路由组件,主要和redux 深度集成,使用必须配合history 对象,使用Router路由的目的是和状态管理库如redux中的history同步对接。
<Router history={history}>
...
</Router>
5、Link和NavLink组件
??????类似于< a >标签,两者都是跳转路由,NavLink的参数更多些。
(1)Link组件
??????Link组件的api属性: to: 有两种写法,表示跳转到哪个路由。
<Link to="/a" />
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true }
}}/>
replace: 就是将push改成replace。 innerRef: 访问Link标签的dom。
(2)NavLink组件
??????包含了Link组件的所有api,在Link组件的基础上进行了扩展。 ??????NavLink的api属性: ①Link 的所有api ②activeClassName 路由激活的时候设置的类名 ③activeStyle 路由激活设置的样式 ④exact 参考Route,符合这个条件才会激活active类 ⑤strict 参考Route,符合这个条件才会激活active类 ⑥isActive 接收一个回调函数,active状态变化的时候回调触发,返回false则中断跳转
const oddEvent = (match, location) => {
console.log(match,location)
if (!match) {
return false
}
console.log(match.id)
return true
}
<NavLink isActive={oddEvent} to="/a/123">组件一</NavLink>
⑦location 接收一个location对象,当url满足这个对象的条件才会跳转
<NavLink to="/a/123" location={{ key:"mb5wu3", pathname:"/a/123" }}/>
6、Redirect组件
??????页面重定向,属性和Link相同。
<Redirect to="/somewhere/else" />
<Redirect
to={{
pathname: "/login",
search: "?utm=your+face",
state: { referrer: currentLocation }
}}
/>
<Redirect push to="/somewhere/else" />
<Switch>
<Redirect from='/old-path' to='/new-path'/>
<Route path='/new-path' component={Place}/>
</Switch>
注: 页面重定向: 客户端向服务器端发送了两次请求 请求转发: 客户端向服务器发送了一次请求
7、Switch组件
??????进行路由切换 ,只会匹配第一个路由,类似Tab标签。Switch内部只能包含Route、Redirect、Router。
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
8、withRouter组件
??????当一个非路由组件也想访问到当前路由的match,location,history对象,那么withRouter将是一个非常好的选择,可以理解为将一个组件包裹成路由组件。 ??????即包装器,将普通的组件包装成路由组件。包装后普通组件就可以访问路由信息(如:history、location、match)
import { withRouter } from 'react-router-dom'
const MyComponent = (props) => {
const { match, location, history } = this.props
return (
<div>{props.location.pathname}</div>
)
}
const FirstTest = withRouter(MyComponent);
9、404视图
??????当用户访问一些不存在的URL时就该返回404视图了,但不存在的地址该如何匹配呢?----使用Switch 。 ??????Switch组件的作用类似于JS中的switch语句,当一项匹配成功之后,就不再匹配后续内容。这样的话就可以把要匹配的内容写在Switch组件中,最后一项写404视图,当其他都匹配不成功时就是404视图。例如:
<Switch>
<Route exact={true} path={"/"} component={Home}/>
<Route path={"/about"} component={About}/>
<Route path={"/topics"} component={Topics}/>
<Route component={View404}/>
</Switch>
六、react-router-dom实现路由示例
??????首先安装react-router-dom。 Home.js:
import React,{ Component } from 'react';
class Home extends Component{
render(){
return (
<div>
<h2>Home页面</h2>
</div>
)
}
}
export default Home;
About.js:
import React,{Component} from "react";
class About extends Component{
render() {
return (
<div>
<h2>About页面</h2>
</div>
)
}
}
export default About;
Topic.js:
import React,{ Component } from "react";
class Topic extends Component{
render() {
console.log(this.props)
return(
<div>
<h2>{ this.props.match.params.topicId}</h2>
</div>
)
}
}
export default Topic;
Topics:
import React,{ Component } from "react";
import { Route,Link } from "react-router-dom";
import Topic from "./Topic";
class Topics extends Component{
render() {
console.log(this)
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${this.props.match.url}/西安邮电大学`}>
西安邮电大学
</Link>
</li>
<li>
<Link to={`${this.props.match.url}/西安石油大学`}>
西安石油大学
</Link>
</li>
<li>
<Link to={`${this.props.match.url}/西安工业大学`}>
西安工业大学
</Link>
</li>
</ul>
<Route path={`${this.props.match.url}/:topicId`} component={ Topic }/>
<Route path={ this.props.match.url } render={()=>(
<h3>请求选择一个学校</h3>
)}/>
</div>
)
}
}
export default Topics;
App.js:
import logo from './logo.svg';
import './App.css';
import {BrowserRouter, Route, Link} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Topics from "./components/Topics";
function App() {
return (
<BrowserRouter>
<div>
<ul>
<li>
<Link to={"/"}>Home</Link>
</li>
<li>
<Link to={"/about"}>About</Link>
</li>
<li>
<Link to={"/topics"}>Topics</Link>
</li>
</ul>
<hr/>
<Route exact={true} path={'/'} component={ Home }/>
<Route path={'/about'} component={ About }/>
<Route path={ '/topics'} component={Topics }/>
</div>
</BrowserRouter>
);
}
export default App;
|