zoukankan      html  css  js  c++  java
  • react-router

    原文链接https://blog.csdn.net/liangklfang/article/details/53355553

    上面的例子表示当页面的路由满足了特定的格式,我就会去实例化Standarz这个router Component,那么我们看看实例化这个Router Component的时候会穿进去那些参数:

     

    下面我将会仔细分析一下上面的内容,不过之前我还是想去看看Router这个Route Component自带的history参数。

    问题2:Router这个Component的history参数有那些可选择项

    browserHistory:表示使用浏览器默认的history API去检测路由的变化,这时候服务器端必须能够处理特定的路由然后响应给客户端;支持服务器端渲染;URI比较简洁
    hashHistory:不需要服务器端支持;不支持服务器端渲染;如果应用通过push和replace来不断修改history,那么我们可以存储‘地址状态’,其不会在新的地址中出现,就像我们在HTML中通过post来提交数据一样。hash历史通过DOM API的window.location.hash = newHash来不断改变,因此不存在新的地方去存储地址状态。但是,我们希望所有的历史都能够通过‘地址状态’来存储,因此我们为每一个地址创建了一个独一无二的key,而且把key存储在sessionstorage中,当用户点击后退,前进的时候我们可以有一种机制去重新加载新的‘地址状态’。因此,当你在一个超链接中不断点击的时候其后面的_k是不断变化的,形式为:_k=a7h1uf
    createMemoryHistory:Memory history不会修改和读取地址栏的地址,这是我们实现服务端渲染的方式,而且他对于测试和其他渲染环境,如React Native也是很有用的。他和其他两种历史也是不一样的,因为你必须创建一个Memory History,s使用下面这种方式就可以进行测试了:

     

    const history = createMemoryHistory(location)

    如果你想要进一步个性化你的history选项,或者使用其他方式去增强你的history,你可以使用useRouterHistory。注意:useRouterHistory已经是使用history的useQueries ,useBasename增强过了的。

     

    下面是使用了basename:

     

    1. import { useRouterHistory } from 'react-router'
    2.  
      import { createHistory } from 'history'
    3.  
       
    4.  
      const history = useRouterHistory(createHistory)({
    5.  
      basename: '/base-path'
    6.  
      })

    下面是使用了useBeforeUnload增强:

     

    1. import { useRouterHistory } from 'react-router'
    2.  
      import { createHistory, useBeforeUnload } from 'history'//useBeforeUnload和listenBeforeUnload
    3.  
      const history = useRouterHistory(useBeforeUnload(createHistory))()
    4.  
       
    5.  
      history.listenBeforeUnload(function () {
    6.  
      return 'Are you sure you want to leave this page?'
    7.  
      })

    问题3:router还有一个routes属性表示的是什么

     

    1.  
      let routes = <Route path="/" component={App}>
    2.  
      <Route path="/repos" component={Repos}/>
    3.  
      <Route path="/about" component={About}/>
    4.  
      </Route>;
    5.  
       
    6.  
      <Router routes={routes} history={browserHistory}/>

    也就是说,这时候我们不是Route嵌套在Router里面,而是作为Router的一个属性来传递,这也是可以的!如果发生了上面的路由嵌套,如加载/repos就会先加载APP,然后加载Repos,这时候我们的APP必须通过this.props.children来实例化被嵌套组件:

     

    1.  
      export default React.createClass({
    2.  
      render() {
    3.  
      return <div>
    4.  
      {this.props.children}
    5.  
      </div>
    6.  
      }
    7.  
      })

     

    问题4:我们先来研究上面那张图的params属性
    我们看看上面的路由配置:

     

     <Route path="/stdize(/:name)" component={Standarz}>
    

    我们来详细的看看下面的params的签名:

     

     


    也就是说,当我们实例化Standarz的时候,可以通过params来获取到我们传入的参数,所有参数全部封装在params对象上,如下:

     

    1. function Standarz({dispatch,content,params}) {
    2.  
      console.log('params',params);
    3.  
      //Object {name: "button"}
    4.  
      const contentProps={
    5.  
      dispatch,
    6.  
      content,
    7.  
      }
    8.  
      }

    注意:如果遇到了通配符的时候,我们也是可以通过上面这种方式来取出数据:

     

    1.  
      <Router>
    2.  
      <Route path="/:userName/:id" component={UserPage}/>
    3.  
      <Route path="/about/me" component={About}/>
    4.  
      </Router>

    而且这时候我们访问/about/me/的时候,第二个组件不会被初始化,因为第一个路由也被匹配了,所以下一个就被直接跳过了!这时候params对象上有userName和id两个属性!

     

    通配符的规则如下:
    (1):paramName
    :paramName匹配URL的一个部分,直到遇到下一个/、?、#为止。这个路径参数可以通过this.props.params.paramName取出。
    (2)()
    ()表示URL的这个部分是可选的。
    (3)*
    *匹配任意字符,直到模式里面的下一个字符为止。匹配方式是非贪婪模式。
    (4) **
    ** 匹配任意字符,直到下一个/、?、#为止。匹配方式是贪婪模式。

    问题5:上面的route是什么?

    从该图可以看出,其中传入的route属性有一个path属性表示的是路径,也就是我们在Route中配置的path属性,还有一个component属性表示的Connect函数!

    问题6:上面的routeParams属性是什么?

    从输出可以看到,其和params是一样的签名

    问题7:routes显示的是什么?

    问题8:location指的是?

    注意:这里的key就是上面说的‘地址状态’,如果访问地址为:http://localhost:8000/#/stdize/button?name=gssdy&_k=0zazfn,此时我们的query就是对象:{name:'gssdy'},而我们的search就是"?name=gssdy"。 location 对象,它可以简单的认为是 URL 的对象形式表示,这里要提的是 location.state,这里 state 的含义与 HTML5 的history.pushState API 中的 state 对象一样。每个 URL 都会对应一个 state 对象,你可以在对象里存储数据,但这个数据却不会出现在 URL 中。实际上,数据被存在了 sessionStorage 中;

    问题9:history的内部签名是?

    其内部提供了很多的方法,方法列表如上。

    9.1 history.pushState({name:'Nicholas'},'Nicholas Page','1.html')

    执行pushState后,新的状态信息会被加入历史栈中,而浏览器地址栏也会变成新的相对URL,但是浏览器不会真正向服务器发送信息,即使状态改变之后重新查询location.href也会返回和地址栏中相同的地址。第二个参数目前没有浏览器实现,因此完全尅传入一个空字符串,或者一个短标题。而第一个参数应该竟可能提供页面初始化所需要的各种信息,因为pushState会提供新的历史状态,你会发现后退按钮也能够用了!按下后退按钮会触发window的popstate事件,其含有一个state属性就是我们第一个参数传入的属性。记住:浏览器加载的第一个页面没有状态,因此点击后退按钮返回浏览器加载的第一个页面时候state为null.

    9.2 history.replaceState(null,'Nicholas Page','1.html')或者忽略第二个参数

    更新当前状态,传入的参数和pushState前两个参数相同,调用这个方法不会再历史栈中创建新的状态,只会重写当前状态。

    事实上,history/location两个对象同时存在于路由组件的 context 中,你还可以通过 React 的 context API 在组件的子级组件中获取到这两个对象。比如在 SignIn 组件的内部又包含了一个 SignInChild 组件,你就可以在组件内部通过 this.context.history 获取到 history 对象,进而调用它的 API 进行跳转等操作,这部分内容可以参考参考文献

    问题10:IndexRoute会默认加载一个组件,那么他是如何完成的?

     

    1. <Router>
    2.  
      <Route path="/" component={App}>
    3.  
      <Route path="accounts" component={Accounts}/>
    4.  
      <Route path="statements" component={Statements}/>
    5.  
      </Route>
    6.  
      </Router>

    上面代码中,访问根路径/,不会加载任何子组件。也就是说,App组件的this.props.children,这时是undefined。因此,通常会采用{this.props.children||<Home/>}这样的写法。这时,Home明明是Accounts和Statements的同级组件,却没有写在Route中。IndexRoute就是解决这个问题,显式指定Home是根路由的子组件,即指定默认情况下加载的子组件。你可以把IndexRoute想象成某个路径的index.html。

    1.  
      <Router>
    2.  
      <Route path="/" component={App}>
    3.  
      <IndexRoute component={Home}/>
    4.  
      <Route path="accounts" component={Accounts}/>
    5.  
      <Route path="statements" component={Statements}/>
    6.  
      </Route>
    7.  
      </Router>

    现在,用户访问/的时候,加载的组件结构如下。

    1.  
      <App>
    2.  
      <Home/>
    3.  
      </App>

    这种组件结构就很清晰了:App只包含下级组件的共有元素,本身的展示内容则由Home组件定义。这样有利于代码分离,也有利于使用React Router提供的各种API。注意,IndexRoute组件没有路径参数path。

     

    问题11:React-Router中还需要哪些注意点

    (1)Redirect 组件,具有from和to两个参数,to表示的是绝对路径
    (2)IndexRedirect组件表示访问跟路由的时候实例化的组件
    (3)Link组件是a标签的React版本,具有activeStyle和activeClassName为当前路由指定样式
    (4)链接到跟路由/应该使用IndexLink组件,而不是Link组件,因为跟路由activeStyle和activeClassName会失效,或者说总是生效,因为/会匹配任何子路由。而IndexLink组件会使用路径的精确匹配。当然也可以使用Link组件的onlyActiveOnIndex属性
    (5)如果使用表单跳转而不是用户点击跳转,可以使用browserHistory.push:

    1.  
      import { browserHistory } from 'react-router'
    2.  
      // ...
    3.  
      handleSubmit(event) {
    4.  
      event.preventDefault()
    5.  
      const userName = event.target.elements[0].value
    6.  
      const repo = event.target.elements[1].value
    7.  
      const path = `/repos/${userName}/${repo}`
    8.  
      browserHistory.push(path)
    9.  
      },

      或者使用context:

    1.  
      export default React.createClass({
    2.  
      // ask for `router` from context
    3.  
      contextTypes: {
    4.  
      router: React.PropTypes.object
    5.  
      },
    6.  
      handleSubmit(event) {
    7.  
      // ...
    8.  
      this.context.router.push(path)
    9.  
      },
    10.  
      })

    (6)路由的onEnter和onLeave钩子,可以使用onEnter替代Redirect组件或者做一些认证:
      

    1.  
      <Route path="inbox" component={Inbox}>
    2.  
      <Route
    3.  
      path="messages/:id"
    4.  
      onEnter={
    5.  
      ({params}, replace) => replace(`/messages/${params.id}`)
    6.  
      }
    7.  
      />
    8.  
      </Route>

     也可以通过setRouteLeaveHook为一个特定的Route的指定离开的函数,如下:

    1.  
      const Home = withRouter(
    2.  
      React.createClass({
    3.  
      componentDidMount() {
    4.  
      this.props.router.setRouteLeaveHook(
    5.  
      this.props.route,
    6.  
      this.routerWillLeave
    7.  
      )
    8.  
      },
    9.  
      routerWillLeave(nextLocation) {
    10.  
      // 返回 false 会继续停留当前页面,
    11.  
      // 否则,返回一个字符串,会显示给用户,让其自己决定
    12.  
      if (!this.state.isSaved)
    13.  
      return '确认要离开?';
    14.  
      },
    15.  
      })
    16.  
      )

     

     

    问题1:首先看看自己页面中使用的一个react-router

     

    1.  
      export default function ({ history }) {
    2.  
      return (
    3.  
      <Router history={history}>
    4.  
      <Route path="/" component={HomePage} />
    5.  
      <Route path="/users" component={Users} />
    6.  
      <Route path="/stdize(/:name)" component={Standarz}>
    7.  
      </Route>
    8.  
      <Route path="*" component={NotFound} />
    9.  
      </Router>
    10.  
      );
    11.  
      }

    上面的例子表示当页面的路由满足了特定的格式,我就会去实例化Standarz这个router Component,那么我们看看实例化这个Router Component的时候会穿进去那些参数:

     

    下面我将会仔细分析一下上面的内容,不过之前我还是想去看看Router这个Route Component自带的history参数。

    问题2:Router这个Component的history参数有那些可选择项

    browserHistory:表示使用浏览器默认的history API去检测路由的变化,这时候服务器端必须能够处理特定的路由然后响应给客户端;支持服务器端渲染;URI比较简洁
    hashHistory:不需要服务器端支持;不支持服务器端渲染;如果应用通过push和replace来不断修改history,那么我们可以存储‘地址状态’,其不会在新的地址中出现,就像我们在HTML中通过post来提交数据一样。hash历史通过DOM API的window.location.hash = newHash来不断改变,因此不存在新的地方去存储地址状态。但是,我们希望所有的历史都能够通过‘地址状态’来存储,因此我们为每一个地址创建了一个独一无二的key,而且把key存储在sessionstorage中,当用户点击后退,前进的时候我们可以有一种机制去重新加载新的‘地址状态’。因此,当你在一个超链接中不断点击的时候其后面的_k是不断变化的,形式为:_k=a7h1uf
    createMemoryHistory:Memory history不会修改和读取地址栏的地址,这是我们实现服务端渲染的方式,而且他对于测试和其他渲染环境,如React Native也是很有用的。他和其他两种历史也是不一样的,因为你必须创建一个Memory History,s使用下面这种方式就可以进行测试了:

     

    const history = createMemoryHistory(location)

    如果你想要进一步个性化你的history选项,或者使用其他方式去增强你的history,你可以使用useRouterHistory。注意:useRouterHistory已经是使用history的useQueries ,useBasename增强过了的。

     

    下面是使用了basename:

     

    1.  
      import { useRouterHistory } from 'react-router'
    2.  
      import { createHistory } from 'history'
    3.  
       
    4.  
      const history = useRouterHistory(createHistory)({
    5.  
      basename: '/base-path'
    6.  
      })

    下面是使用了useBeforeUnload增强:

     

     

    1.  
      import { useRouterHistory } from 'react-router'
    2.  
      import { createHistory, useBeforeUnload } from 'history'//useBeforeUnload和listenBeforeUnload
    3.  
      const history = useRouterHistory(useBeforeUnload(createHistory))()
    4.  
       
    5.  
      history.listenBeforeUnload(function () {
    6.  
      return 'Are you sure you want to leave this page?'
    7.  
      })

    问题3:router还有一个routes属性表示的是什么

     

    1.  
      let routes = <Route path="/" component={App}>
    2.  
      <Route path="/repos" component={Repos}/>
    3.  
      <Route path="/about" component={About}/>
    4.  
      </Route>;
    5.  
       
    6.  
      <Router routes={routes} history={browserHistory}/>

    也就是说,这时候我们不是Route嵌套在Router里面,而是作为Router的一个属性来传递,这也是可以的!如果发生了上面的路由嵌套,如加载/repos就会先加载APP,然后加载Repos,这时候我们的APP必须通过this.props.children来实例化被嵌套组件:

     

    1.  
      export default React.createClass({
    2.  
      render() {
    3.  
      return <div>
    4.  
      {this.props.children}
    5.  
      </div>
    6.  
      }
    7.  
      })

     

    问题4:我们先来研究上面那张图的params属性
    我们看看上面的路由配置:

     

     <Route path="/stdize(/:name)" component={Standarz}>
    

    我们来详细的看看下面的params的签名:

     


    也就是说,当我们实例化Standarz的时候,可以通过params来获取到我们传入的参数,所有参数全部封装在params对象上,如下:

     

    1.  
      function Standarz({dispatch,content,params}) {
    2.  
      console.log('params',params);
    3.  
      //Object {name: "button"}
    4.  
      const contentProps={
    5.  
      dispatch,
    6.  
      content,
    7.  
      }
    8.  
      }

    注意:如果遇到了通配符的时候,我们也是可以通过上面这种方式来取出数据:

     

    1.  
      <Router>
    2.  
      <Route path="/:userName/:id" component={UserPage}/>
    3.  
      <Route path="/about/me" component={About}/>
    4.  
      </Router>

    而且这时候我们访问/about/me/的时候,第二个组件不会被初始化,因为第一个路由也被匹配了,所以下一个就被直接跳过了!这时候params对象上有userName和id两个属性!

     

    通配符的规则如下:
    (1):paramName
    :paramName匹配URL的一个部分,直到遇到下一个/、?、#为止。这个路径参数可以通过this.props.params.paramName取出。
    (2)()
    ()表示URL的这个部分是可选的。
    (3)*
    *匹配任意字符,直到模式里面的下一个字符为止。匹配方式是非贪婪模式。
    (4) **
    ** 匹配任意字符,直到下一个/、?、#为止。匹配方式是贪婪模式。

    问题5:上面的route是什么?

    从该图可以看出,其中传入的route属性有一个path属性表示的是路径,也就是我们在Route中配置的path属性,还有一个component属性表示的Connect函数!

    问题6:上面的routeParams属性是什么?

    从输出可以看到,其和params是一样的签名

    问题7:routes显示的是什么?

    问题8:location指的是?

    注意:这里的key就是上面说的‘地址状态’,如果访问地址为:http://localhost:8000/#/stdize/button?name=gssdy&_k=0zazfn,此时我们的query就是对象:{name:'gssdy'},而我们的search就是"?name=gssdy"。 location 对象,它可以简单的认为是 URL 的对象形式表示,这里要提的是 location.state,这里 state 的含义与 HTML5 的history.pushState API 中的 state 对象一样。每个 URL 都会对应一个 state 对象,你可以在对象里存储数据,但这个数据却不会出现在 URL 中。实际上,数据被存在了 sessionStorage 中;

    问题9:history的内部签名是?

    其内部提供了很多的方法,方法列表如上。

    9.1 history.pushState({name:'Nicholas'},'Nicholas Page','1.html')

    执行pushState后,新的状态信息会被加入历史栈中,而浏览器地址栏也会变成新的相对URL,但是浏览器不会真正向服务器发送信息,即使状态改变之后重新查询location.href也会返回和地址栏中相同的地址。第二个参数目前没有浏览器实现,因此完全尅传入一个空字符串,或者一个短标题。而第一个参数应该竟可能提供页面初始化所需要的各种信息,因为pushState会提供新的历史状态,你会发现后退按钮也能够用了!按下后退按钮会触发window的popstate事件,其含有一个state属性就是我们第一个参数传入的属性。记住:浏览器加载的第一个页面没有状态,因此点击后退按钮返回浏览器加载的第一个页面时候state为null.

    9.2 history.replaceState(null,'Nicholas Page','1.html')或者忽略第二个参数

    更新当前状态,传入的参数和pushState前两个参数相同,调用这个方法不会再历史栈中创建新的状态,只会重写当前状态。

    事实上,history/location两个对象同时存在于路由组件的 context 中,你还可以通过 React 的 context API 在组件的子级组件中获取到这两个对象。比如在 SignIn 组件的内部又包含了一个 SignInChild 组件,你就可以在组件内部通过 this.context.history 获取到 history 对象,进而调用它的 API 进行跳转等操作,这部分内容可以参考参考文献

    问题10:IndexRoute会默认加载一个组件,那么他是如何完成的?

     

    1.  
      <Router>
    2.  
      <Route path="/" component={App}>
    3.  
      <Route path="accounts" component={Accounts}/>
    4.  
      <Route path="statements" component={Statements}/>
    5.  
      </Route>
    6.  
      </Router>

    上面代码中,访问根路径/,不会加载任何子组件。也就是说,App组件的this.props.children,这时是undefined。因此,通常会采用{this.props.children||<Home/>}这样的写法。这时,Home明明是Accounts和Statements的同级组件,却没有写在Route中。IndexRoute就是解决这个问题,显式指定Home是根路由的子组件,即指定默认情况下加载的子组件。你可以把IndexRoute想象成某个路径的index.html。

    1.  
      <Router>
    2.  
      <Route path="/" component={App}>
    3.  
      <IndexRoute component={Home}/>
    4.  
      <Route path="accounts" component={Accounts}/>
    5.  
      <Route path="statements" component={Statements}/>
    6.  
      </Route>
    7.  
      </Router>

    现在,用户访问/的时候,加载的组件结构如下。

    1.  
      <App>
    2.  
      <Home/>
    3.  
      </App>

    这种组件结构就很清晰了:App只包含下级组件的共有元素,本身的展示内容则由Home组件定义。这样有利于代码分离,也有利于使用React Router提供的各种API。注意,IndexRoute组件没有路径参数path。

     

    问题11:React-Router中还需要哪些注意点

    (1)Redirect 组件,具有from和to两个参数,to表示的是绝对路径
    (2)IndexRedirect组件表示访问跟路由的时候实例化的组件
    (3)Link组件是a标签的React版本,具有activeStyle和activeClassName为当前路由指定样式
    (4)链接到跟路由/应该使用IndexLink组件,而不是Link组件,因为跟路由activeStyle和activeClassName会失效,或者说总是生效,因为/会匹配任何子路由。而IndexLink组件会使用路径的精确匹配。当然也可以使用Link组件的onlyActiveOnIndex属性
    (5)如果使用表单跳转而不是用户点击跳转,可以使用browserHistory.push:

    1.  
      import { browserHistory } from 'react-router'
    2.  
      // ...
    3.  
      handleSubmit(event) {
    4.  
      event.preventDefault()
    5.  
      const userName = event.target.elements[0].value
    6.  
      const repo = event.target.elements[1].value
    7.  
      const path = `/repos/${userName}/${repo}`
    8.  
      browserHistory.push(path)
    9.  
      },

      或者使用context:

    1.  
      export default React.createClass({
    2.  
      // ask for `router` from context
    3.  
      contextTypes: {
    4.  
      router: React.PropTypes.object
    5.  
      },
    6.  
      handleSubmit(event) {
    7.  
      // ...
    8.  
      this.context.router.push(path)
    9.  
      },
    10.  
      })

    (6)路由的onEnter和onLeave钩子,可以使用onEnter替代Redirect组件或者做一些认证:
      

    1.  
      <Route path="inbox" component={Inbox}>
    2.  
      <Route
    3.  
      path="messages/:id"
    4.  
      onEnter={
    5.  
      ({params}, replace) => replace(`/messages/${params.id}`)
    6.  
      }
    7.  
      />
    8.  
      </Route>

     也可以通过setRouteLeaveHook为一个特定的Route的指定离开的函数,如下:

    1.  
      const Home = withRouter(
    2.  
      React.createClass({
    3.  
      componentDidMount() {
    4.  
      this.props.router.setRouteLeaveHook(
    5.  
      this.props.route,
    6.  
      this.routerWillLeave
    7.  
      )
    8.  
      },
    9.  
      routerWillLeave(nextLocation) {
    10.  
      // 返回 false 会继续停留当前页面,
    11.  
      // 否则,返回一个字符串,会显示给用户,让其自己决定
    12.  
      if (!this.state.isSaved)
    13.  
      return '确认要离开?';
    14.  
      },
    15.  
      })
    16.  
      )

    这部分内容可以阅读参考文献“React Router使用教程”

  • 相关阅读:
    暑假学习笔记(一)——初识Neo4j和APICloud入门
    置信规则库学习记录——1
    博客整理——宣传文案
    博客整理——软件工程实践总结
    博客整理——Alpha版冲刺
    博客整理——事后诸葛亮
    面试被企业拒绝后还有必要再申请吗?
    MapReduce分布式算法
    水题系列二:PhoneNumbers
    水题系列一:Circle
  • 原文地址:https://www.cnblogs.com/zyx-blog/p/9359624.html
Copyright © 2011-2022 走看看