Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

React Router学习笔记

20
Dec
2016

React Router学习笔记

By Alex
/ in JavaScript
/ tags 学习笔记
0 Comments
简介

React Router(本文后续简写为RR)是一个专门服务于React应用的强大的路由库。利用它你可以轻松的建立URL和UI之间的对应关系、在浏览器历史记录中自由导航。

本章先手工实现一个简单的路由机制,然后利用RR进行改造,以了解RR的优势和基本功能。

手工实现路由
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import React from 'react'
import { render } from 'react-dom'
 
const About = React.createClass( { /*...*/ } )
const Inbox = React.createClass( { /*...*/ } )
const Home = React.createClass( { /*...*/ } )
 
const App = React.createClass( {
    getInitialState() {
        return {
            // 使用Hash部分进行路由
            route: window.location.hash.substr( 1 )
        }
    },
 
    componentDidMount() {
        // 当Hash部分变化后,需要改变React状态进行重渲染
        window.addEventListener( 'hashchange', () => {
            this.setState( {
                route: window.location.hash.substr( 1 )
            } )
        } )
    },
 
    render() {
        // 根据状态,也就是URL的Hash的不同,决定渲染哪个子组件
        let Child
        switch ( this.state.route ) {
            case '/about': Child = About; break;
            case '/inbox': Child = Inbox; break;
            default: Child = Home;
        }
 
        return (
            <div>
                <h1>App</h1>
                <ul>
                    <li><a href="#/about">About</a></li>
                    <li><a href="#/inbox">Inbox</a></li>
                </ul>
                <Child/>
            </div>
        )
    }
} )
 
render( <App />, document.body )
通过RR实现路由
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React from 'react'
import ReactDom from 'react-dom'
 
import { Router, Route, IndexRoute, Link, hashHistory } from 'react-router'
 
const App = React.createClass( {
    render() {
        return (
            <div>
                <h1>App</h1>
                /* 将a替换为RR提供的Link组件 */
                <ul>
                    <li><Link to="/about" >About</Link></li>
                    <li><Link to='/inbox' >Inbox</Link></li>
                </ul>
                /* 这里不需要手工判断渲染哪个子路由组件,RR知道 */
                {this.props.children}
            </div>
        )
    }
} )
 
/**
* 渲染的是由Router/Route定义的路由规则,而非UI组件
*/
ReactDom.render( (
    /* 使用hashHistory,表示路由的判断依据是URL的Hash部分 */
    <Router history={hashHistory}>
        /* path指定路由基准URL */
        <Route path="/" component={App}>
            /* 路由的嵌套层次,与组件的嵌套层次对应 */
            <IndexRoute component={Home}/>  /* 默认UI */
            <Route path="about" component={About}/>  /* Hash部分是About时,例如/#/about */
            <Route path="inbox" component={Inbox}/>  /* Hash部分是Inbox时 */
        </Route>
    </Router>
), document.body )

可以看到,RR知道如何(根据配置信息)构建嵌套的UI,你不需要手工的读取URL并找到对应的Child组件, App与其内部的子组件实现了解耦。

添加更多的UI

现在,我们在inbox这个路径下面再嵌套一级子路由: 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const Message = React.createClass( {
    render() {
        return <h3>Message</h3>
    }
} )
 
const Inbox = React.createClass( {
    render() {
        return (
            <div>
                <h2>Inbox</h2>
                /* 渲染下一级子路由组件 */
                {this.props.children}
            </div>
        )
    }
} )
 
ReactDom.render( (
    <Router history={hashHistory}>
        <Route path="/" component={App}>
            <IndexRoute component={Home}/>
            <Route path="about" component={About}/>
            <Route path="inbox" component={Inbox}>
                /* 嵌套路由 */
                <IndexRoute component={InboxStats}/> /* 嵌套路由的默认UI */
                /* 在类似/inbox/messages/123这样的URL下渲染Message组件 */
                <Route path="messages/:id" component={Message}/>
            </Route>
        </Route>
    </Router>
), document.body )

这样,当你访问/inbox/messages/123时,RR将会构建如下组件层次:

XHTML
1
2
3
4
5
<App>
    <Inbox>
        <Message params={{ id: 'Jkei3c32' }}/>
    </Inbox>
</App>

当你访问/inbox时,则构建如下组件层次:

XHTML
1
2
3
4
5
<App>
    <Inbox>
        <InboxStats/>
    </Inbox>
</App>

