iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 17
4
Modern Web

一步一腳印的React旅程系列 第 17

[筆記][React]React網頁好朋友Router(1)-基本用法篇!

Hi!大家好啊!React旅程走到這裡開始覺得有點累了XD,下班放假都還是不能忘記準備文章,嗚嗚...不過和不只報名一個系列的大大們來說,還算是算小咖了,希望這一次可以和大家一起走到最後,好的!題外話結束進入正文吧!


今天在準備文章的時候,發現為什麼有些範例寫的不同,再多查一下才發現原來React Router經過了一次大改版「v4」,在v4版中的React Router把所有的東西都包成了組件,也讓React Router更接近React的開發型態!所以本系列中選擇使用v4版來學習,對v2v3版就不太適用了,這點要注意一下!

掰惹位,因為小弟我沒有碰過v2v3的版本,所以無法詳細舉出他們和v4的不同,真的很不好意思!不過詳細差別可以參考下方文章:

  1. https://medium.com/frochu/viscovery-%E5%A6%82%E4%BD%95%E5%BE%9Ereact-router-v2-%E8%BD%89%E6%8F%9B%E8%87%B3v4-ebcb0e418a8d

React Router登場!

Router就是路由!不過中文名稱不是重點XD,這個套件主要是用來搭配ReactSPA的開發。

欸等等!這裡說的不是按摩的SPA,是大家常說的「單頁應用程式(single page web application)」,主要是因為React的最小單位是組件,所以如果我們用它來開發SPA的話,就不必每次都重新刷新頁面,只要在跳頁的時候用目標組件重新渲染畫面就可以了,其他和傳統Web的差別上方的網址也講得很清楚!如果有興趣可以去看看。

首先把react-router-dom裝上,在v4版就不需要安裝react-router才能使用這個,畢竟上面說他們的核心各自切出來了對吧!那我們先進行安裝了:

npm i react-router-dom

安裝完後就來建立專案吧!過程我就不說了,建好後假設我們有以下基本的目錄:
https://ithelp.ithome.com.tw/upload/images/20181010/201069355XvvZLZnYD.jpg

綠色框框內的Component內有幾個組件:

  1. Title.jsx
    import React from "react"
    
    class Title extends React.Component {
        render(){
            return <h1>{this.props.title}</h1>
        }
    }
    export {Title}
    
  2. Home.jsx
    import React from "react"
    
    class Home extends React.Component{
        render(){
            return <p>這裡是首頁</p>
        }
    }
    export {Home}
    
  3. About.jsx
    import React from "react"
    
    class About extends React.Component{
        render(){
            return <p>這邊是關於我們</p>
        }
    }
    export {About}
    

話說記得上面的那三隻組件都要記得在同目錄的index.js做匯出哦!下面的Main.jsx雖然我不會提,但也不要忘記哦!

  1. Main.jsx
    由於Main組件會export最後的結果,所以我把Router寫在他上面,就像其他套件一樣,使用前要先import物件進來,這裡除了React外我們會需要Router中的BrowserRouterRouteLink三個組件,所以寫成這樣:

    import React from "react"
    import { BrowserRouter, Route, Link } from "react-router-dom"
    import { Title } from "../Title"
    import { Home } from "../Home"
    import { About } from "../About"
    

    所有的Route設定都要在BrowserRouter組件中,所以先在render中寫好他,另外要注意的是BrowserRouter內只能有一個根DOM,所以我們把所有內容寫在它的根div中:

    class Main extends React.Component {
        render() {
            return (
                <BrowserRouter>
                    <div>
                    </div>
                </BrowserRouter>
            )
        }
    }
    

    Title加上去,之後使用選單為每個Link設定連結,這裡的連結不會導致重新刷新網站,只會在客戶端的網址後方加入to中指定的值而已,等等我們可以來看一下:

    class Main extends React.Component {
        render() {
            return (
                <BrowserRouter>
                    <div>
                        <Title title="功能選單" />
                        <ul>
                            {/*Link組件中的to會改變網址,但不會刷新頁面*/}
                            <li><Link to="/">回到首頁</Link></li>
                            <li><Link to="/about">關於我們</Link></li>
                        </ul>
                    </div>
                </BrowserRouter>
            )
        }
    }
    

    目前的畫面,當點擊關於我們時上方的網址會改變,但可以發現下方並沒有重新跳轉新的頁面,還是維持在原本的頁面:
    https://ithelp.ithome.com.tw/upload/images/20181010/20106935r5aoWLD3XL.jpg

    最後用Route組件來設定pathcomponent的屬性,path會去讀網址後方的路徑,並依渲染該Routercomponent所指定的組件:

    class Main extends React.Component {
        render() {
            return (
                <BrowserRouter>
                    <div>
                        <Title title="功能選單" />
                        <ul>
                            <li><Link to="/">回到首頁</Link></li>
                            <li><Link to="/about">關於我們</Link></li>
                        </ul>
                        {/*這邊塞一個分隔線*/}
                        <hr />
                        {/*路徑指定/代表根目錄,所以預設就會渲染Home組件,
                        而後方有/about的話會渲染About*/}
                        <Route path="/" component={Home} />
                        <Route path="/about" component={About} />
                    </div>
                </BrowserRouter>
            )
        }
    }
    

    設定好後來看看結果:
    https://ithelp.ithome.com.tw/upload/images/20181010/20106935iWEpH4jRTH.jpg

    運行的原理就是藉由Linkto去改變網址(上圖的1),之後Route再判斷是否符合自身的path去他的component(上圖的2)!

    但是!以為我會略過他嗎?Home組件根本一直存在啊!他不是設定只會在根目錄出現嗎?就讓我來解釋吧!這裡的原因是就算我切換到/about,也還是符合homeRoutepath指定的/對吧?所以我們需要在HomeRoute加入exact進行嚴格比對!一定只有/才顯示:

    //在Route內加入exact就會進行嚴格比對path
    <Route exact path="/" component={Home} />
    

    這麼一來只有完全符合path才會渲染出組件了:
    https://ithelp.ithome.com.tw/upload/images/20181010/20106935wXRG1czqhd.png
    所以如果沒有使用exact的話,只要path有前面符合就會渲染該組件,例如path="/Message"的話,不論網址是http://localhost:8080/MessageListhttp://localhost:8080/MessageList/Content都會當作符合而渲染組件。

