react-router-dom

1. BrowserRouter 与 HashRouter最显著的区别是,HashRouter的URL中含#,例如:http://example.com/#/your/page

2. webpack内置就支持code splitting, 但要使用babel(将JSX编译为JavaScript)时,需安装 @babel/plugin-syntax-dynamic-import插件。

module.exports = {
    "presets": ["@babel/preset-react"],
    "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

使用@loadable/component:

import loadable from "@loadable/component";
import Loading from "./Loading.js";
// 动态加载
const LoadableLogin = loadable(() => import("./login.js"), {
  fallback: <Loading />   // 加载时显示的组件
});

//在react-router中使用
<Route path="/" component={loadableLogin}></Route>

 3. react-router-dom hooks

要求:React >= 16.8,必须使用function声明,不能使用class extends

3.1. useHistory

import React from 'react';
import { connect } from 'react-redux';
import { useHistory } from 'react-router-dom';

// 使用function不能用class ... extends
function UserList (props){
    const history = useHistory();
    const users = props.users.map((user,index) => <li key={index}>{user.name}</li>);
    function handleClick (e) {
        history.push('/clock');
    }

    return (
        <div>
            <ul>
                { users }
            </ul>
            <button onClick={handleClick}>Go Back To Clock!</button>
        </div>
    )
}

export default connect(state => state)(UserList);

3.2 useParams 获取<Route> path中的参数。

         <Switch>
            <Route path="/login" >
              <Login />
            </Route>
            <Route path="/clock">
                <Clock />
            </Route>
            <Route path="/blog/:num" component={BlogPost} />
            <Route path="/">
                <UserList />
            </Route>
          </Switch>

注意如果不使用exact, path="/"必须放在最后(按照第一个匹配到的去渲染)

import React from 'react';
import {useParams} from 'react-router-dom';

export default function BlogPost(props) {
    let {num} = useParams();
   let {name} = useParams();
// let name = props.match.params.name; // console.log('in blog', name); return (<div>The params is {name}--{num}</div>); }

4.1 <BrowserRouter>使用HTML5history API(pushStatereplaceState, popState)

<BrowserRouter
  basename={optionalString}  // 适用于从子目录中获取资源,指定子目录
  forceRefresh={optionalBool}
  getUserConfirmation={optionalFunc}
  keyLength={optionalNumber}  // location.key的长度,默认6
>
  <App />
</BrowserRouter>         

4.2 <HashRouter> URL中含有#

<HashRouter
  basename={optionalString}
  getUserConfirmation={optionalFunc}
  hashType={optionalString}  // 用于定义window.location.hash的编码类型
>
  <App />
</HashRouter>

hashType:

  • "slash"- Creates hashes like#/and#/sunshine/lollipops
  • "noslash"- Creates hashes like#and#sunshine/lollipops
  • "hashbang"- Creates“ajax crawlable”(deprecated by Google) hashes like#!/and#!/sunshine/lollipops

4.3  <MemoryRouter> 用于测试或非浏览器环境(如React Native),可将历史URL记录保存在内存中。

<MemoryRouter
  initialEntries={optionalArray}  //full-blown location objects with {pathname, search, hassh, state} or string URLS.
  initialIndex={optionalNumber}
  getUserConfirmation={optionalFunc}
  keyLength={optionalNumber}
>
  <App />
</MemoryRouter>

4.4 <Link>

<Link to="/courses?sort=name" />  // to为String类型,包含pathname, search, hash部分
<Link
  to={{               // to为Object类型
    pathname: "/courses",
    search: "?sort=name",
    hash: "#the-hash",
    state: { fromDashboard: true }
  }}
/>
<Link to={location => `${location.pathname}?sort=name`} />  // to为Func类型
<Link to="/courses" replace />  // replace:点击后将替换history栈中的元素,而不是在栈顶新增

4.5 <NavLink>与<Link>相同, 在页面中都会以<a></a>的形式渲染出来。

<NavLink to="/react" activeClassName="hurray">
  React
</NavLink>
<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: "bold",
    color: "red"
  }}
