简介: React Router是一个基于 React 之上的强大路由库,它可让你向应用中快速地添加视图和数据流,同时保持页面与URL间的同步。html
router共包含如下几种:
react-router: React Router 核心
react-router-dom: 用于 DOM 绑定的 React Router
react-router-native: 用于React Native 的 React Router
react-router-redux: React Router 和 Redux的集成
react-router-config: 静态路由配置的小助手
说明:
今天咱们主要说的是 react-router-dom
react-router-dom包含 HisitoryRouter BrowserRouter等
注意:使用react-router-dom时,只须要引入react-router-dom就能够了,不须要引入react-router
<BrowserRouter>
使用
import { BrowserRouter, NavLink, Switch, Redirect} from 'react-router-dom'
<BrowserRouter basename={`${path}`}>
<ul id="menu">
<NavLink activeClassName={styles.active} exact to={`/`} />
<NavLink activeClassName={styles.active} to={`/user`} />
</ul>
<div id='page-container' className={styles.container}>
<Switch>
<Route path={`/`} exact component={main} />
<Route path={`/user`} render={(props) => <User pageId="user" />} />
<Redirect to="/" />
</Switch>
</div>
</BrowserRouter>
// NavLink添加activeClassName属性以后可让首页tab默认选中
复制代码
<Switch>
<Route exact path="/app/" render={() =>
<Redirect to='/app/error'></Redirect>}></Route>
<Route path="/app/users" component={Users}></Route>
<Route path="/app/report" component={Report}></Route>
<Route path="/app/error" component={ErrorPage}></Route>
<Redirect to="/app/" />
</Switch>
说明:
一、地址栏输入 /app/user ,跳转到指定的Users组件
二、exact指定输入 /app/重定向redirect to /app/error,重定向到ErrorPage组件中
3. 若是输入错误的地址,重定向到/app/路由,跳转到ErrorPage组件中
注意:重定向分两种 1.指定重定向,2.模糊重定向
复制代码
<Switch>
只渲染出第一个与当前地址匹配的<Route>
或<Redirect>
node
Switch解决的问题:若是你访问/about
,那么与/about
相关的路径都将被渲染出来,如/about/user
,由于他们对应的路由与访问的地址/about
匹配。这显然不是咱们想要的,咱们只想渲染出第一个匹配的路由就能够了,因而<Switch>
应用而生!react
Historyredux
pushState()浏览器
popstate性能优化
路由实现 - history
<header>
<a onclick="changeRoute(this)" data-path="home">首页</a>
<a onclick="changeRoute(this)" data-path="center">我的中心</a>
<a onclick="changeRoute(this)" data-path="help">帮助</a>
</header>
<section id="content"></section>
<script>
/**
Hisotory.pushState(state, title, url) 按指定的名称和URL(若是提供该参数)将数据push进回话历史栈。
state: 一个与添加的记录相关联的状态对象,主要用于popstate事件,该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化之后保留在本地,从新载入这个页面的时候,能够拿到这个对象。若是不须要这个对象,此处能够填为null
title: 新页面的标题。可是,如今全部浏览器都忽视这个参数,因此这里能够填空字符串。
url: 新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个地址。
*/
function changeRoute (route) {
let path = route.dataset.path
changePage(path)
history.pushState({content: path}, null, path)
}
/**
当活动历史记录条目更改时,将触发popstate事件。history.pushState()的调用建立的,或者受到对 history.replaceState()的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。
history.pushState() 或者 history.replaceState()不会触发popstate事件。只有在作出浏览器动做时,才会触发该事件,如用户点击浏览器的回退按钮 或者在js代码中调用history.back()
*/
window.addEventListener('popstate', (e) => {
let content = e.state && e.state.content
changePage(content)
})
function changePage (pageContent) {
let content = document.getElementById('content')
content.innerText = pageContent
}
</script>
复制代码
BrowerRouterbash
<BrowerRouter>
使用HTML5的history
API (pushState
、replacestate
popstate
事件)来同步URL和UI服务器
<BrowerRouter basename={optionalString} forceRefresh={optionlBool} getUserConfirmation={optionlFunc} keyLength={optionlNumber}>
<App/>
</BrowerRouter>
复制代码
属性介绍babel
basename: string
:基准URL.适用于当应用置于服务器上子目录的状况下。数据结构
forceRefresh: bool
: 当设为true
时,每次跳转都会刷新页面。通常只在浏览器不支持HTML history API
的状况下使用。
getUserConfirmation: func
: 能够传入一个函数,用来肯定导航前的用户确认行为,默认使用window.confirm
,以下:
keyLength: number
: 设置location.key
的长度,默认为6位
children: node
:子元素里只能渲染单一的元素
HashRouter
使用URL中的hash部分(即window.location.hash
)来保持UI和URL同步
注意:HashHistory不支持location.key
或location.state
,因此任何须要这两个属性的代码或者插件将不能起做用。因为HashHisitory
这种技术的主要目的是支持老式浏览器,因此官方更推荐的作法是对服务器进行配置,而后采用<BrowerRouter>
代替。
MemoryRouter
在内存中记录history的路由组件(这种路由不会读取或者写入地址栏),适合用于测试或者非浏览器环境(如ReactNative
)
导航跳转
为应用提供声明式、可访问的导航的一个组件:
import { Link } from 'react-router-dom'
<Link to="/about">About</Link>
复制代码
参数: to: string 要跳转到的 pathname 或者 location
to: object
用法:
<Link to={{
pathname: '/course',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true }
}} />
复制代码
replace: bool 当这个值为 true 时,会替换掉history栈里当前的history,而不是在栈里新增一条history,如:
<Link to="/courses" replace>
复制代码
<Link>
组件的特殊版本,当前URL匹配时,会在元素上增长样式相关的属性activeClassName
,从而达到高亮标记的效果。
import { NavLink } from 'react-router-dom'
<NavLink to="/about">About</NavLink>
复制代码
参数:
activeClassName: string
规定处于激活状态时的类名,默认值是active
,会自动和className属性合并:
<NavLink to="/faq" activeClassName="selected">FAQs</NavLink>
复制代码
activeStyle: object
规定激活状态下应用到元素上的样式:
<NavLink to="/faq" activeStyle={{
fontWeight: 'bold',
color: 'red'
}}>FAQs</NavLink>
复制代码
exact: bool
设为true
时,只有当location彻底匹配的时候才会添加类名或者样式: 与Switch的区别
<NavLink exact to="/profile">Profile</NavLink>
复制代码
strict: bool
:当设为 true
时,location中pathname
末尾的/
就会在匹配URL的时候被考虑: 就是说有/
和没有/
的结果彻底不一样
<NavLink strict to="/events/">Events</NavLink>
复制代码
isActive: func
:用来为link添加判断激活状态额外逻辑的函数,如:
isActive能够经过逻辑来控制
// 只有当事件ID为奇数时才可能为激活状态
const oddEvent = (match, location) => {
if (!match) {
return false
}
const eventID = parseInt(match.params.eventID)
return !isNaN(eventID) && eventID % 2 === 1
}
<NavLink to="/events/263" isActive={oddEvent}>Event 263</NavLink>
复制代码
location: object
isActive
函数里用来比较用的值(即传入的第二个参数),一般状况下取值为当前浏览器的URL.若是要和不一样的location比较,就能够用这个属性来传值。
<NavLink activeClassName={styles.active} exact to={`/`} />
复制代码
二. Router、Route与Switch 一、Router 通用的基础路由组件。一般在应用里会采用派生的路由代替,有:
<BrowserRouter>
<HashRouter>
<MemoryRouter>
<NativeRouter>
<StaticRouter>
最经常使用的直接采用<Router>
的场景是用状态管理库(Redux、Mobx)来同步一个自定义的history。不过应当注意的是:这不是说必须结合状态管理库使用ReactRouter,只是在深度集成的时候须要用到:
import { Router } from 'react-router'
import createBrowserHistory from 'history/createBrowserHistory'
const history = createBrowserHistory()
<Router history={history}>
<App />
</Router>
复制代码
参数:
history: object 用来导航用的history对象
二、Route
组件也许是ReactRouter里最须要好好理解和使用的最重要的一个组件。它的基本职责是在location匹配路由的path参数值时渲染特定的UI:
import { BrowserRouter as Router, Route } from 'react-router-dom'
<Router>
<div>
<Route exact path="/" component={Home} />
<Route path="/news" component={NewsFeed} />
</div>
</Router>
复制代码
当location是/时,UI将会渲染为:
<div>
<Home />
<!-- react-empty: 2 -->
</div>
复制代码
而若是当UI是/news时,UI将会渲染为:
<div>
<!-- react-empty: 1 -->
<NewsFeed />
</div>
复制代码
react-empty
注释,只是React的null渲染的实现。可是这种作法是有益的,从技术上而言一个Route应该老是被渲染出来即便结果是渲染null,而只要应用的location和Route的path匹配,那么对应的组件就会获得渲染。
2-一、路由渲染方法
渲染一个路由有三种方式:
<Route component>
<Route render>
<Route children>
每一种方式在不一样的情景下都是有用的,可是在<Route>
里只能同时用一个属性,不过大部分状况下用的是component
注意: 因为优先级上:component > render > children
,因此在<Route>
里最多只能选用一种。
首先须要知道的是,这三种方式都会获得match、location、history
这三个路由属性。
说明:
component
方式,代表location匹配时要渲染的React组件,组件可使用路由属性来进行渲染:
<Route path="/user/:username" component={User} />
const User = ({ match }) => {
return <h1>Hello, {match.params.username}!</h1>
}
复制代码
当使用这种方式时,路由系统会使用React.createElement
来从给定的组件里建立一个新的React
元素。这意味着若是传给component
属性的是一个内联的函数,那么每次渲染时就都会 建立一个新的组件。这就会致使现有的组件卸载,而后挂载新的组件。而非更新已有的组件。因此要使用内联函数来进行内联渲染时,可使用render方式或者children方式
render: func
方式,这种方式将方便于内联渲染,而且不会有上述的从新挂载问题。咱们能够传入一个函数,那么当location匹配的时候这个函数就会被调用,从而不会进行新的React元素建立过程。render属性接收和component属性一致的路由属性:
<Route path="/home" render={() => <div>Home</div>} />
const FadingRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<FadeIn>
<Component {...props} />
</FadeIn>
)} />
)
<FadingRoute path="/cool" component={Something} />
复制代码
children: func
方式,有时候,不管path
是否成功匹配都须要进行渲染。那么这种状况下,可使用children
属性方式,这种方式不管匹配是否成功都会调用对应的函数。 而children
渲染属性接收和component、render
方式同样的属性,不过当路由不匹配的时候,match
的值是null
。因此采用这种方式的好处是,咱们能够灵活地动态调整UI,不管路由是否匹配。举例如:
路由匹配时添加激活状态的类名:
<ul>
<ListItemLink to="/somewhere" />
<ListItemLink to="/somewhere-else" />
</ul>
const ListItemLink = ({to, ...rest}) => (
<Route path={to} children={({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest} />
</li>
)}/>
)
复制代码
对于动画而言,这种方式也是有用的:
<Route children={({ match, ...rest }) => (
// 由于动画会一直渲染,因此可使用生命周期来让子元素动画进出
<Animate>
{match && <Something {...rest}/>}
</Animate>
)}
复制代码
三、Switch
渲染子元素里第一个匹配的<Route>
或者<Redirect>
,那么,和只使用一堆<Route>
不一样的是什么? <Switch>
独一无二的是它只渲染一个路由。而反过来,每一个匹配location的<Route>
都会被获得渲染,参考以下代码:
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
复制代码
若是URL是/about,那么<About>
、<User>
和<NoMatch>
都会渲染,由于它们的path都获得了匹配。而这种设计是有意的,由于这使得咱们能够用多种方式来用<Route>
组成咱们的应用,如侧边栏、面包屑、引导Tab等。
不过有时候,咱们只想要拾取一个<Route>
来渲染,就好比URL处于/about
时就不但愿它匹配/:user
(或者展现404页面),如:
import { Switch, Route } from 'react-router'
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
</Switch>
复制代码
如今,若是URL是/about
,那么<Switch>
就会开始寻找匹配的<Route>
。<Route path="/about"/>
将会匹配,从而<Switch>
就会中止匹配而后渲染出<About>
组件。类似地,若是URL处于/michael
,那么<User>
组件就会渲染。
对于动画过渡而言,这也是一种有用的方式,由于匹配的<Route>
须要渲染在先前相同的位置:
<Fade>
<Switch>
{/* 这里只会有一个子节点 */}
<Route/>
<Route/>
</Switch>
</Fade>
<Fade>
<Route/>
<Route/>
{/*
这里一直都会有两个子节点,尽管其中一个可能为null,
这会使得过渡效果起效果有点难处理
*/}
</Fade>
复制代码
参数说明:
location: object
和以前其余地方里含义一致 children: node
<Switch>
组件的子元素都应该是<Router>
或者<Redirect>
,而且只有匹配当前URL的第一个子元素会被渲染。其中,<Route>
元素使用path
属性来进行匹配,而<Redirect>
使用from
属性进行匹配。而不带path属性的<Route>
元素或者不带from属性的<Redirect>
元素会老是匹配当前location
因此当在<Switch>
里使用<Redirect>
组件时,它可使用和<Route>
同样的匹配属性:path、exact、strict
,而from只是path属性的别名。
当给<Switch>
传入location
属性时,会覆盖当前匹配的子元素中的location
<Switch>
<Route exact path="/" component={Home} />
<Route path="/users" component={Users} />
<Redirect from="/accounts" to="/users" />
<Route component={NoMatch} />
</Switch>
复制代码
异步获取数据
复制代码
复制代码
在接口的设计上应体现出数据与业务的相分离。下降耦合性。在代码的设计上也要尽可能下降重复性代码。在写代码以前要清楚接口的数据结构,设计好代码结构,尽可能减小在写代码时候频繁修改代码。
复制代码
7.API
react中最重要的三个API: createElement 、render、 component
component(react中一切皆组件)中包含setState
virtual Dom
jsx表面看是HTML标签,其实是下面这套内容
React.createElement(
"div",
null,
"hello ",
this.props.name,
" ,I am",
{2 + 2},
"years old"
)
复制代码
是jsx的babel作的编译,jsx表面是html,其实是js
React.createElement
返回的数据
JSX
本质上就是转换为React.createElement
在react内部构建虚拟dom,最终渲染出页面
React.createElement
是用来建立虚拟DOM的
React.createElement
是一个递归调用的过程,也就是说dom中嵌套dom,会嵌套相应个数的React.createElement
学习一个库的最好方法,先开发一个小应用,而后尝试理解源码。
参考连接:ReactRouter4学习笔记
<div>
<A />
<B>
<C />
<D />
</B>
</div>
复制代码
请说出componentWillUpdate
和componentDidUpdate
中A B C D
四个组件的执行顺序。
A B C D
复制代码
A C D B
复制代码
原理:当组件更新时,若是组件的props和state都没发生改变,render方法都不会触发,省去Virtual DOM的生成和对比过程,达到提高性能的目的。React自动帮咱们作了一层浅比较。 PureComponent真正起做用的,只是在一些纯展现组件上,复杂组件使用的话shallowEqual那一关基本就过不了。
shouldComponentUpdate
不可变数据的力量,返回新对象,而不是修改老对象。
大部分状况下,可使用 React.PureComponent 来代替手写 shouldComponentUpdate。
setState
只在合成事件和钩子函数中是"异步"的,在原生事件和setTimeout
中都是同步的。setState
的"异步"并非说内部由异步代码实现,其实自己执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新以前,致使在合成事件和钩子函数中无法立马拿到更新后的值,造成了所谓的“异步”,也能够经过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。setState
的批量更新优化也是创建在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout
中不会批量更新,在“异步”中若是对一个值进行屡次setState
,setState
的批量更新策略会对其进行覆盖,取最后一次的执行,若是是同时setState
多个不一样的值,在更新时会对其进行合并批量更新。redux-thunk为何能让action发送函数 redux-thunk是对dispatch方法作一个升级,以前这个dispatch方法只能接收一个对象,升级以后,就能够接收函数了。根据参数的不一样执行不一样的事情,若是是对象就直接传递给store,若是是函数就先执行函数,在调用dispatch。原理就是对store的dispatch方法作了一个升级。
4、Props.childern