react-router 指引
2019年04月02日 10:24:08
简单介绍 react-router 的总体组成,大概需要阅读 5 分钟。
react-router 是基于 react 封装的前端路由库,通过它可以实现 SPA 中的路由切换(不刷新页面的情况下进行页面切换)。react-router 是一组声明式导航组件的集合,意味着你可以像普通 UI 组件一样,在 JSX 中使用路由组件,这非常符合 react 的风格。
安装
react-router 项目包含四个 npm 包,按需选择安装。
- react-router:是实现路由的核心包,其余的几个包都依赖此包。服务端渲染时,只需要引入此包即可。
- react-router-dom:react-router 的 react 绑定,适用于浏览器环境。
- react-router-native:react-router 的 react-native 绑定,适用于基于 react-native 的原生 App 环境。
- react-router-config:静态路由配置,从 react-router v4 开始没有了集中路由配置,改为动态路由,react-router-config 在动态路由的基础上提供了静态路由配置。
如果是在浏览器上的使用,只需要安装 react-router-dom 包即可。
npm install react-router-dom
一个简单的例子
下面是一个简单路由切换示例,有两个链接,点击链接切换到相应的内容。
function BasicExample() {
return (
<BrowserRouter>
<div>
<Link to="/home">Home</Link><br/>
<Link to="/about">About</Link><br/>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</div>
</BrowserRouter>
);
}
这个示例麻雀虽小,但五脏俱全,包含了编写 react-router 应用所必备的三个组成部分:
- 使用
<BrowserRouter>
组件包裹根元素,监听路由变化 - 使用
<Route>
组件定义路由,在路由匹配时渲染组件 - 使用
<Link>
组件作为跳转链接,点击时切换路由
无论多复杂的路由应用,归根结底都是这么个套路。
react-router 核心组件
react-router 包含三类组件:
- 路由器组件:如
<Router>
、<HashRouter>
和<BrowserRouter>
,作用是监听路由变化。 - 路由组件:如
<Route>
、<Switch>
、<Redirect>
,作用是匹配路由时渲染组件。 - 导航组件:如
<Link>
、<NavLink>
,作用是渲染出链接,并且点击链接时自动切换路由。
以及一些辅助 API 和组件:<Prompt>
、withRouter
react-router 的使用
参考官网文档和示例。
本来是想针对每个组件的用法写写demo的,但是写了几个之后感觉看我写的示例还不如看官网的,我写的 demo 不一定会定期更新,但是官方的文档会保持更新,并且文档详细示例也丰富。
常见问题
(1) 组件 component/render/children 的区别
下面是<Route>
组件的源码,从中可以观察到他们的区别:
class Route extends React.Component {
render() {
return (
// 省略部分代码...
<RouterContext.Provider value={props}>
{children && !isEmptyChildren(children)
? children
: props.match
? component
? React.createElement(component, props)
: render
? render(props)
: null
: null}
</RouterContext.Provider>
)
}
}
翻译成伪代码如下:
if (存在 children) {
渲染 children ,并且忽略 component 和 render 属性
} else {
if (匹配当前路由) {
if (component 不为空) {
返回 React.createElement(component, props) 创建的组件实例
} else if (render 不为空) {
返回 render(props) 创建的组件
} else {
返回`null`
}
}
(2)使用<BrowserRouter>
时服务端的配置
<BrowserRouter>
和<HashRouter>
都可以实现前端路由的功能,前者路由变化部分是 URL 的 pathname 段,后者路由变化部分是 hash 段。
前者:http://127.0.0.1:3000/article/num1
后者:http://127.0.0.1:3000/#/article/num1
这样的区别带来的直接问题就是当处于二级或多级路由状态时,刷新页面,<BrowserRouter>
会将当前路由发送到服务器,而<HashRouter>
不会。
解决办法:使用<BrowserRouter>
时,服务端配合做点修改,当服务端收到请求的 url 不是功能性的,而是前端路由时,返回入口 html 文件内容。这样修改后,虽然页面刷新会发送到服务器,但是服务器返回的页面内容相同,达到的效果和使用<HashRouter>
相同。
以 node.js 为例,大致代码如下:
app.use(function(req, res, next) {
// 如果是功能性 URL,则交给按正常流程处理
// 如果是前端路由,返回单页应用的入口页内容
fs.readFile(__dirname + '/public/dist/index.html', function(err, data){
if(err){
console.log(err);
res.send('后台错误');
} else {
res.writeHead(200, {
'Content-type': 'text/html',
'Connection':'keep-alive'
});
res.end(data);
}
})
});