总之,URL的层次和组件的层次具有对应关系,上层组件的this.props.children,等于匹配的下层路由的component属性所指向的组件。注意这种对应关系不一定是“严格”的:

  1. URL中的一个“层次”,可以跨越多个以斜杠 / 划分的片断
  2. 组件中的一个“层次”,可以包含多级React元素。层次的结果取决于你在何处声明this.props.children

这种对应关系很自然,但是如果要手工实现的话需要编写不少罗嗦的代码。

获取路径变量和URL参数

React组件会被RR自动注入路径变量,例如messages/:id中的id,可以通过 props.params.id 读取到:

JavaScript
1
2
3
4
5
6
7
8
const Message = React.createClass( {
    componentDidMount() {
        const id = this.props.params.id
        fetchMessage( id, function ( err, message ) {
            this.setState( { message: message } )
        } )
    }
} )

URL中附带的查询参数也被注入,例如/user?name=alex中的name,可以通过 props.location.query.name 读取到。

基础
Router组件

该组件用于将RR引入到React应用程序中,渲染时一般将其作为JSX根元素:

JavaScript
1
2
3
4
5
ReactDom.render( (
    <Router history={hashHistory}>
       /* ... */
    </Router>
), document.body )
属性列表
属性 说明
history 该router需要监听的history对象,通常是browserHistory或hashHistory
children 一个或者多个Route、PlainRoute组件。指定路由的规则集
routes children的别名
createElement 当router准备渲染一个组件树分支时,调用此函数进行React元素的创建。你可以用该方法控制创建过程:
JavaScript
1
2
3
4
5
6
<Router createElement={createElement} />
 
// 默认行为
function createElement(Component, props) {
    return <Component {...props} />
}
onError

方法签名: onError(error) 

进行路由匹配的时候可能出现错误,这些错误通常来自于那些异步的特性,例如route.getComponents、route.getIndexRoute、route.getChildRoutes等。你可以基于此方法进行捕获和处理

onUpdate

一旦router改变其状态以对URL变更进行响应,即调用此方法

Route组件

这是一个配置组件(Configuration Components),定义一个路由规则,该规则将一个URL片断和一个React组件对应起来。路由规则可以嵌套,并与URL嵌套、组件嵌套对应。

JSX中的Route元素实际上等价于Route的子类型PlainRoute。

属性列表:

属性 说明
path 此规则匹配的URL片断,可以使用绝对路径(以/开头),也可以使用相对于父Route元素的想对路径
component 对于根元素,指定渲染什么React组件,对于非根元素,指定上级Route元素的 props.children 对应什么组件
components 以对象形式指定多个命名组件,父路由组件可以通过 props[name] 来访问这些组件。举例:
JavaScript
1
<Route path="groups" components={{main: Groups, sidebar: GroupsSidebar}} />

 在父路由组件中,你可以这些引用这两个子路由组件:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
const {main, sidebar} = this.props
return (
    <div>
        <div className="Main">
            {main}
        </div>
        <div className="Sidebar">
            {sidebar}
        </div>
    </div>
)
getComponent

方法签名: getComponent(nextState, callback) 。callback的签名: cb(err, component) 

类似于component,但是用于动态路由。RR会在需要的时候调用此函数,加载需要的组件。此方法的实现示例:

JavaScript
1
2
3
4
5
(nextState, cb) => {
    // 异步的查找、加载组件
    // 执行RR提供的回调
    cb(null, Course)
}
onEnter

方法签名:

JavaScript
1
2
3
4
5
6
7
8
// nextState 下一个(即将进入的)路由状态对象
// replace 函数,调用它可以重定向到另外一个位置
// callback(err) 如果提供此参数,则onEnter被异步调用,路由切换在callback调用前一直阻塞
// 函数中的this指向当前Route对象
onEnter(nextState, replace, callback?)
 
// 前一个(正要离开的)路由状态对象
onLeave(prevState)

路由切换被确认时执行的钩子函数。使用这些钩子可以做很多事情,例如:

  1. Enter路由之前执行身份验证
  2. Leave路由之前保存数据

在路由切换时:

  1. 首先在旧叶子Route上执行 onLeave 钩子,并向上逐级执行直到遇到新旧URL公用的祖先路由(此祖先路由的onLeave不执行)为止
  2. 然后在最顶级的新旧URL不同的祖先路由上执行 onEnter 钩子,并向下逐级执行到新叶子路由为止

示例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const userIsInATeam = (nextState, replace, callback) => {
    // 路由切换前需要准备数据
    fetch(/**/) .then(response = response.json()) .then(userTeams => {
            // 某些条件下,可以重定向路由到其它位置
            if (userTeams.length === 0) {
                replace(`/users/${nextState.params.userId}/teams/new`)
            }
            // 调用RR提供的回调,以完成路由切换
            callback();
        }) .catch(error => {
            // 在此执行错误处理
            callback(error);
        })
}
 
