前两天在写react项目的时候发现在路由上面走了很多的弯路。正所谓磨刀不误砍柴工,还是先系统地学习一下吧...
重要API一览
路由容器组件
HashRouter: 使用hash方式进行路由;
MemoryRouter: 在内存中管理history,地址栏不会变化。在reactNative中使用。
Route标签
该标签有三种渲染方式component、render、children(绝大多数情况使用component组件就好了);
三种渲染方式都会得到三个属性match、history、location;
渲染组件时,route props跟着一起渲染;
children方式渲染会不管地址栏是否匹配都渲染一些内容,在这里加动画一时很常见的做法。
Link标签
NavLink标签
<NavLink>是<Link>的一个特定版本,会在匹配上当前URL的时候会给已经渲染的元素添加样式参数;
activeClassName,当地址匹配时添加相应class;
activeStyle,当地址匹配时添加相应style;
exact,当地址完全匹配时,才生效;
isActive,添加额外逻辑判断是否生效。
Prompt标签
Redirect标签
match
params: 通过解析URL中动态的部分获得的键值对;
isExact: 当为true时,整个URL都需要匹配;
path: 在需要嵌套<Route/>的时候用到;
url: 在需要嵌套<Link/>的时候会用到;
获取方式: 以this.props.match方式。
import { BrowserRouter as Router,// 或者是HashRouter、MemoryRouter Route,// 这是基本的路由块 Link,// 这是a标签 Switch // 这是监听空路由的 Redirect // 这是重定向 Prompt // 防止转换 } from 'react-router-dom'
权限控制
利用组件内的Redirect标签。
const PrivateRoute = ({ component: Component,...rest }) => ( <Route {...rest} render={props => ( fakeAuth.isAuthenticated ? ( <Component {...props}/> ) : ( <Redirect to={{ pathname: '/login',state: { from: props.location } }}/> ) )}/> )
阻止离开当前路由
<Prompt when={isBlocking} message={location => ( `你真的要跳转到 ${location.pathname}么?` )} />
过渡动画
样式分别定义:.example-enter、.example-enter.example-enter-active、.example-leave、.example-leave.example-leave-active。
实例
<ReactCSSTransitionGroup transitionName="fade" transitionEnterTimeout={300} transitionLeaveTimeout={300} > <!-- 这里和使用 ReactCSSTransitionGroup 没有区别,唯一需要注意的是要把你的地址(location)传入「Route」里使它可以在动画切换的时候匹配之前的地址。 --> <Route location={location} key={location.key} path="/:h/:s/:l" component={HSL} /> </ReactCSSTransitionGroup>
按需加载
官方方法
借助bundle-loader
实现按需加载。
新建一个bundle.js文件:
import React,{ Component } from 'react' export default class Bundle extends React.Component { state = { // short for "module" but that's a keyword in js,so "mod" mod: null } componentWillMount() { this.load(this.props) } componentWillReceiveProps(nextProps) { if (nextProps.load !== this.props.load) { this.load(nextProps) } } load(props) { this.setState({ mod: null }) props.load((mod) => { this.setState({ // handle both es imports and cjs mod: mod.default ? mod.default : mod }) }) } render() { if (!this.state.mod) return false return this.props.children(this.state.mod) } }
在入口处使用按需加载:
// bundle模型用来异步加载组件 import Bundle from './bundle.js'; // 引入单个页面(包括嵌套的子页面) // 同步引入 import Index from './app/index.js'; // 异步引入 import ListContainer from 'bundle-loader?lazy&name=app-[name]!./app/list.js'; const List = () => ( <Bundle load={ListContainer}> {(List) => <List />} </Bundle> ) <HashRouter> <Router basename="/"> <div> <Route exact path="/" component={Index} /> <Route path="/list" component={List} /> </div> </Router> </HashRouter>
webpack.config.js文件配置:
output: { path: path.resolve(__dirname,'./output'),filename: '[name].[chunkhash:8].bundle.js',chunkFilename: '[name]-[id].[chunkhash:8].bundle.js',},
个人觉得更好用的写法
import Loadable from 'react-loadable' import Loading from './my-loading-component' Loadable({ loader: () => import(`views/AsyncView`),// 如果没有loading动画,就返回null LoadingComponent: () => null,// 如果有loading动画,则如下 loading: Loading })
webpack配置:
// 添加插件babel-plugin-import-inspector { "plugins": [ ["import-inspector",{ "serverSideRequirePath": true,"webpackRequireWeakId": true,}] ] }