React Router

2020-05-06  本文已影响0人  Sun____

一、基础

默认路由(IndexRoute)
在解释 默认路由(IndexRoute) 的用例之前,我们来设想一下,一个不使用默认路由的路由配置是什么样的:

<Router>
  <Route path="/" component={App}>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

当用户访问 / 时, App 组件被渲染,但组件内的子元素却没有, App 内部的 this.props.children 为 undefined 。 你可以简单地使用 {this.props.children || <home>} 来渲染一些默认的 UI 组件。
但现在,Home 无法参与到比如 onEnter hook 这些路由机制中来。 在 Home 的位置,渲染的是 Accounts 和 Statements。 由此,router 允许你使用 IndexRoute ,以使 Home 作为最高层级的路由出现.

<Router>
  <Route path="/" component={App}>
    <IndexRoute component={Home}/>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

现在 App 能够渲染 {this.props.children} 了, 我们也有了一个最高层级的路由,使 Home 可以参与进来。

Index Links
如果你在这个 app 中使用 <Link to="/">Home</Link>, 它会一直处于激活状态,因为所有的 URL 的开头都是 / 。 这确实是个问题,因为我们仅仅希望在 Home 被渲染后,激活并链接到它。

如果需要在 Home 路由被渲染后才激活的指向 / 的链接,请使用 <IndexLink to="/">Home</IndexLink>

二、高级用法

1. 动态路由
Route 可以定义 getChildRoutesgetIndexRoutegetComponents 这几个函数。它们都是异步执行,并且只有在需要时才被调用。我们将这种方式称之为 “逐渐匹配”。 React Router 会逐渐的匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。
如果配合 webpack 这类的代码分拆工具使用的话,一个原本繁琐的构架就会变得更简洁明了。

const CourseRoute = {
  path: 'course/:courseId',

  getChildRoutes(location, callback) {
    require.ensure([], function (require) {
      callback(null, [
        require('./routes/Announcements'),
        require('./routes/Assignments'),
        require('./routes/Grades'),
      ])
    })
  },

  getIndexRoute(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Index'))
    })
  },

  getComponents(location, callback) {
    require.ensure([], function (require) {
      callback(null, require('./components/Course'))
    })
  }
}

2. 跳转前确认
React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件 可以拦截正在发生的跳转,或在离开 route 前提示用户。routerWillLeave返回值有以下两种:

可以在 route 组件 中引入 Lifecycle mixin 来安装这个钩子。

import { Lifecycle } from 'react-router'

const Home = React.createClass({

  // 假设 Home 是一个 route 组件,它可能会使用
  // Lifecycle mixin 去获得一个 routerWillLeave 方法。
  mixins: [ Lifecycle ],

  routerWillLeave(nextLocation) {
    if (!this.state.isSaved)
      return 'Your work is not saved! Are you sure you want to leave?'
  },

  // ...

})

如果你想在一个深层嵌套的组件中使用 routerWillLeave钩子,只需在route 组件中引入 RouteContextmixin,这样就会把 route 放到 context 中。

import { Lifecycle, RouteContext } from 'react-router'

const Home = React.createClass({

  // route 会被放到 Home 和它子组件及孙子组件的 context 中,
  // 这样在层级树中 Home 及其所有子组件都可以拿到 route。
  mixins: [ RouteContext ],

  render() {
    return <NestedForm />
  }

})

const NestedForm = React.createClass({

  // 后代组件使用 Lifecycle mixin 获得
  // 一个 routerWillLeave 的方法。
  mixins: [ Lifecycle ],

  routerWillLeave(nextLocation) {
    if (!this.state.isSaved)
      return 'Your work is not saved! Are you sure you want to leave?'
  },

  // ...

})

3. 组件生命周期
路由切换时,组件生命周期的变化情况

4. 在组件外部使用导航
虽然在组件内部可以使用 this.context.router 来实现导航,但许多应用想要在组件外部使用导航。使用Router组件上被赋予的history可以在组件外部实现导航。

// your main file that renders a Router
import { Router, browserHistory } from 'react-router'
import routes from './app/routes'
render(<Router history={browserHistory} routes={routes}/>, el)

// somewhere like a redux/flux action file:
import { browserHistory } from 'react-router'
browserHistory.push('/some/path')

三、API接口

1. 组件--Router
props:

2. 组件--Link
允许用户浏览应用的主要方式。<Link> 以适当的 href 去渲染一个可访问的锚标签。<Link> 可以知道哪个 route 的链接是激活状态的,并可以自动为该链接添加 activeClassName 或 activeStyle。
props:

示例:

<Link to={`/users/${user.id}`} activeClassName="active">{user.name}</Link>
// 变成它们其中一个依赖在 History 上,当这个 route 是
// 激活状态的
<a href="/users/123" class="active">Michael</a>
<a href="#/users/123">Michael</a>

// 修改 activeClassName
<Link to={`/users/${user.id}`} activeClassName="current">{user.name}</Link>

// 当链接激活时,修改它的样式
<Link to="/users" style={{color: 'white'}} activeStyle={{color: 'red'}}>Users</Link>

