React 快速上手 - 07 前端路由 react-router

會煮咖啡的貓發表於2018-05-24

react

本系列目錄

點贊是美德 : )


React 快速上手 - 07 前端路由 react-router

目標

  • 基礎使用
  • 引數傳遞
  • 路由匹配
  • 轉換動畫
  • 跳轉路由

環境

  • react 16
  • react-router 4
  • react-router-dom 4
  • react-transition-group

0. 安裝

react-router

通過官網我們可以發現 react-router 可以用在 web 網站端 native 裝置端

我們這裡針對 web 網站端 安裝

yarn add react-router-dom
複製程式碼

react-router 會包自動依賴安裝

1. 先跑一個簡單例子

  • 程式碼示範
import React, {Component} from 'react'
import {HashRouter as Router, Route, Link, Switch} from 'react-router-dom'

const Home = () => (
  <div>
    <h2>首頁</h2>
  </div>
)
const About = () => (
  <div>
    <h2>關於</h2>
  </div>
)

class RouterView extends Component {
  render() {
    return (
      <Router>
        <div>
          <ul>
            <li>
              <Link to="/">首頁</Link>
            </li>
            <li>
              <Link to="/about">關於</Link>
            </li>
          </ul>

          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
          </Switch>
        </div>
      </Router>
    )
  }
}

export default RouterView
複製程式碼
  • 程式碼結構

最外層需要包裹元件 <Router> 元件 <Route> 就是你看到的元件內容,放在哪裡就哪裡顯示 元件 <Link> 是頁面連結

  • 動圖說明

基礎路由

  • codepen

https://codepen.io/ducafecat/pen/GdBPqZ

2. 基礎使用

2.1 BrowserRouter 還是 HashRouter

  • BrowserRouter 是需要服務端配合, 是基於html5的pushState和replaceState的,很多瀏覽器不支援,存在相容性問題。

連結地址長這樣 http://localhost:3000/about

  • HashRouter 是瀏覽器端解析路由

連結地址長這樣 http://localhost:3000#/about

  • 靈活切換
import {HashRouter as Router, Route, Link, Switch} from 'react-router-dom'
//import {BrowserRouter as Router, Route, Link, Switch} from 'react-router-dom'

<Router>
  ...
</Router>
複製程式碼

我用 as Router 做了別名,方便切換

2.2 元件 <Route> 屬性 exact 完全匹配

<Route path="/about" component={About} />
複製程式碼

exact=false 的時候 path 等於 /about /about/me 都能匹配

但是 exact=true 的時候 只匹配 path 等於 /about

2.3 元件 <Route> 屬性 strict 末尾斜槓的匹配

<Route strict path="/about/" component={About} />
複製程式碼

strict=true 路由請求末尾必須帶 /

2.4 元件 <Link>

生成路由連結

  • 屬性 to: string

路由地址字串

<Link to="/about?me=haha">關於</Link>
複製程式碼
  • 屬性 to: object

路由物件

<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }>關於</Link>
複製程式碼
  • 屬性 replace: bool

設定 true 替換瀏覽器物件 history 為當前路由,按回退按鈕時會發現之前的路由被替換了

2.5 元件 <NavLink>

生成路由連結的基礎上,如果是當前路由設定啟用樣式

  • 屬性 activeClassName: string

樣式名稱

<NavLink to="/about" activeClassName="selected">關於</NavLink>
複製程式碼
  • 屬性 activeStyle: object

樣式物件

<NavLink to="/about" activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}>關於</NavLink>
複製程式碼
  • 屬性 isActive: func

判斷函式

const checkIsActive = (match, location) => {
  if (!match) {
    return false
  }
  ...
  return true
}

<NavLink to="/about" isActive={checkIsActive}>關於</NavLink>
複製程式碼

2.6 元件 <Switch>

只渲染出第一個與當前訪問地址匹配的 <Route><Redirect>

否則你有幾個 <Route> 都會顯示