突發狀況!

就當我以為一天又平安過去的時候,猜猜看發生什麼事情?其實也沒什麼,不過就只是在/About的時候點了重新整理:),但是讓我進入時空隧道的事情發生了,我看到了下方的畫面:
https://ithelp.ithome.com.tw/upload/images/20181010/201069354ZkQ90FCIX.png

真的是一群老天鵝飛過心中對吧?怎麼會這樣子啊!上一秒還可以的欸!重新整理就掛掉太扯了吧!就在此時又開始查了一堆資料!總算是發現了原因,聽我娓娓道來吧!

由於網頁再換頁的時候都會發送一個GET請求到伺服器上面,然後伺服器會回傳要顯示的畫面到客戶端上顯示,但是我們將Router設定在客戶端,一開始會將所有的JavaScript載入畫面中,只用一個index.html頁面去渲染各個組件,而且剛剛我們上面有看,Linkto只會改變網址並不會有GET的過程,所以在我們重新整理時,他依照網址/about發送了一個GET請求,不過我們並沒有叫做/about這個目錄或是檔案對吧?因此就會出現錯誤。

那怎麼辦!!!!!!

不好意思我太激動了,但是到底該怎麼辦才好?

別急,就在官方的常見問題中也有記載,要處理這個狀況可以用HashRouter取代BrowserRouter
記得上面的import也要改:

import { HashRouter, Route, Link } from "react-router-dom"

之後是Main組件的render()

class Main extends React.Component {
    render() {
        return (
            <HashRouter>
                <div>
                    <Title title="功能選單" />
                    <ul>
                        <li><Link to="/">回到首頁</Link></li>
                        <li><Link to="/about">關於我們</Link></li>
                    </ul>
                    <hr />
                    <Route exact path="/" component={Home} />
                    <Route path="/about" component={About} />
                </div>
            </HashRouter>
        )
    }
}

設定好後會發現網址列多了一個#,而接著不論怎麼重新整理都完全沒問題了:
https://ithelp.ithome.com.tw/upload/images/20181011/20106935hc15kiqned.png

網址後方的這個#的意思是指,#後的所有字串都不會發GET請求到server端,像是上方的http://localhost:8080/src/#/about就還是只會送出http://localhost:8080/src/server端,然後只要server端回傳JavaScript載入到客戶端,剩下的/about就由Router去判斷處理。

那既然這樣我們就使用HashRouter就好了對吧!應該說在當你沒有其他選擇的狀況下,也只能選用HashRouter,但如果開發的網站有上傳到伺服器管理(例如Appache)的話,就能夠依照各個伺服器的設置來處理這個狀況,詳情可以看這篇文章今天小弟找到這篇的時候,是跪著看的

最後附上今天的GitHub連結:
GitHub程式目錄連結
GitPage頁面連結


今天的文章到這裡,感謝各位大大的觀賞!如果文章中有任何錯誤或是講解不清楚的地方,都可以留言告訴我,小弟會盡快修改或補充文章內容的!謝謝大家/images/emoticon/emoticon41.gif

參考文章:

  1. https://reacttraining.com/react-router/web/example/basic

上一篇
[筆記][React]React的目錄結構篇
下一篇
[筆記][React]React網頁好朋友Router(2)-掌握match是成功的一半
系列文
一步一腳印的React旅程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言