Lambda博客
  • 自我介绍
  • SSR
    • 浅谈SSR
  • React
    • 页面路由——React-Router
    • 管好你的状态——React-Redux
    • 一个哪行——React详解
    • 左膀右臂一个不少——React初探
  • 问题记录
    • js-xlsx实现纯前端导出excel表格
    • 前端开发需要了解的东西
    • 打造高性能网站
  • JavaScript学习笔记
    • 语法和API
    • js-垃圾回收
    • 收集的JS使用技巧
    • 执行机制
    • 原型和原型链
    • 执行上下文
    • 事件循环
    • JavaScript手写代码
    • 43道JS面试题
    • 悄悄的带走了你的东西——闭包详解
    • 你是个富二代吗?——JavaScript作用域链
    • 捉摸不透的女生——JavaScript类型转换
    • 这是你的女神!——JavaScript
  • 网络学习笔记
    • 强不强——HTTP-协商缓存VS强缓存
    • 网络缓存
    • 我要飞的更高——计算机网络
    • 快点!再快点!——CDN
    • 喜怒哀乐多状态——HTTP状态码
    • 你会爱上我的(ಥ_ಥ) ——TCP详解
    • 隔壁老王想篡位?门都没有——同源策略
  • 软技能
  • 安全专题
    • 保护好自己——网站安全,预防
  • 浏览器兼容性
    • 我是个好人——浏览器兼容性
  • 多图片加载——懒加载详解
  • 数据结构
  • Node
    • Node初探
  • Typescript
    • JS Plus 真香——初探 TypeScript
  • 设计模式
    • 不要花里胡哨,要一套一套——设计模式(1)
    • 学会“套路”——设计模式(2)
  • Vue
    • 实操实干——vue实例记录
    • 停下来,问问自己——Vue-刨根问底
    • 你有喘息的机会吗?——Vue,逐步了解
    • 你累吗?来来来,安利框架——Vue-初次见面
  • 小程序
    • 今天天气怎么样——记一次小程序开发
  • Webpack
    • 蛋糕分割整合工具——Webpack-前端工程化
  • 你一块,我一块——Web-modules 前端模块化
  • HTML5
    • 你会画小猪佩奇吗?—— canvas
    • 画个矢量图——HTML5-SVG
    • 听说你爱闹腾——HTML5-多媒体
    • 动画神器——HTML5-requestAnimationFrame
    • 留下痕迹——HTML5-客户端存储
    • 你知道指北针吗?——HTML5-DeviceMotionEvent
    • 你在哪啊?我在这啊——HTML5-Geolocation
    • 你到这,你到那——HTML5-拖拽
    • 你从这,到那——HTML5-拖拽上传文件
    • 开启前端之路——HTML 标签
  • 瀑布流(无限滚动)
  • 我是怎么来的?——浏览器渲染原理
  • Css
    • 由大变小,你行吗 —— 移动端
    • 动起来,这样比较炫—— CSS3 动画
    • 请不要拐弯抹角 —— CSS3 选择器
    • 想炫吗?—— CSS3 属性
    • 最后的最后——CSS自问自答
    • 万事开头难?——深入学习前端常见布局
    • 一入前端深似海——BFC剖析
    • 还有哪些好玩的东西——CSS提升
    • 那些好看的页面是怎么形成的——CSS 初识
  • 拿个小本本记下——Cookie
由 GitBook 提供支持
在本页
  • React-Router
  • 简介
  • 基础
  • 路由匹配原理
  • Histories
  • 高级用法
在GitHub上编辑
  1. React

页面路由——React-Router

React-Router

简介

React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 之间的同步。

使用 React Router

import React from 'react'
import { render } from 'react-dom'

// 首先我们需要导入一些组件...
import { Router, Route, Link } from 'react-router'

// 然后我们从应用中删除一堆代码和
// 增加一些 <Link> 元素...
const App = React.createClass({
  render() {
    return (
      <div>
        <h1>App</h1>
        {/* 把 <a> 变成 <Link> */}
        <ul>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/inbox">Inbox</Link></li>
        </ul>

        {/*
          接着用 `this.props.children` 替换 `<Child>`
          router 会帮我们找到这个 children
        */}
        {this.props.children}
      </div>
    )
  }
})

// 最后,我们用一些 <Route> 来渲染 <Router>。
// 这些就是路由提供的我们想要的东西。
React.render((
  <Router>
    <Route path="/" component={App}>
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox} />
    </Route>
  </Router>
), document.body)

如果你不熟悉 JSX,也可以用普通对象来替代。

const routes = {
    path: '/',
    component: App,
    childRoutes: [
        {path: 'about', component: About},
        {path: 'inbox', component: Inbox}
    ]
}
React.render(<Router routes={routes} />, document.body)

获取 URL 参数

为了从服务器获取 message 数据,我们首先需要知道它的信息。当渲染组件时,React Router 会自动向 Route 组件中注入一些有用的信息,尤其是路径中动态部分的参数。