<Route path="/users/:userId/teams" onEnter={userIsInATeam}/>
onLeave
onChange

方法签名: onChange(prevState, nextState, replace, callback?) ,参数作用类似于onEnter

当浏览器的地址发生改变,但是当前路由对象既不Enter也不Leave的情况下触发的钩子方法。触发时机举例:

  1. 当前路由的子路由发生变化
  2. URL查询参数部分发生变化

以下属性为PlainRoute专有

childRoutes

子路由集合,对应JSX中Route的子元素

getChildRoutes

方法签名:

JavaScript
1
2
3
// partialNextState 部分解析的下一状态,因为子路由尚未加载,因而路由匹配尚未完成
// callback(err, routesArray)
getChildRoutes(partialNextState, callback)

类似于childRoutes,但是用于动态路由。RR会在需要的时候调用此函数,以完成路由的匹配。示例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 静态路由,子路由代码需要同步加载
let myRoute = {
    path: 'course/:courseId',
    childRoutes: [
        announcementsRoute,
        gradesRoute,
        assignmentsRoute
    ]
}
 
// 异步子路由,仅仅在匹配 course/**/**这样的URL时才异步的加载
let myRoute = {
    path: 'course/:courseId',
    getChildRoutes(location, cb) {
        // 在此加载子路由
        // 然后调用RR提供的回调
        cb(null, [announcementsRoute, gradesRoute, assignmentsRoute])
    }
}
 
 
// 导航依赖的子路由,可以链接到某些状态
<Link to="/picture/123" state={{ fromDashboard: true }}/>
 
let myRoute = {
    path: 'picture/:id',
    getChildRoutes(partialNextState, cb) {
        let {state} = partialNextState
 
        // 根据条件,加载不同的子路由
        if (state && state.fromDashboard) {
            cb(null, [dashboardPictureRoute])
        } else {
            cb(null, [pictureRoute])
        }
    }
}
indexRoute

参考IndexRoute子元素

getIndexRoute

方法签名: getIndexRoute(partialNextState, callback) 

异步的加载IndexRoute

IndexRoute组件

这是一个配置组件,属于特殊的路由规则,当(下层)URL片断为空时,匹配此规则。例如:

XHTML
1
2
3
<Route path="/" component={App}>
    <IndexRoute component={Dashboard} />
</Route>

当你访问URL / 时,会渲染App组件,且其children为一个Dashboard组件。

Redirect组件

这是一个配置组件,用于URL的重定向,例如:

XHTML
1
<Redirect from="messages/:id" to="/messages/:id" />
属性列表
属性 说明
from 需要重定向的URL,包含路径变量部分
to 重定向到的目标
query 查询参数部分,默认情况下自动把from的查询参数带过去
IndexRedirect组件

这是一个配置组件,指定指定默认使用的下层URL。例如:

JavaScript
1
2
3
4
5
<Route path="/" component={App}>
    <IndexRedirect to="/welcome" />
    // 访问 / 时自动重定向到 /welcome
    <Route path="welcome" component={Welcome} />
</Route>
Link组件

该组件用于触发路由切换,以便在应用程序中导航,该组件渲染为a标签。

如果Link指向的路由恰恰是应用中的当前路由(URL匹配),则RR自动给Link添加activeClassName样式类,并且其activeStyle指定的样式被启用。

属性列表:

属性 说明
to

一个位置描述符(location descriptor)对象,或者一个字符串。如果不指定该属性,生成一个没有href的a标签

使用字符串时,指定需要链接到的绝对路径。使用位置描述符时,可以指定以下属性:

  1. pathname  URL的路径部分
  2. query  一个对象,指定URL查询参数部分
  3. hash  URL的#部分,例如#a-hash
  4. state 需要持久化到location的状态

示例:

JavaScript
1
2
3
4
5
6
// 字符串
<Link to="/hello">Hello</Link>
// 位置描述符
<Link to={{ pathname: '/hello', query: { name: 'ryan' } }}>Hello</Link>
// 返回位置描述符的函数
<Link to={location => ({ ...location, query: { name: 'ryan' } })}>Hello</Link> 
activeClassName to指定的路由是当前路由时,启用的样式类
activeStyle to指定的路由是当前路由时,启用的样式
onClick 点击时执行的函数,在此函数调用e.preventDefault()可以阻止路由切换
onlyActiveOnIndex

仅当to与当前路由精确匹配时,才认为是Active。等价于 <IndexLink> 组件