2.7 元件 <Redirect>

路由重定向

<Switch>
  <Redirect from='/users/:id' to='/users/profile/:id'/>
  <Route path='/users/profile/:id' component={Profile}/>
</Switch>
複製程式碼

當請求 /users/:id 被重定向去 '/users/profile/:id'

  • 屬性 from: string

需要匹配的將要被重定向路徑。

  • 屬性 to: string

重定向的 URL 字串

  • 屬性 to: object

重定向的 location 物件

  • 屬性 push: bool

若為真,重定向操作將會把新地址加入到訪問歷史記錄裡面,並且無法回退到前面的頁面。

2.8 元件 <Prompt>

當使用者離開當前頁面前做出一些提示。

  • 屬性 message: string 當使用者離開當前頁面時,設定的提示資訊。
<Prompt message="確定要離開?" />
複製程式碼

Prompt

  • 屬性 message: func

當使用者離開當前頁面時,設定的回掉函式

<Prompt message={location => `確定要去 ${location.pathname} ?`} />
複製程式碼

Prompt-func

  • 屬性 when: bool

決定是否啟用 Prompt

3. 引數傳遞

3.1 編寫路由定義

<Route path="/topics/:topicId" component={Topic} />
複製程式碼

:topicId 定義引數

3.2 編寫接收元件

const Topic = ({match}) => (
  <div>
    <h3>引數: {match.params.topicId}</h3>
  </div>
)
複製程式碼

match.params 就是傳遞的引數

3.3 完整例子

程式碼

import React, {Component} from 'react'
import {BrowserRouter as Router, Route, Link, Switch} from 'react-router-dom'

const Topic = ({match}) => (
  <div>
    <h3>引數: {match.params.topicId}</h3>
  </div>
)

class RouterView extends Component {
  render() {
    return (
      <Router>
        <div>
          <ul>
            <li>
              <Link to="/topics/rendering">Rendering with React</Link>
            </li>
            <li>
              <Link to="/topics/components">Components</Link>
            </li>
            <li>
              <Link to="/topics/props-v-state">Props v. State</Link>
            </li>
          </ul>

          <Switch>
            <Route path="/topics/:topicId" component={Topic} />
          </Switch>
        </div>
      </Router>
    )
  }
}

export default RouterView
複製程式碼

動圖效果

引數傳遞

4. 沒有匹配

程式碼

import React, {Component} from 'react'
import {BrowserRouter as Router, Route, Link, Switch} from 'react-router-dom'

const Back = () => (
  <div>
    <h2>首頁</h2>
  </div>
)

const NoMatch = () => (
  <div>
    <h2>沒有匹配</h2>
  </div>
)

class RouterView extends Component {
  render() {
    return (
      <Router>
        <div>
          <ul>
            <li>
              <Link to="/back">返回</Link>
            </li>
            <li>
              <Link to="/also/will/not/match">路由請求</Link>
            </li>
          </ul>

          <Switch>
            <Route path="/back" component={Back} />
            <Route component={NoMatch} />
          </Switch>
        </div>
      </Router>
    )
  }
}

export default RouterView
複製程式碼

在最下面寫個預設元件,都沒命中,就是這個了

動圖效果

沒有匹配

5. 巢狀路由

react-route4 中巢狀要這樣寫

<Switch>
  <Route path="/article" component={ArticleList} />
  <Route path="/article/:id" component={Article} />
  <Route path="/article/:id/recommend" component={ArticleRecommend} />
</Switch>
複製程式碼

寫成一排,業務如下

path 元件 說明
/article ArticleList 文章列表
/article/:id Article 文章
/article/:id/recommend ArticleRecommend 文章推薦

6. 自定義路由

適合用來做許可權檢查

6.1 建立自定義 Route

const isAuthenticated = true
const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      isAuthenticated ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);
複製程式碼

6.2 使用自定義路由