const Message = React.createClass({

  componentDidMount() {
    // 来自于路径 `/inbox/messages/:id`
    const id = this.props.params.id

    fetchMessage(id, function (err, message) {
      this.setState({ message: message })
    })
  },
})

也可以通过 query 字符串来访问参数。比如访问 /foo?bar=baz可以通过访问 this.props.location.query.bar 从 Route 组件中获取 'baz'的值

基础

添加首页

当URL为/时,我们想渲染一个在 App中的组件。不过此时,App 的 render中的this.prop.children还是undefined。这种情况下我们可以使用 IndexRoute来设置一个默认页面。

import { IndexRoute } from 'react-router'

const Dashboard = React.createClass({
  render() {
    return <div>Welcome to the app!</div>
  }
})

React.render((
  <Router>
    <Route path="/" component={App}>
      {/* 当 url 为 / 时渲染 Dashboard */}
      <IndexRoute component={Dashboard} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        <Route path="messages/:id" component={Message} />
      </Route>
    </Route>
  </Router>
), document.body)

让 UI 从 URL 中解耦出来

通过在定义 path 中使用 /messages/:id 替换 messages/:id 在多层前套路由中使用绝对路径的能力让我们对 URL 拥有绝对的掌握。我们无需在 URL 中添加更多的层级。

兼容旧的 URL

使用 <Redirect> 使URL重定向。

import { Redirect } from 'react-router'

React.render((
  <Router>
    <Route path="/" component={App}>
      <IndexRoute component={Dashboard} />
      <Route path="about" component={About} />
      <Route path="inbox" component={Inbox}>
        <Route path="/messages/:id" component={Message} />

        {/* 跳转 /inbox/messages/:id 到 /messages/:id */}
        <Redirect from="messages/:id" to="/messages/:id" />
      </Route>
    </Route>
  </Router>
), document.body)

进入和离开 Hook

Route 可以定义 onEnter 和 onLeave 两个 hook,这些 hook 会在页面跳转确认时触发一次。这些 hook 对于一些情况非常的有用,例如权限验证或者在路由跳转前将一些数据持久化保存起来。

在路由跳转过程中

  • onLeave 会在所有将离开的路由中触发,从最下层的路由开始直到最外层路由结束。

  • onEnter 会在将进入的路由时触发。

替换配置方式

如果你不想使用 JSX,也可以直接使用原生的 route 数组对象。

const routeConfig = [
  { path: '/',
    component: App,
    indexRoute: { component: Dashboard },
    childRoutes: [
      { path: 'about', component: About },
      { path: 'inbox',
        component: Inbox,
        childRoutes: [
          { path: '/messages/:id', component: Message },
          { path: 'messages/:id',
            onEnter: function (nextState, replaceState) {
              replaceState(null, '/messages/' + nextState.params.id)
            }
          }
        ]
      }
    ]
  }
]

React.render(<Router routes={routeConfig} />, document.body)

路由匹配原理

路由拥有三个属性来决定是否匹配一个URL

  • 嵌套关系

  • 路径语法

  • 优先级

嵌套关系

当一个给定的 URL 被调用时,整个集合中(命中的部分)都会被渲染。嵌套路由被描述成一种树形结构

路径语法

匹配一个(或一部分)URL的一个字符串模式。大部分的路由路径都可以直接按照字面量理解,除了下边的特殊符号

  • :paramName - 匹配一段位于 /、?或#之后的URL

  • () - 在它内的内容被认为是可选的

  • * - 匹配任意字符直到命中下一个字符或者整个 URL 的末尾

优先级

路由算法会根据定义的顺序自顶向下匹配路由

Histories

React Router是建立在 history 上的。简而言之,一个 history 知道如何去监听浏览器地址栏的变化,并解析这个 URL 转化为 location 对象,然后 router 使用它匹配到路由,最后正确的渲染页面。

  • browserHistory

  • hashHistory

  • createMemoryHistory

使用

import { browserHistory } from 'react-router'

render(
  <Router history={browserHistory} routes={routes} />,
  document.getElementById('app')
)

browserHistory

他使用浏览器中 History API 用于处理 URL

服务器需要做好处理 URL 的准备。处理应用启动最初的/这样的请求是没问题的,但当用户来回跳转并在/account/123刷新时,服务器就会收到来自 account/123的请求,这时需要处理这个 URL 并响应对应的 JavaScript 代码。

hashHistory

Hash History 使用 URL 中的 hash # 部分去创建形如example.com/#/some/path 的路由。

对比

Hash History 不需要服务器任何配置就可以运行,但是我们不推荐在实际线上环境中使用,因为每一个 web 应用都应该渴望使用 browserHistory。

传递参数

当一个 History 通过应用程序中的 push 或 replace跳转时,他可以在新的 location中存储 location state而不显示在 URL 中,这就像是在一个 HTML 中 post 的表单数据。

createMemoryHistory

