使用react-router實現單頁面應用路由


這是Webpack+React系列配置過程記錄的第二篇。其他內容請參考:

上一篇文章講述了如何搭建單頁面應用的開發環境。本文接着介紹如何實現單頁面應用。

實際上,單頁面應用利用js實現了頁面的動態化,用戶使用時基本感知不到網頁是如何實現的,開發也不應該讓用戶感知到。為了實現這一步,react提供了一個官方的路由組件react-router,讓你的單頁面應用支持頁面的跳轉以及動態渲染。

使用react-router的前端實現

安裝react-router
npm install --save react-router-dom  

我使用的是最新版本4.1.1,這個版本的react-router針對瀏覽器、原生應用等環境已經做了區分。我做的是基於web的單頁面應用,因此安裝的時候我使用的是react-router-dom這個依賴。

使用react-router

還是基於上一篇文章搭建的開發環境,我們使用官方示例簡單入門對react-router的使用方法。

回顧一下上一篇文章的開發環境:

開發環境目錄

這里主要需要修改index.js,其內容改為官方示例代碼:

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

const BasicExample = () => (  
  <Router>
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/about">About</Link></li>
        <li><Link to="/topics">Topics</Link></li>
      </ul>

      <hr/>

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

const Home = () => (  
  <div>
    <h2>Home</h2>
  </div>
)

const About = () => (  
  <div>
    <h2>About</h2>
  </div>
)

const Topics = ({ match }) => (  
  <div>
    <h2>Topics</h2>
    <ul>
      <li>
        <Link to={`${match.url}/rendering`}>
          Rendering with React
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/components`}>
          Components
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/props-v-state`}>
          Props v. State
        </Link>
      </li>
    </ul>

    <Route path={`${match.url}/:topicId`} component={Topic}/>
    <Route exact path={match.url} render={() => (
      <h3>Please select a topic.</h3>
    )}/>
  </div>
)

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

ReactDOM.render(<BasicExample/>, document.getElementById('main'));  

注意到上面的代碼引入了react-router的BrowserRouter(重命名為Router),Route,Link三個組件。新版本的react-router提出了一切都以Component實現的原則,因此這些組件實際上都是React.Component的實現。

BrowserRouter是一個容器Component,其內部實現了路由跳轉的邏輯。舊版本直接使用Router就可以,新版本根據環境不同有不同的實現,Web環境使用BrowserRouter。

Route也是一個Component,配置了頁面路徑以及當用戶輸入的路徑命中時需要渲染的組件。

Link用於跳轉,有點像HTML的a標簽。不一樣的是Link是需要在后端進行編譯,最終可能以a標簽的形式呈現給用戶。

驗證react-router

使用下面命令編譯並運行應用:

npm run build  
npm start 

訪問http://localhost:2000 可以看到頁面已經可以正常訪問,且點擊可以實現頁面跳轉。

react-router效果

如果你細心一點,你會發現剛剛訪問的頁面鏈接是使用a標簽出發了頁面的跳轉。但是跳轉后頁面並沒有向后端發送頁面請求。事實上,這就是單頁面應用為了解耦前后端實現的目標之一。Link在頁面的呈現

我在index.js中使用Link組件,編譯后在瀏覽器上以a標簽呈現給用戶。而react-router連接了這些a標簽的事件處理,動態的實現組件的渲染和瀏覽器地址欄內容的修改。因此這個過程不需要重新加載內容也不會向后端發出網絡請求。

使用react-router的后端實現

單頁面是為了實現前后端的業務解耦,但絕對的解耦是不存在的。有兩個方面佐證這個觀點。首先是api協議的約定,這必須要前端與后端的同時支持;其次就是本文的主要內容——路由實現。

缺少后端支持的問題

通過上面內容我們知道單頁面應用的路由是通過js實現的,相對於傳統網站而言是“偽跳轉”。那么如果用戶直接在瀏覽器上輸入http://localhost:2000/about 然后回車會有什么效果。

使用我的demo環境,會看到下圖:缺少后端支持的效果

果斷的404響應。因為我的服務器根本沒有/about這個目錄或者文件。

增加后端支持

為了解決這個問題,我需要增加這樣一個依賴:

npm install --save connect-history-api-fallback 

並且在項目根目錄的index.js(注意不是public目錄的index.js,這里應該重新命名一下的,當前將就一下)。

內容改為:

var express = require('express');  
var app = express();

app.use('/', require('connect-history-api-fallback')()); // Add  
app.use('/', express.static('public'));

var server = app.listen(2000, function() {  
  var port = server.address().port;
  console.log('Open http://localhost:%s', port);
});

另外需要修改一下public目錄下的index.html文件的內容。這是使用相對路徑獲取js文件,屬於上次留下來的bug,不改的話會存在問題。內容如下:

<html>  
  <head>
  </head>
  <body>
    <p>Hello world</p>
    <div id='main'></div>
    <script src="/out.js"></script><!-- 改動行 -->
  </body>
</html>  

這樣就可以解決問題了。重新在瀏覽器輸入http://localhost:2000/about 可以看到頁面顯示正常了。

需要注意增加的代碼必須放在express.static行的上方。這涉及的express框架的原理,不在這里深究,只要知道connect-history-api-fallback組件根據配置,幫我們把public目錄下的index.html作為所有不存在的路徑的請求的響應返回給瀏覽器。

 

同步博客原文鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM