React-Router 中文简明教程(中)

框架

浏览数:1,368

2017-6-7

本篇示例源码:
react-router-demo-part2

五. 为 Link 组件设置触发状态

有时我们需要在导航链接上设置一个触发状态的样式,让用户知道当前在哪个路由下,比如某宝的底部导航:
react-router-part1-catalog6

一种方法 是使用Link组件的activeStyle属性直接设置行内样式,修改modules/App.js中:

  • About
  • Repos
  • 注意:React 组件的样式属性值必须写在双括号内,这是因为样式在 JSX 语法里被作为一个对象。
    另一种更好的方法 是使用Link组件的activeClassName属性,为被触发的导航链接添加一个 Class:

  • About
  • Repos
  • 在index.html里简单定义下.active:

    
    

    手动刷新http://localhost:8080 因为 Webpack-dev-server 的热替换功能 不会作用于index.html。
    点击 About 或 Repos,被触发的导航链接会变为红色:
    react-router-part1-catalog7

    除了站点导航的一级链接,站点里的大部分链接 一般并不需要知道是否被触发,如果将导航链接单独提取为一个组件,你就不用担心因为activeClassName和activeStyle属性的到处存在 而难以维护。
    创建modules/NavLink.js,代码如下:

    // modules/NavLink.js
    import React from 'react'
    import { Link } from 'react-router'
    
    export default class NavLink extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                
            );
        }
    }
    

    这里的…this.props点点点,使用了延展操作符(spread operator),它可以传递一组属性键值对给
    修改modules/App.js,导入./NavLink 并将< Link/ >换成< NavLink/ >

    // NavLink
    import NavLink from './NavLink';
    
    // ...
    
    
  • About
  • Repos
  • 六. URL 参数

    来看下面的 URL:

    /repos/reactjs/react-router
    /repos/facebook/react
    

    这些 URL 可以匹配如下这样的路由路径:

    /repos/:userName/:repoName
    

    其中:后面的部分为参数,它的具体值可以通过路由组件的this.props.params[name]属性获取到
    首先,创建modules/Repo.js:

    // modules/Repo.js
    import React from 'react';
    
    export default class Repo extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                
    //编辑器问题h2标签添加了空格,复制代码请自行删除空格 < h2 >{this.props.params.repoName}< /h2 >
    ); } }

    这里的this.props.params.repoName是为了获取路由路径——/repos/:userName/:repoName中repoName的具体值。

    修改index.js,添加 Repo 路由,path属性指定了路由的 匹配规则,这里设为/repos/:userName/:repoName

    // ...
    // import Repo
    import Repo from './modules/Repo'
    
    ReactDOM.render((
        
            
                
                
                {/* Repo Route */}
                
            
        
    ), document.getElementById('app'));
    

    修改 modules/Repos.js,添加一些链接:

    // ...
    import { Link } from 'react-router'
    
    export default class Repos extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                
    //编辑器问题h2标签添加了空格,复制代码请自行删除空格 < h2 >Repos< /h2 >
    • React Router
    • React
    ); } }

    测试下结果,首页确保你的静态服务器是开启状态 如果没有就运行npm start
    访问http://localhost:8080/#/repos/可以看到:
    react-router-part1-catalog8

    点击链接 React Router 会渲染Repo组件:
    react-router-part1-catalog9

    可以看到Repo组件的内容为:react-router,正是通过this.props.params.repoName属性获取到路由匹配规则/repos/:userName/:repoName中参数:repoName的具体值,即 URL 中/repos/reactjs/react-router的 react-router 字段。
    另外,路由 path 属性的常用 匹配规则(参考了阮一峰的 React-Router 教程资料)如下:

    path 匹配 说明
    path=”/hello/:userName” /hello/nicholas
    /hello/jack
    :paramName匹配 URL 的一个部分,直到遇到下一个/,?,#为止。这个路径参数可以通过this.props.params.paramName获取
    path=”/hello(/:userName)” /hello
    /hello/nicholas
    /hello/jack
    ()表示 URL 的这个部分是可选的
    path=”/hello/*” /hello/nicholas
    /hello/nicholas/jack
    /hello/index.html
    *匹配任意字符,直到模式里面的下一个字符为止,匹配方式为非贪婪模式
    path=”/**/*.jpg” /hello/test.jpg
    /react/hello/to/a.jpg
    **匹配任意字符,直到遇到下一个/,?,#为止,匹配方式为贪婪模式

    七. 多层嵌套路由

    < router >还可以多层嵌套,上一节示例中当点击 Repos 的子导航链接 React Router 整个子导航区域会被内容渲染所替代,如果想查看 React 的内容,就必须点击浏览器回退按钮,这显然不利于用户体验。我们来改进下,让子导航始终存在,将内容部分放在导航下面显示
    首先,在index.js中将Repo路由嵌套进Repos路由内部:

    
        
    
    

    接着在modules/Repos.js中添加this.props.children属性,将Link组件换成之前我们封装的NavLink组件,链接的触发状态也有了:

    // ...
    // import NavLink
    import NavLink from './NavLink'
    
    export default class Repos extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                
    < h2 >Repos< /h2 > {/* 将 Link 换成 NavLink */}
    • React Router
    • React
    { this.props.children }
    ); } }

    ok 测试下,点击链接 React-Router 结果如下:
    react-router-part1-catalog10

    八. IndexRoute 组件

    // modules/App.js
    // ...
    render() {
        return (
            

    React Router Tutorial

    • About
    • Repos
    {this.props.children}
    ); }
    // index.js
    // ...
    ReactDOM.render((
        
            
                
                
            
        
    ), document.getElementById('app'));
    

    上面代码,当访问 根路径/(即http://localhost:8080/#/) 时,页面并没有渲染任何子组件,因为此时App组件中的{this.props.children}获取到的值为undefined,如图:
    react-router-part1-catalog11

    我们可以用{this.props.children || < Home/>}来解决这个问题,在this.props.children为空时渲染Home组件,创建modules/Home.js:

    // modules/Home.js
    import React from 'react';
    
    export default class Home extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return 
    Home
    } }

    在modules/App.js中增加{this.props.children || < Home/>}:

    // modules/App.js
    // ...
    render() {
        return (
            

    React Router Tutorial

    • About
    • Repos
    {this.props.children || }
    ); }

    上面的办法虽然能解决问题,但显然不优雅,应该将Home组件放到路由中。将modules/App.js中的{this.props.children || < Home/>}还原为{this.props.children}。
    修改index.js,使用 IndexRoute 组件作为首页路由:

    // index.js
    // ...
    import { Router, Route, hashHistory, IndexRoute } from 'react-router';
    import Home from './modules/Home';
    
    ReactDOM.render((
        
            
    
                {/* 添加 IndexRoute 组件 */}
                
    
                
                
                    
                
            
        
    ), document.getElementById('app'));
    

    访问根路径http://localhost:8080,测试结果如图:
    react-router-part1-catalog12

    九. IndexLink 组件

    你有没注意到,在我们的app中还缺少一个能切换渲染Home组件的导航链接?
    尝试在modules/App.js中添加一条< NavLink/>,将to属性设为/根路径:

  • Home
  • 测试时你会发现无论点击哪个导航链接,Home 链接始终处于激活状态(如图,点击 About 后):
    react-router-part1-catalog13

    因为,当子路由处于激活状态时 父路由也会被激活,而/路径可以匹配任何子路由,解决办法是使用 IndexLink 组件,修改 modules/App.js,增加activeClassName属性:

    // ...
    import { IndexLink } from 'react-router';
    
    export default class App extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                

    React Router Tutorial

      {/* add here */}
    • Home
    • About
    • Repos
    {this.props.children}
    ); } }

    这里有个小问题,只有在精确匹配到根路由时,Home 链接的activeClassName属性才生效。
    另一个方法是直接使用Link组件的onlyActiveOnIndex属性来达到同样效果,语法如下:

  • Home
  • 之前我们已经将导航链接Link组件封装成了NavLink组件,这里就只需在NavLink组件上增加onlyActiveOnIndex属性,修改modules/App.js:

    import React from 'react';
    import NavLink from './NavLink';
    
    export default class App extends React.Component {
        constructor(props) {
            super(props);
        }
    
        render() {
            return (
                

    React Router Tutorial

      {/* 增加 onlyActiveOnIndex 属性即可 */}
    • Home
    • About
    • Repos
    {this.props.children}
    ); } }

    react-router-part1-catalog14

    本篇示例源码:
    react-router-demo-part2

    原文地址:http://www.mrfront.com/2016/12/23/react-router-tutorial-part2/