如果不指定该属性,那么 <Link to="/">Home</Link> 这个链接总是Active,因为URL总是以/开头。此时可以使用 <IndexLink to="/">Home</IndexLink> 代替,这样仅当URL为/时才匹配

规则匹配算法

同级别的Route,先声明的具有更高的优先级。

URL语法和匹配规则如下:

语法 匹配说明
:paramName

匹配一个URL片断,直到遇到 / 、 ? 或者 # ,组件自动获得 params.paramName 属性。举例:

JavaScript
1
2
// 匹配:/hello/alex  /hello/wong
<Route path="/hello/:name">  
() 包围URL的一部分,表示该部分是可选的。举例:
JavaScript
1
2
// 匹配:/hello/alex  /hello
<Route path="/hello(/:name)">
* 非贪婪的通配,遇到此通配符后面指定的那个字符之前一直匹配。捕获到的匹配项会存入 params.splat 属性中。举例:
JavaScript
1
2
// 匹配:/files/hello.jpg  /files/hello.html
<Route path="/files/*.*">  
** 贪婪的通配,直到遇到  / 、 ? 或者 # 。捕获到的匹配项会存入 params.splat 属性中。举例:
JavaScript
1
2
// 匹配:/files/hello.jpg   /files/path/to/file.jpg
<Route path="/**/*.jpg">  
Histories

RR在history库的基础上构建,一个history对象可以监听浏览器的URL的改变,并把URL解析为一个location对象。RR使用此location对象来匹配路由规则并渲染正确的组件树。

缺省可用的history实现包括三种:

History实现 说明
browserHistory

对于运行在浏览器中的应用,这是推荐的实现。它使用浏览器内置的History API来操控URL,能够创建“真实的”URL,例如 gmem.cc/some/path

服务器配置

要在所有浏览器中使用这种History实现,需要服务器的支持。你可能需要将某个通配的路径映射到同一个HTML文件,例如gmem.cc/**总是映射到gmem.cc/index.html。

使用Express作为服务器时,可以参考如下代码:

JavaScript
1
2
3
app.get('*', function (request, response){
    response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})

使用Nginx时,可以使用try_files指令:

1
2
3
4
5
server {
    location / {
        try_files $uri /index.html;
    }
}

使用Apache时可以使用rewrite模块:

.htaccess
1
2
3
4
5
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

IE8,IE9支持

RR会自动检测浏览器是否支持History API,如果不支持,所有URL转换都会导致完全的页面reload

hashHistory

仅仅使用URL的哈希(#)部分,生成gmem.cc/#/some/path风格的URL

该实现的优点是不需要配置服务器。缺点是URL比较难堪而且不支持服务器端渲染

createMemoryHistory 不会操控浏览器地址栏,使用RR进行服务器端渲染时使用,也可以用于测试React Native等其它渲染环境
withRouter函数

用于包装其它React组件,为其提供 props.router 属性。函数签名:

JavaScript
1
2
3
4
5
6
/**
* Component 被包装的组件
* options 选项:
*      withRef  如果为true,则包装后的组件的getWrappedInstance()返回被包装组件
*/
withRouter(Component, [options])

props.router属性与context.router是同一种对象。

RouterContext

依据给定的路由状态,渲染对应的组件树。Router组件使用了该组件,在React组件的上下文对象上添加一个 this.context.router 属性。

context.router

该对象提供与路由有关的方法和数据,你可以使用该对象进行编程式的路由控制:

属性/方法 说明
push(pathOrLoc)  切换路由到一个新的URL,并在浏览器历史中压入条目。示例:
JavaScript
1
2
3
4
5
6
7
8
// 使用字符串
router.push( '/users/alex' )
// 使用位置描述符对象
router.push( {
    pathname: '/users/alex',
    query: { modal: true },
    state: { fromDashboard: true }
} ) 
replace(pathOrLoc)  类似于push,但是替换浏览器历史的当前条目
go(n)  在浏览器历史中前进或者后退
goBack()  在浏览器历史中后退一步
goForward()  在浏览器历史中前进一步
setRouteLeaveHook(route, hook)  注册一个钩子,在离开route这个路由时调用,用于导航确认
createPath(pathOrLoc, query)  根据router的配置,把查询参数对象转换为URL路径名(不包括协议、域名部分)
createHref(pathOrLoc, query) 

创建一个URL,如果使用hashHitory,会自动在URL路径名前面添加#/

isActive(pathOrLoc, indexOnly)  判断pathOrLoc是否对应当前路由。如果匹配路由R,则同样匹配R的祖先路由,那么在R及其祖先路由对应的Component中调用该方法,均返回true
路由组件