>
  FAQs
</NavLink>
<NavLink exact to="/profile">  // exact只有当前选中的标签会显示active样式
  Profile
</NavLink>
<NavLink strict to="/events/">  //严格匹配路径右侧的/
  Events
</NavLink>

此外,还有aria-current属性,其值可以是:

  • "page" - used to indicate a link within a set of pagination links
  • "step" - used to indicate a link within a step indicator for a step-based process
  • "location" - used to indicate the image that is visually highlighted as the current component of a flow chart
  • "date" - used to indicate the current date within a calendar
  • "time" - used to indicate the current time within a timetable
  • "true" - used to indicate if the NavLink is active

默认是page.

4.6 <Prompt> 当用户离开(而开发者此时不希望用户离开此页)时,弹出提示

<Prompt
  when={formIsHalfFilledOut}
  message="Are you sure you want to leave?"
/>

4.7 <Redirect>重定向

<Route exact path="/">
  {loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>

默认情况下,to参数会取代from参数在history栈中的位置,但如果有push,则会向history栈中新增。

<Redirect push to="/somewhere/else" />
// 含匹配模式的redirect
<Switch>
  <Redirect from='/users/:id' to='/users/profile/:id'/>
  <Route path='/users/profile/:id'>
    <Profile />
  </Route>
</Switch>

 4.8 <Route>

(1)
<
Route> <NewsReport /> </Route>
(2)内联component
<
Route component={NewsReport}></Route> // 这种方式下,每次render都会重加载(remount)子元素(先unmount旧的,再mount新的)

相比于方式(2),更推荐使用(3)(4)。在更新时减小render成本。

(3) render函数
<Router> <Route path="/home" render={() => <div>Home</div>} /> </Router>
function FadingRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={routeProps => (
        <FadeIn>
          <Component {...routeProps} />
        </FadeIn>
      )}
    />
  );
}

ReactDOM.render(
  <Router>
    <FadingRoute path="/cool" component={Something} />
  </Router>,
  node
);

<Route component>优先于<Route render>, 因此不要在同一个<Route>中同时使用。

(4) <Route children>与<Route render>不同的一点是,无论path是否匹配其函数都会被调用,只不过当URL不匹配时,route的match属性为null.

(4)
function ListItemLink({ to, ...rest }) {
  return (
    <Route
      path={to}
      children={({ match }) => (
        <li className={match ? "active" : ""}>
          <Link to={to} {...rest} />
        </li>
      )}
    />
  );
}

ReactDOM.render(
  <Router>
    <ul>
      <ListItemLink to="/somewhere" />
      <ListItemLink to="/somewhere-else" />
    </ul>
  </Router>,
  node
);
<Route
  children={({ match, ...rest }) => (
    {/* Animate will always render, so you can use lifecycles
        to animate its child in and out */}
    <Animate>
      {match && <Something {...rest}/>}
    </Animate>
  )}
/>

优先级: 1.<Route children> 2.<Route component> 3.<Route render>, 因此同一个<Route>中最多使用其中一个。

path属性可以是String类型,还可以是Array类型。

<Route path="/users/:id">
  <User />
</Route>
<Route path={["/users/:id", "/profile/:id"]}>
  <User />
</Route>

exact属性:是否完全匹配

pathlocation.pathnameexactmatches?
/one /one/two true no
/one /one/two false yes

 strict属性:匹配location.path中是否以 / 结束,对于其后的分段无影响。

pathlocation.pathnamematches?
/one/ /one no
/one/ /one/ yes
/one/ /one/two yes
同时含有exact和strict属性。
pathlocation.pathnamematches?
/one /one yes
/one /one/ no
/one /one/two no
sensitive属性: 大小写敏感。

4.9 <Switch>的children只能是<Route>和<Redirect>,并且在匹配时,只有一个匹配到的会被渲染出来。
<Route>通过path属性进行匹配,而<Redirect>通过from属性进行匹配。如果<Route>不含path属性或者<Redirect>不含from属性,那么总是会匹配当前的路径。
let routes = (
  <Switch>
    <Route exact path="/">
      <Home />
    </Route>

    <Route path="/users">
      <Users />
    </Route>
    <Redirect from="/accounts" to="/users" />

    <Route>
      <NoMatch />
    </Route>
  </Switch>
);