Memory History 不会在地址栏被操作或读取。同时它也非常适合测试和其他渲染环境

和另外两种 history 的一点不同是必须创建它,这种方式便于测试。

const history = createMemoryHistory(location)

使用

import React from 'react'
import { render } from 'react-dom'
import { browserHistory, Router, Route, IndexRoute } from 'react-router'

import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'

render(
  <Router history={browserHistory}>
    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      <Route path='about' component={About} />
      <Route path='features' component={Features} />
    </Route>
  </Router>,
  document.getElementById('app')
)

默认路由 (IndexRoute) 与 IndexLink

默认路由 (IndexRoute)

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

Index Links

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

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

高级用法

动态路由

React Router 适用于小型网站,也可以支持大型网站。

对于大型应用来说,一个首当其冲的问题就是所需加载的 JavaScript 的大小。程序应当只加载渲染页所需的 JavaScript。

路由是个非常适于做代码分拆的地方:他的责任就是配置好每个 view

React Router 里的路径匹配以及组件加载都是异步完成,不仅允许你延迟加载组件,并且可以延迟加载路由配置。在首次加载包中你只需要有一个路径定义,路由会自动解析剩下的路径。

Route 可以定义 getChildRoutes,getIndexRoute和getComponents这几个函数。他们都是异步执行,并且只有在需要的时候才被调用。称之为“逐渐匹配”。

匹配 URL 并只加载该 URL 对应页面所需的路径配置和组件。

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'))
    })
  }
}

跳转前确认

React Router 它提供一个 routerWillLeave 钩子,使得组件可以拦截正在发生的跳转,或在离开 route 前提示用户。

  1. return false 取消此次跳转

  2. return返回提示信息,在离开 route前提示用户进行确认。

通过在 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?'
  },
})

服务端渲染

服务端渲染与客户端渲染有些许不同

  • 发生错误时发送一个 500 的响应

  • 需要重定向时发送一个 30x 的响应

  • 在渲染之前获得数据(用 router 帮你完成这点)

为了迎合这一需求,需要在 <Router />API 下一层使用

  • 使用 match 在渲染之前根据 location 匹配 route

  • 使用 RoutingContext 同步渲染route组件

import { renderToString } from 'react-dom/server'
import { match, RoutingContext } from 'react-router'
import routes from './routes'

serve((req, res) => {
  // 注意!这里的 req.url 应该是从初始请求中获得的
  // 完整的 URL 路径,包括查询字符串。
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.send(500, error.message)
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
      res.send(200, renderToString(<RoutingContext {...renderProps} />))
    } else {
      res.send(404, 'Not found')
    }
  })
})

组件生命周期

<Route path="/" component={App}>
  <IndexRoute component={Home}/>
  <Route path="invoices/:invoiceId" component={Invoice}/>
  <Route path="accounts/:accountId" component={Account}/>
</Route>

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

1.当用户打开应用 '/' 页面

组件
生命周期

APP

componentDidMount

Home

componentDidMount

Invoice

N/A

Account

N/A

2.当用户从 '/'跳转到 '/invoice/123'

组件
生命周期

App

componentWillReceiveProps, componentDidUpdate

Home

componentWillUnmount

Invoice

componentDidMount

Account

N/A

  • App 从 router 中接收到新的 props,所以触发了两个生命周期方法

  • Home 不再被渲染,所以它将被移除

  • Invoice 首次被挂载

3.当用户从 /invoice/123 跳转到 /invoice/789

组件
生命周期

App

componentWillReceiveProps, componentDidUpdate

Home

N/A

Invoice

componentWillReceiveProps, componentDidUpdate

Account

N/A

4.当从 /invoice/789 跳转到 /accounts/123

组件
生命周期

App

componentWillReceiveProps, componentDidUpdate

Home

N/A

Invoice

componentWillUnmount

Account

componentDidMount

获取数据

我们在 Invoice 组件里实现一个简单的数据获取功能

let Invoice = React.createClass({

  getInitialState () {
    return {
      invoice: null
    }
  },

  componentDidMount () {
    // 上面的步骤2,在此初始化数据
    this.fetchInvoice()
  },

  componentDidUpdate (prevProps) {
    // 上面步骤3,通过参数更新数据
    let oldId = prevProps.params.invoiceId
    let newId = this.props.params.invoiceId
    if (newId !== oldId)
      this.fetchInvoice()
  },

  componentWillUnmount () {
    // 上面步骤四,在组件移除前忽略正在进行中的请求
    this.ignoreLastFetch = true
  },

  fetchInvoice () {
    let url = `/api/invoices/${this.props.params.invoiceId}`
    this.request = fetch(url, (err, data) => {
      if (!this.ignoreLastFetch)
        this.setState({ invoice: data.invoice })
    })
  },

  render () {
    return <InvoiceView invoice={this.state.invoice}/>
  }

})
上一页React下一页管好你的状态——React-Redux

最后更新于3年前

YzAfw8.png

YzAqO0.png
服务器配置
Page cover image