react-router
使用
1、 安装
npm i react-router-dom
import {
HashRouter,
BrowserRouter,
Route,
Link,
Switch,
} from 'react-router-dom'
用最大的 HashRouter / BrowserRouter 来包裹组件
<BrowserRouter>
<ul>
<li>
<Link to='/list'>显示列表</Link>
</li>
<li>
<Link to='/Clock'>显示时间</Link>
</li>
</ul>
<div>
{/* exact 精准匹配 */}
<Route exact path='/list' component={List}></Route>
<Route path='/ContextTest' component={ContextTest}></Route>
<Route exact path='/Clock' component={Clock}></Route>
<Route path='/reducer' component={Reducer}></Route>
<Route path='/reftest' component={RefTest}></Route>
<Route path='/redux' component={Redux}></Route>
<Route path='/reactredux' component={ReactReduxPage}></Route>
</div>
</BrowserRouter>
当使用switch组件的时候就会从上到下找到合适的组件 叫独占路由
<Switch>
<Route exact path='/list' component={List}></Route>
<Route path='/ContextTest' component={ContextTest}></Route>
<Route exact path='/Clock' component={Clock}></Route>
<Route path='/reducer' component={Reducer}></Route>
</Switch>
如果不写 path 就是匹配任意值
子路由的渲染优先级是 children > component > render
Redirect
要重定向到的位置,其中 pathname 可以是 path-to-regexp 能够理解的任何有效的 URL 路径。
<Redirect
path={'/'}
to={{ pathname: '/login', search: '?utm=your+face', state: { referrer: currentLocation }}}
/>
上例中的 state 对象可以在重定向到的组件中通过 this.props.location.state 进⾏访问。⽽ referrer
键(不是特殊名称)将通过路径名 /login 指向的登录组件中的 this.props.location.state.referrer 进⾏访问。
实现 react-router
暂定 实现一下组件
import { Route, Link, Switch, BrowserRouter } from '../react-router/index'
我们知道 react 路由有 hashRouter、browerRouter、memoryRouter
这几个router的作用是传递不同的 historyApi 给 子组件去使用, 将三者api封装成统一的api
1、关于 BrowserRouter
传递了 history 的 api给下层
export class BrowserRouter extends Component {
constructor(props) {
super(props)
this.history = history.createBrowserHistory()
}
render() {
return (
<Router children={this.props.children} history={this.history}></Router>
)
}
}
2、 关于 Router
这层组件 承接了 不同 路由方式的api的 中间层, 上层传递了history 的统一api 给这层使用
作用:
- 监听路由变化 ,若路由发生改变,则重新渲染当前组件
- 通过 createContext 把 history api 传递给 子组件
- 把 location 也传给子组件
- 传递一个默认path 表示 根路径匹配
import RouterContext from './context'
export class Router extends Component {
// 静态方法 传递一个默认path 表示 根路径
static computeRootMatch(pathname) {
return { path: '/', url: '/', params: {}, isExact: pathname === '/' }
}
constructor(props) {
super(props)
this.state = {
location: props.history.location,
}
// 提供监听
this.unlisten = props.history.listen((obj) => {
this.setState({ location: obj.location })
})
}
componentWillUnmount() {
this.unlisten() // 执行取消监听
}
render() {
const { children, history } = this.props
// 提供history跳转 ,和 location 的参数
return (
<RouterContext.Provider
value={{
history,
location: this.state.location,
match: Router.computeRootMatch(this.state.location.pathname),
}}
>
{children}
</RouterContext.Provider>
)
}
}
我们输出 History 看看
history 包含了以下方法
比如我们常使用 的 go、push、replace、back
action: (...)
back: ƒ ()
block: ƒ (a)
createHref: ƒ g(a)
forward: ƒ ()
go: ƒ r(a)
listen: ƒ (a)
location: Object
push: ƒ w(a, d)
replace: ƒ u(a, d)
get action: ƒ action()
get location: ƒ location()
hash: ""
key: "h7fgir2z"
pathname: "/ContextTest"
search: "?fuck=true"
state: null
3、 实现 Link
其 只提供的 a 标签支持跳转,需屏蔽默认事件
history api 通过 useContext(RouterContext) 获取
// 组件 link
export function Link({ children, to, ...restProps }) {
const { history } = useContext(RouterContext)
const handleClick = (e) => {
// 页面跳转
e.preventDefault()
history.push(to)
}
return (
<a href={''} to={to} onClick={handleClick}>
{children}
</a>
)
}
4、Route
1、 作用是显示 匹配路由的 children
2、 如果有个switch的组件,那computedMatch就会有值
3、 取值优先级 computedMatch > matchPath(location.pathname, this.props) > context.match 分别就是 取 switch组件给的匹配结果 => 自己的匹配结果 => 根目录结果
4、children > component > render 除此之外children如果是个函数那么必渲染(上层有switch组件除外)
5、 通过测试发现 switch 组件会改变 它的children,只会渲染匹配的,没有使用switch组件时,所有 Route 都会渲染,只是渲染结果可能为null。
import matchPath from './matchPath'
export class Route extends Component {
render() {
return (
<RouterContext.Consumer>
{(context) => {
const { location } = context
const { children, component, render, path, computedMatch } = this.props
// 表示匹配的 结果
const match = computedMatch
? computedMatch
: path
? matchPath(location.pathname, this.props)
: context.match
// 这里取值的优先顺序是
// computedMatch > matchPath(location.pathname, this.props) > 默认的 match
// console.log('match:', match)
const props = { ...context, match }
// 匹配 children,component,render null
// 不匹配
return match
? children // 1
? typeof children === 'function' // 2
? children(props)
: children
: component // 3
? React.createElement(component, props)
: render // 4
? render()
: null
: typeof children === 'function' // 1
? children()
: null
}}
</RouterContext.Consumer>
)
}
}
Switch 组件
找出 匹配的组件
export class Switch extends Component {
render() {
return (
<RouterContext.Consumer>
{(context) => {
let match;
let element;
const { location } = context
React.Children.forEach(this.props.children, (child) => {
if (match == null && React.isValidElement(child)) {
element = child
match = matchPath(location.pathname, child.props) // 匹配出来的
}
})
return match
? React.cloneElement(element, { computedMatch: match })
: null
}}
</RouterContext.Consumer>
)
}
}
Redirect 组件
该组件得和 Route 同级,得有 path 和 to 属性 ,path是为了 给 switch 去和 location.pathname,否则无论如何都会渲染
export class Redirect extends Component {
render() {
return (
<RouterContext.Consumer>
{(context) => {
const { history } = context
const { to, push = false } = this.props
return (
<LifeCycle
onMount={() => {
push ? history.push(to) : history.replace(to)
}}
></LifeCycle>
)
}}
</RouterContext.Consumer>
)
}
}
class LifeCycle extends Component {
componentDidMount() {
if (this.props.onMount) {
this.props.onMount.call(this, this)
}
}
render() {
return null
}
}
关于 withRouter
该方法是一个高阶组件的用法
需在Route 组件包一层 修改下match的值, 通过 provider
// 高阶组件
export const withRouter = (WrappedComponent) => (props) => {
return (
<RouterContext.Consumer>
{(context) => {
return <WrappedComponent {...props} {...context} />
}}
</RouterContext.Consumer>
)
}
Prompt 实现
阻碍的原理是 调用 history.block(message)
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!