3. Route (组件的配置)
Route 是用于声明路由映射到应用程序的组件层。
props:

const routes = (
  <Route component={App}>
    <Route path="groups" component={Groups}/>
    <Route path="users" component={Users}/>
  </Route>
)

class App extends React.Component {
  render () {
    return (
      <div>
        {/* 这会是 <Users> 或 <Groups> */}
        {this.props.children}
      </div>
    )
  }
}
// 想想路由外部的 context — 如果你可拔插
// `render` 的部分,你可能需要这么做:
// <App main={<Users />} sidebar={<UsersSidebar />} />

const routes = (
  <Route component={App}>
    <Route path="groups" components={{main: Groups, sidebar: GroupsSidebar}}/>
    <Route path="users" components={{main: Users, sidebar: UsersSidebar}}>
      <Route path="users/:userId" component={Profile}/>
    </Route>
  </Route>
)

class App extends React.Component {
  render () {
    const { main, sidebar } = this.props
    return (
      <div>
        <div className="Main">
          {main}
        </div>
        <div className="Sidebar">
          {sidebar}
        </div>
      </div>
    )
  }
}

class Users extends React.Component {
  render () {
    return (
      <div>
        {/* 当路径是 "/users/123" 是 `children` 会是 <Profile> */}
        {/* UsersSidebar 也可以获取作为 this.props.children 的 <Profile> ,
            所以这有点奇怪,但你可以决定哪一个可以
            继续这种嵌套 */}
        {this.props.children}
      </div>
    )
  }
}
<Route path="courses/:courseId" getComponent={(location, cb) => {
  // 做一些异步操作去查找组件
  cb(null, Course)
}}/>
<Route path="courses/:courseId" getComponents={(location, cb) => {
  // 做一些异步操作去查找组件
  cb(null, {sidebar: CourseSidebar, content: Course})
}}/>

4. PlainRoute(组件的配置)
route 定义的一个普通的 JavaScript 对象。Router 把 JSX 的 <Route> 转化到这个对象中,如果你喜欢,你可以直接使用它们。 所有的 props 都和 <Route> 的 props 一样,除了那些列在这里的。
props:

let myRoute = {
  path: 'course/:courseId',
  childRoutes: [
    announcementsRoute,
    gradesRoute,
    assignmentsRoute
  ]
}

// 异步的子 route
let myRoute = {
  path: 'course/:courseId',
  getChildRoutes(location, cb) {
    // 做一些异步操作去查找子 route
    cb(null, [ announcementsRoute, gradesRoute, assignmentsRoute ])
  }
}

// 可以根据一些 state
// 跳转到依赖的子 route
<Link to="/picture/123" state={{ fromDashboard: true }}/>

let myRoute = {
  path: 'picture/:id',
  getChildRoutes(location, cb) {
    let { state } = location

    if (state && state.fromDashboard) {
      cb(null, [dashboardPictureRoute])
    } else {
      cb(null, [pictureRoute])
    }
  }
}
// 例如:
let myIndexRoute = {
  component: MyIndex
}

let myRoute = {
  path: 'courses',
  indexRoute: myIndexRoute
}

// 异步的 index route
let myRoute = {
  path: 'courses',
  getIndexRoute(location, cb) {
    // 做一些异步操作
    cb(null, myIndexRoute)
  }
}

5. Redirect(组件的配置)
在应用中 <Redirect> 可以设置重定向到其他 route 而不改变旧的 URL。

props:

// 从 `/profile/123` 改变到 `/about/123`
<Route component={App}>
  <Route path="about/:userId" component={UserProfile}/>
  {/* /profile/123 -> /about/123 */}
  <Redirect from="profile/:userId" to="about/:userId" />
</Route>

注意,在 route 层 <Redirect> 可以被放在任何地方,尽管正常的优先 规则仍适用。如果你喜欢将下一个重定向到它各自的 route 上,from 的路径会匹配到一个与 path 一样的正常路径。

<Route path="course/:courseId">
  <Route path="dashboard" />
  {/* /course/123/home -> /course/123/dashboard */}
  <Redirect from="home" to="dashboard" />
</Route>

6. Route Components
当 route 匹配到 URL 时会渲染一个 route 的组件。路由会在渲染时将以下属性注入组件中:

render((
  <Router>
    <Route path="/" component={App}>
      <Route path="groups" component={Groups} />
      <Route path="users" component={Users} />
    </Route>
  </Router>
), node)

class App extends React.Component {
  render() {
    return (
      <div>
        {/* 这可能是 <Users> 或 <Groups> */}
        {this.props.children}
      </div>
    )
  }
}

7. routerWillLeave(nextLocation)
在组件中添加一个钩子,当路由要从 route 组件的配置中跳转出来时被调用,并且有机会去取消这次跳转。主要用于表单的部分填写。
在常规的跳转中, routerWillLeave 会接收到一个单一的参数:我们正要跳转的 location。去取消此次跳转,返回 false。

8. History

上一篇 下一篇

猜你喜欢

热点阅读