所谓路由组件,是指路由规则(Route)关联的组件,当路由规则被匹配、且父路由组件输出了当前路由组件时,路由组件被自动渲染。

路由组件被渲染时,RR自动为其注入一些属性。

注入的属性
属性 说明
location 当前location对象
params URL中的动态部分,包括路径变量捕获
route 导致此组件被渲染的路由对象
router 与context.router相同
routeParams

捕获到的、在Route.path中直接声明的路径变量。如果Route.path为users/:userId 而当前URL为/users/123/portfolios/345。那么:

  1. this.props.routeParams为 {userId: '123'} 
  2. this.props.params为 {userId: '123', portfolioId: '345'} 
children 匹配的、将被渲染的子路由组件。如果当前路由使用命名组件,则该属性为undefined。各命名组件将作为this.props的直接属性
高级主题
动态路由

对于大型应用来说,下载尽可能少的JavaScript文件以启动应用很重要,最好仅下载与当前的UI相关的JS。如果不这样做,用户将忍受过长的加载时间。生产环境下我们通常使用模块化,配合代码分割(Code Splitting)技术来满足尽快启动的需求,然后随着用户的操作不断加载用到的JS。

路由定义了UI的样子,很自然的可以作为代码分割点。

RR支持异步的读取路由规则、异步的加载组件。在最初的捆绑文件(Bundle,即启动应用的那个代码分割块)中,你只需要提供一个路由规则,其它规则可以后续按需加载。

Route组件可以定义getChildRoutes、getIndexRoute、getComponents方法,这些方法仅在需要的时候才会被调用以完成规则匹配和组件渲染。RR称这种方式为渐进匹配(gradual  matching)——逐步的匹配URL片段且仅仅加载必要的信息。

下面是一个动态路由的示例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const CourseRoute = {
    path: 'course/:courseId',
    // 当尝试导航到course/**/**时,下面的方法被调用
    getChildRoutes(partialNextState, callback) {
        // 这里使用Webpack的CommonJS扩展
        require.ensure([], function (require) {
            callback(null, [
                // 同步加载子路由定义模块
                require('./routes/Announcements'),
                require('./routes/Assignments'),
                require('./routes/Grades'),
            ])
        })
    },
    // 当尝试导航到course/**时,下面的方法被调用
    getIndexRoute(partialNextState, callback) {
        require.ensure([], function (require) {
            callback(null, {
                component: require('./components/Index'),
            })
        })
    },
    // 当尝试导航到course/**时,该方法被调用,加载对应的组件
    getComponents(nextState, callback) {
        require.ensure([], function (require) {
            callback(null, require('./components/Course'))
        })
    }
}
导航确认

你可以调用router的 setRouteLeaveHook 方法,设置一个钩子,当离开某个路由时,该钩子会被执行,你可以利用此钩子:

  1. 向用户做出提示
  2. 阻止导航的发生

 示例:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// v2.4.0引入的withRouter可以向组件注入当前router对象
const Home = withRouter(
    React.createClass({
 
        componentDidMount() {
            // 为当前组件对应的route对象设置钩子
            this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave)
        },
 
        routerWillLeave(nextLocation) {
            //  返回false禁止导航
            //  返回字符串则让用户确认是否导航
            if (!this.state.isSaved)
                return 'Your work is not saved! Are you sure you want to leave?'
        },
 
    })
)
组件外导航

你可以使用 withRouter 包装一个组件,从而通过 this.props.router 获得当前router对象的引用。有了router对象后你就可以随意导航(触发路由切换)了。

在React组件外部,例如Redux中间件或者Flux Action的代码中,你可以通过history对象进行导航:

JavaScript
1
2
3
4
5
6
7
import {browserHistory} from 'react-router'
 
// 导航到 /some/path.
browserHistory.push('/some/path')
 
// 后退到前一个URL
browserHistory.goBack()
最小化Bundle的尺寸

为了简便,RR通过顶级模块react-router暴露了完整的API。这导致整个RR库及其依赖被包含到入口点Bundle中,从而增加了Bundle的大小。为了避免此问题,可以从react-router/lib的子模块进行导入:

JavaScript
1
2
3
4
5
import { Link, Route, Router } from 'react-router'
// 可以改写为:
import Link from 'react-router/lib/Link'
import Route from 'react-router/lib/Route'
import Router from 'react-router/lib/Router'

 

 

← Redux学习笔记
CSS Modules学习笔记 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 基于Eclipse的Node.js开发
  • ExtJS 4的MVC框架
  • WebStorm知识集锦
  • JavaScript知识集锦
  • ExtJS 4中的数据处理

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2