SPA:单页面web应用(single page web application)
- 整个应用只有一个完整的页面
- 点击页面中的链接不会刷新页面,只会做页面的局部刷新
- 数据通过Ajax请求获取,并在前端异步展现
路由
什么是路由
-
一个路由 就是一个映射关系(key:value)
-
key为路径,value可能是function或component
路由的分类
1.后端路由
- value是function,用来处理客户端提交的请求
- 注册路由:router.get(path,function(req,res))
- 工作过程:当node接到一个请求时,根据路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
2.前端路由
- 浏览器端路由,value是component,用于展示页面内容
- 注册路由:<Router path='/test' component={Test}>
- 工作过程:当浏览器的path变为/test时,当前路由组件就变为Test组件
实例
App.jsx
<div className="list-group">
{/* 原生html中,靠<a>跳转不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</div>
因为注册路由需要使用<BrowserRouter></BrowserRouter>包裹,可以直接包裹住App组件
import {BrowserRouter} from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
)
NavLink和Link的区别
路由链接也可以使用NavLink
<NavLink>是<Link>的一个特定版本,会在匹配上当前的url的时候给已经渲染的元素添加参数,组件的属性有
- activeClassName(string):设置选中样式,默认值为active
- activeStyle(object):当元素被选中时,为此元素添加样式
- exact(bool):为true时,只有当导致和完全匹配class和style才会应用
- strict(bool):为true时,在确定为位置是否与当前URL匹配时,将考虑位置pathname后的斜线
- isActive(func)判断链接是否激活的额外逻辑的功能
<div className="list-group">
{/* 原生html中,靠<a>跳转不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<NavLink activeClassName="cumt" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="cumt" className="list-group-item" to="/home">Home</NavLink>
</div>
封装NavLink
因为NavLink中的属性太多了,所以封装为一个组件,每次调用组件名称就好了(懒是第一生产力)
export default class MyNavLink extends Component {
render() {
// console.log(this.props);
return (
<NavLink activeClassName="cumt" className="list-group-item" {...this.props}/>
)
}
}
之后再写路由链接写这个就好了,不过注意Home也就是标签体,其实是标签中的一个属性children,所以再MyNavLink组件中不需要传过去,只需要展开props就好了
<MyNavLink to="/home">Home</MyNavLink>
Switch单一匹配
在注册路由那里包裹一个switch实现单一匹配,当匹配到一个时就不再向下进行匹配了
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Route path="/home" component={Test}/>
</Switch>
Redirect
一般写在所有路由的最下方,当上边的路由都不匹配时就使用这个路由
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
//当以上都不匹配时,就显示这个路由
<Redirect to="/about"/>
</Switch>
二级路由
注意,二级路由需要写上父级路由的路径
<div>
<ul className="nav nav-tabs">
<li>
<MyNavLink to="/home/news">News</MyNavLink>
</li>
<li>
<MyNavLink to="/home/message">Message</MyNavLink>
</li>
</ul>
{/* 注册路由 */}
<Switch>
<Route path="/home/news" component={News}/>
<Route path="/home/message" component={Message}/>
<Redirect to="/home/news"/>
</Switch>
</div>
向下一级路由传递参数的三种方式
向路由组件传递参数
1.params参数
路由连接:携带参数<Link to={`/demo/${name}/${age}`} ></Link>
注册路由声明接收:<Route path='/demo/:name/:age' component={test}/>
接收使用参数:this.props.match.params
2.search参数
路由链接:<Link to={`/demo/${name}/?id=id&name=name`} ></Link>
注册路由无需注册
接收参数:const {search}=this.props.location
//我们接收到的是urlencoded编码格式的字符串,需要借助querystring库转换为对象
const result=qs.parse(search.slice(1)) //把问号去掉
3.state参数
路由链接:<Link to={{pathname='/demo',state={id:id,name:name}}} ></Link>
注册路由无需声明
//刷新也是可以保留住参数
接收参数:this.props.location.state
1.param参数
路由连接:携带参数<Link to={`/demo/${name}/${age}`} ></Link>
注册路由声明接收:<Route path='/demo/:name/:age' component={test}/>
接收使用参数:this.props.match.params
render() {
const {messageNews}=this.state
return (
<div>
<ul>
{
messageNews.map(messageObj=>{
return (
<li key={messageObj.id}>
{/* 向组件传递params参数 */}
<Link to={`/home/home-message/detail/${messageObj.id}/${messageObj.title}`}>{messageObj.title}</Link>
</li>
)
})
}
</ul>
{/* 声明接收params参数 */}
<Route path="/home/home-message/detail/:id/:title" component={Deatils}/>
</div>
)
}
render() {
const {details}=this.state
// 接收params参数
const {id,title}=this.props.match.params
const findResult=details.find(findObj=>{
return findObj.id===id
})
return (
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>favorite:{findResult.content}</li>
</ul>
)
}
2.search参数
路由连接:携带参数<Link to={`/demo/?name=${name}&age=${age}`} ></Link>
注册路由声明接收:<Route path='/demo' component={test}/>
接收使用参数:this.props.match.location
接收方法如下
render() {
const {details}=this.state
// 接收params参数
// const {id,title}=this.props.match.params
//接收search参数
const {search}=this.props.location
// const {id,title}=qs.parse(search) 这种方法不行,对象前会有一个?
// 一个对象想转换为urlcoded编码可以使用 qs.stringify(obj)
//一个urlencoded编码的字符串想转换为对象为qs.parse()
const {id,title}=qs.parse(search.slice(1))
const findResult=details.find(findObj=>{
return findObj.id===id
})
return (
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>favorite:{findResult.content}</li>
</ul>
)
}
3.state参数
路由链接: <Link to={{pathname:'/home/home-message/detail',state:{id:messageObj.id,title:messageObj.title}}}>{messageObj.title}</Link>
注册路由:
{/* state参数 无需声明接收正常写路由即可*/}
<Route path="/home/home-message/detail" component={Deatils}/>
接收参数:const {id,title}=this.props.location.state||{}
路由跳转push与replace
push是放入,可以回退到之前的页面,而replace是替换,不可以回退,默认是push
replace可以实现无痕浏览,只需在路由链接中加入replace属性就好
<Link replace to={{pathname:'/home/home-message/detail',state:{id:messageObj.id,title:messageObj.title}}}>{messageObj.title}</Link>
编程式路由导航
以replace方式访问:
replace跳转+携带params参数
this.props.history.replace(`/home/message/detail/${id}/${title}`)
replace跳转+携带search参数
this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
replace跳转+携带state参数
this.props.history.replace(`/home/message/detail`,{id,title})
以push方式访问:
push跳转+携带params参数
this.props.history.push(`/home/message/detail/${id}/${title}`)
push跳转+携带search参数
this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
push跳转+携带state参数
this.props.history.push(`/home/message/detail`,{id,title})
this.props.history.goBack()
this.props.history.goForward()
this.props.history.go(-2)
BrowserRouter与HashRouter的区别
1.底层原理不一样:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
HashRouter使用的是URL的哈希值。
2.path表现形式不一样
BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
(1).BrowserRouter没有任何影响,因为state保存在history对象中。
(2).HashRouter刷新后会导致路由state参数的丢失!!!
4.备注:HashRouter可以用于解决一些路径错误相关的问题。