2020-04-15:
笔记:React-router、react-router-dom、connected-react-router 学习
https://react-guide.github.io/react-router-cn/docs/guides/basics/RouteMatching.html
https://reacttraining.com/react-router/web/api/Switch
本篇是基于一个疑问开始的:
a标签和connected-react-router中push方法都分别通过什么方式改变路由?
从结果直接说明:
1、如果使用了connected-react-router把react-router绑定到redux上,a标签才会跟redux出现关系,同时也才能使用上push;
2、a标签通过主动的方式进行跳转,react-router监听到这个行为后一方面进行路由跳转一方面通过connected-react-router发起一个action更新redux state
3、push是从redux出发,抛出一个action,再进行跳转。
一、路由配置:
更应该使用绝对路径的理由:
1、在多层嵌套路由中使用绝对路径的能力让我们对 URL 拥有绝对的掌控。我们无需在 URL 中添加更多的层级,从而可以使用更简洁的 URL。
2、如果一个路由使用了相对路径
,那么完整的路径将由它的所有祖先节点的路径
和自身指定的相对路径
拼接而成。使用绝对路径
可以使路由匹配行为忽略嵌套关系。
渲染四个URL:
import React from 'react' import { Router, Route, Link } from 'react-router' const App = React.createClass({ render() { return ( <div> <h1>App</h1> <ul> <li><Link to="/about">About</Link></li> // 用Link组件替代a标签 <li><Link to="/inbox">Inbox</Link></li> </ul> {this.props.children} </div> ) } }) const About = React.createClass({ render() { return <h3>About</h3> } }) const Inbox = React.createClass({ render() { return ( <div> <h2>Inbox</h2> {this.props.children || "Welcome to your Inbox"} </div> ) } }) const Message = React.createClass({ render() { return <h3>Message {this.props.params.id}</h3> } }) React.render(( <Router> <Route path="/" component={App}> <Route path="about" component={About} /> <Route path="inbox" component={Inbox}> <Route path="/messages/:id" component={Message} /> {/* 跳转 /inbox/messages/:id 到 /messages/:id */} <Redirect from="messages/:id" to="/messages/:id" /> // 利用重定向兼容新旧url </Route> </Route> </Router> ), document.body)
二、进入和离开的Hook:
Route 可以定义 onEnter
和 onLeave
两个 hook ,这些hook会在页面跳转确认时触发一次。这些 hook 对于一些情况非常的有用,例如权限验证或者在路由跳转前将一些数据持久化保存起来。
三、路由匹配原理:
路由拥有三个属性来决定是否“匹配“一个 URL:
- 嵌套关系:当一个给定的 URL 被调用时,整个集合中(命中的部分)都会被渲染。嵌套路由被描述成一种树形结构。React Router 会深度优先遍历整个路由配置来寻找一个与给定的 URL 相匹配的路由。
路径语法:路由路径是匹配一个(或一部分)URL 的 一个字符串模式。
-
几个特殊的符号:
:paramName
– 匹配一段位于/
、?
或#
之后的 URL。 命中的部分将被作为一个参数()
– 在它内部的内容被认为是可选的*
– 匹配任意字符(非贪婪的)直到命中下一个字符或者整个 URL 的末尾,并创建一个splat
参数、<Route path="/hello/:name"> // 匹配 /hello/michael 和 /hello/ryan <Route path="/hello(/:name)"> // 匹配 /hello, /hello/michael 和 /hello/ryan <Route path="/files/*.*"> // 匹配 /files/hello.jpg 和 /files/path/to/hello.jpg
-
- 优先级:路由算法会根据定义的顺序自顶向下匹配路由。因此,当你拥有两个兄弟路由节点配置时,你必须确认前一个路由不会匹配后一个路由中的
路径
。例如,千万不要这么做:
// not do this <Route path="/comments" ... /> <Redirect from="/comments" ... />
四、History:
React Router 是建立在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location
对象, 然后 router 使用它匹配到路由,最后正确地渲染对应的组件。
history.js: 一个无刷新就可改变浏览器栏地址的插件:
提供了window.history一系列api,目的是帮助构建单页面无刷新网站。地址修改了,而页面只有局部dom被修改,这样能省去每次都重新渲染降低性能损耗,而且能看到动态效果。
react-router中history的常见形式:
1、browserHistory(更好兼容浏览器): React Router 的应用推荐的 history。它使用浏览器中的 History API 用于处理 URL,创建一个像
example.com/some/path
这样真实的 URL 。
对旧版浏览器的兼容 :如果我们能使用浏览器自带的 window.history
API,那么我们的特性就可以被浏览器所检测到。如果不能,那么任何调用跳转的应用就会导致 全页面刷新。
它不需要被实例化,直接 import { browserHistory } from 'react-router',< Router history={browserHistory}>
2、createHashHistory(方便调试) : 使用 URL 中的 hash(#
)部分去创建形如 example.com/#/some/path
的路由。
五、默认路由与IndexLink:
这是关于默认路由(首页)的配置:
1、假如这个路由Home想使用如 onEnter hook这些路由机制时,由于其没有渲染的位置,而无法使用,此时,需要用到IndexRoute渲染Home:
<Router>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="accounts" component={Accounts}/>
<Route path="statements" component={Statements}/>
</Route>
</Router>
2、如果你在这个 app 中使用 <Link to="/">Home</Link>
, 它会一直处于激活状态,因为所有的 URL 的开头都是 /
。 这确实是个问题,因为我们仅仅希望在 Home
被渲染后,激活并链接到它。
如果需要在 Home
路由被渲染后才激活的指向 /
的链接,请使用 <IndexLink to="/">Home</IndexLink>
(基础部分到此,高级部分可能后补)
React-router-dom:
基于react-router(z)
,加入了在浏览器运行环境下的一些功能,如Switch
1、<Switch>:
<Route> 的机制是只要匹配到的view都全部重新渲染,这样浪费性能,<Switch>做到只渲染匹配到的第一个路由的页面。
let routes = ( <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/:user"> <User /> </Route> <Route> <NoMatch /> </Route> </Switch> );
connected-react-router:
是一个绑定react-router到redux的组件,来实现双向绑定router的数据到redux store中,这么做的好处就是让应用更Redux化,可以在action中实现对路由的操作。
// app.ts import * as React from 'react'; import * as ReactDOM from 'react-dom'; import {Store} from 'redux'; import {Provider} from 'react-redux'; import {Persistor} from 'redux-persist'; import {PersistGate} from 'redux-persist/integration/react'; import {ConnectedRouter} from 'connected-react-router'; import {History} from 'history'; export const render = ( store : Store, history : History, persistor : Persistor, ) => ReactDOM.render(( <Provider store={store}> <ConnectedRouter history={history}> <PersistGate loading={null} persistor={persistor}> <App /> </PersistGate> </ConnectedRouter> </Provider> ), document.getElementById('root')); ===================================== // index.ts import {createHashHistory} from 'history'; import * as moment from 'moment'; import 'moment/locale/zh-cn'; import createStore from '@main/model'; import {render} from './app'; import './index.module.less'; moment.locale('zh_cn'); export const history = createHashHistory(); const {store, persistor} = createStore(history); store.dispatch({type : 'INITIALIZE'}); render(store, history, persistor);