...
<Route path="/public" component={Public} />
<Route path="/login" component={Login} />
<PrivateRoute path="/protected" component={Protected} />
複製程式碼

7. 轉換動畫

這裡用到了 react-transition-group

動圖效果

轉換動畫

7.1 安裝

yarn add react-transition-group
複製程式碼

7.2 編寫動畫 css

檔案 fade.css

.fade-enter {
  opacity: 0;
  z-index: 1;
}

.fade-enter-active {
  opacity: 1;
  transition: opacity 250ms ease-in;
}

.fade-exit {
  opacity: 0;
}
複製程式碼
名稱 說明
fade-enter 動畫啟動
fade-enter-active 動畫啟用
fade-exit 動畫離開

其它動畫樣式名字可以參考 css-transition

7.3 匯入包和動畫樣式

...
import {TransitionGroup, CSSTransition} from 'react-transition-group'
import './fade.css'
複製程式碼

TransitionGroup, CSSTransition 必須匯入

7.4 編寫樣式

const styles = {}

styles.fill = {
  position: 'relative',
  height: '200px',
  width: '500px'
}

styles.content = {
  ...styles.fill,
  top: '40px',
  textAlign: 'center',
  height: '120px'
}

styles.nav = {
  padding: 0,
  margin: 0,
  position: 'absolute',
  top: 0,
  height: '40px',
  width: '100%',
  display: 'flex'
}

styles.navItem = {
  textAlign: 'center',
  flex: 1,
  listStyleType: 'none',
  padding: '10px'
}

styles.hsl = {
  color: 'white',
  paddingTop: '20px',
  fontSize: '30px',
  height: '120px'
}

styles.rgb = {
  color: 'white',
  paddingTop: '20px',
  fontSize: '30px',
  height: '120px'
}
複製程式碼

這是 react 的樣式物件,當然你也可以寫成 .css 檔案

7.5 編寫導航

const NavLink = props => (
  <li style={styles.navItem}>
    <Link {...props} style={{color: 'inherit'}} />
  </li>
)
複製程式碼

7.6 編寫展示元件

// 切換區域 A
const HSL = ({match: {params}}) => (
  <div
    style={{
      ...styles.fill,
      ...styles.hsl,
      background: `hsl(${params.h}, ${params.s}%, ${params.l}%)`
    }}
  >
    hsl({params.h}, {params.s}%, {params.l}%)
  </div>
)

// 切換區域 B
const RGB = ({match: {params}}) => (
  <div
    style={{
      ...styles.fill,
      ...styles.rgb,
      background: `rgb(${params.r}, ${params.g}, ${params.b})`
    }}
  >
    rgb({params.r}, {params.g}, {params.b})
  </div>
)
複製程式碼

7.7 編寫容器元件

const RouterView = () => (
  <Router>
    <Route
      render={({location}) => (
        <div style={styles.fill}>
          <Route
            exact
            path="/"
            render={() => <Redirect to="/hsl/10/90/50" />}
          />

          <ul style={styles.nav}>
            <NavLink to="/hsl/10/90/50">Red</NavLink>
            <NavLink to="/hsl/120/100/40">Green</NavLink>
            <NavLink to="/rgb/33/150/243">Blue</NavLink>
            <NavLink to="/rgb/240/98/146">Pink</NavLink>
          </ul>

          <div style={styles.content}>
            <TransitionGroup>
              <CSSTransition key={location.key} classNames="fade" timeout={300}>
                <Switch location={location}>
                  <Route exact path="/hsl/:h/:s/:l" component={HSL} />
                  <Route exact path="/rgb/:r/:g/:b" component={RGB} />
                  <Route render={() => <div>Not Found</div>} />
                </Switch>
              </CSSTransition>
            </TransitionGroup>
          </div>
        </div>
      )}
    />
  </Router>
)
複製程式碼
  • codepen

https://codepen.io/ducafecat/pen/rvroYP

程式碼

參考

相關文章