5. history 对象,有以下属性:

  • length - (number) The number of entries in the history stack
  • action - (string) The current action (PUSHREPLACE, or POP)
  • location - (object) The current location. May have the following properties:
    • pathname - (string) The path of the URL
    • search - (string) The URL query string
    • hash - (string) The URL hash fragment
    • state - (object) location-specific state that was provided to e.g. push(path, state) when this location was pushed onto the stack. Only available in browser and memory history.
  • push(path, [state]) - (function) Pushes a new entry onto the history stack
  • replace(path, [state]) - (function) Replaces the current entry on the history stack
  • go(n) - (function) Moves the pointer in the history stack by n entries
  • goBack() - (function) Equivalent to go(-1)
  • goForward() - (function) Equivalent to go(1)
  • block(prompt) - (function) 阻塞导航

history对象是易变的,所以一般要获取location对象时不通过history对象,而是通过<Route>的渲染属性。

class Comp extends React.Component {
  componentDidUpdate(prevProps) {
    // will be true
    const locationChanged =
      this.props.location !== prevProps.location;

    // INCORRECT, will *always* be false because history is mutable.
    const locationChanged =
      this.props.history.location !== prevProps.history.location;
  }
}

<Route component={Comp} />;

6. location对象

{
  key: 'ac3df4', // not with HashHistory!
  pathname: '/somewhere',
  search: '?some=search-string',
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

获取location对象的方式:

以上方式获取的location对象可以用于生命周期hooks中。不要通过history.location(易变的)获取。

componentWillReceiveProps(nextProps) {
  if (nextProps.location !== this.props.location) {
    // navigated!
  }
}

location对象可用于以下:

const location = {
  pathname: '/somewhere',
  state: { fromDashboard: true }
}

<Link to={location}/>
<Redirect to={location}/>
history.push(location)
history.replace(location)

此外,可以将location对象用在以下场景中,

强制使用给定的location对象,而不是router默认的location.

7. match对象:<Route>如何进行匹配的。包含以下属性:

  • params - (object) Key/value pairs parsed from the URL corresponding to the dynamic segments of the path
  • isExact - (boolean) true if the entire URL was matched (no trailing characters)
  • path - (string) The path pattern used to match. Useful for building nested <Route>s
  • url - (string) The matched portion of the URL. Useful for building nested <Link>s

match对象可用于以下场景:

null match 当<Route>的path属性与当前的location不匹配时,其children属性对应的函数仍然会被调用,只不过此时match对象为null

// location.pathname = '/matches'
<Route path="/does-not-match"
  children={({ match }) => (
    // match === null
    <Route
      render={({ match: pathlessMatch }) => (
        // pathlessMatch === ???
      )}
    />
  )}
/>

没有path属性的<Route>,其match对象继承自父级<Route>。如果父级<Route>的match为null,那么子<Route>的match也为null.对于无path属性,且父级<Route> match为null的子<Route>,通过调用其children属性进行渲染。

8. withRouter 高阶组件: withRouter可以包装任何自定义组件,将react-router 的 history,location,match 三个对象传入。
无需一级级传递react-router 的属性,当需要用的router 属性的时候,在组件外包上withRouter(),就可以拿到需要的路由信息。

import React from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";

// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  };

  render() {
    const { match, location, history } = this.props;

    return <div>You are now at {location.pathname}</div>;
  }
}

// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation);

WrappedComponent用于测试组件是否孤立于其他组件。

// MyComponent.js
export default withRouter(MyComponent)

// MyComponent.test.js
import MyComponent from './MyComponent'
render(<MyComponent.WrappedComponent location={{...}} ... />)

 

posted @ 2020-03-04 20:50  cecelia  阅读(471)  评论(0编辑  收藏  举报