react-router 踩坑記


react-router踩坑分享

背景

有一個Web項目,采用的是前后端分離的方式,前端使用的是react技術棧,后端使用的是Django框架。

有這么需求: 在一個新聞列表里,點擊某一條新聞,要求新聞詳情頁里的title keywords description是動態的。

辛苦歷程

JavaScript動態修改

當從后端取回數據時,用JavaScript動態改變title標簽和meta標簽就ok了。但是后來才發現,這個需求的目的是為了SEO,而爬蟲可以爬到的數據是后端返回的html頁面上的數據。如果是通過JavaScript來動態處理title keywords description的話,是不利於SEO的,因此,動態修改title keywords description只能放到后端做。后端將html拼接好,返回前端來,前端再進行相應的邏輯操作。

第一次嘗試

當思路理清楚之后,其實想着也就很簡單, 三步就可以了。

  1. 前端向后端發送請求。
  2. 后端獲取前端發送請求的URL地址,得到參數,然后從數據庫里讀出相應的數據。
  3. 返回一個新的html頁面。

然后開始進行測試:

首先,打開主頁面,點擊任意一條新聞,此時的url還是hash模式的。

當跳到新聞詳情頁表的時候,打開控制台,進入Network, 查看所發送的請求。

可以看見,請求的路徑是localhost:3000, 也就是根目錄,而后台配置的根路由的代碼如下:

urlpatterns = [
  url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'),
]

因此,可以知道,在請求后台的時候,后台返回了index.html模板回來。但是,問題出現了,我們想要返回的index.html里的keywords description title是在后端拼接好然后返回給前端,而目前我們所能實現的僅僅是返回一個index.html, 我們沒辦法改變那3個值,因為我們沒有辦法獲取到前端的URL,或者說是前端沒有辦法傳參數到后端去, 導致后端不能從數據庫里查出來那三個值。

那么,現在需要解決的問題就是如何將參數傳到后端去。

第二次嘗試

由於,點擊某條新聞的時候,跳到新聞詳情頁的時候,這是瀏覽器主動去向后端發送的請求,因此是很難去控制這個過程的。

所以,有了下面這個解決方案: 既然很難在瀏覽器主動發請求的時候去控制參數的傳遞,那么就不用去控制。而是在瀏覽器返回了index.html后,這個時候,已經有了新聞的id, 因此就可以人為的控制再發一次請求,這個時候后端配置一個路由,根據id去數據庫里查找keywords description title, 並在后端拼接好返回給前端。只不過有個缺點,會刷新一次頁面。

但是,在實踐的過程中,我理論上推導了一次整個過程,當人為控制發請求后,后端返回一個index.html, 然后又會執行js文件,然后又會發請求,然后又會返回index.html, 然后又會執行js文件,然后又會發請求。。。 就這樣,一直循環下去,顯然是不可能的。

第三次嘗試

這探索新的解決的方案的時候,了解了Http Header里的referer, 當瀏覽器向web服務器發送請求的時候,一般都會帶上referer, 告訴服務器,我是從哪個連接來的。因為想着后端只要獲取到了前端的URL,一切就搞定了,但是顯而易見,referer依然不行。拿網易雲音樂測試如下:

第四次嘗試

在不斷嘗試的過程中,珩哥提到,后端獲取不到前端的URL地址,是因為react-router路由是使用的hash路由(http://localhost:3001/#/news/index/4141?_k=mv8udy),如果將hashHistory改為browserHistory(http://localhost:3001/news/index/4141)的話,那么后端一定就可以獲取到前端的URL。

解決方案:

cd projectName/app/src
vim app.js

// hash路由
import { Router, hashHistory as historyProvider, match } from 'react-router';

// 修改為browserHistory
import { Router, browserHistory as historyProvider, match } from 'react-router';

進行查看:

主頁面:

新聞詳情頁:

總結步驟

  1. 設置react-router路由方式為: import { browserHistory } from 'react-router';
  2. 對服務器進行改造,否則用戶直接請求某個某個路由,就會報404。
  3. 進行測試。

那么如何進行測試呢?

  1. 看前端的請求地址。
  2. 多刷新頁面試試。(因為之前的是單頁面的,就算改變了路由,還是不會向后台發送請求。)
  3. 多看title的變化, 會遺留title不變化或者說是變化出錯的bug。
  4. 只要有處理路由相關的地方,都測一下。(很重要)

其他方案

不知道大家是否還有其他解決方案?

原理

History

是一個庫,react-router基於它來管理歷史會話記錄。

簡單的說,一個history知道如何去監聽瀏覽器地址欄的變化,並解析這個URL轉化為location對象,然后router使用它匹配到路由,最后正確地渲染對應的組件。

常見的3種History

BrowserHistory

它其實就是HTML5推出的歷史記錄API,可以把瀏覽器記錄當作一個棧,通過History提供的API來對瀏覽器記錄進行相應的操作。

常見的方法有下面這幾種:

  1. history.push(path, [state])
  2. history.replace(path, [state])
  3. history.go(n)
  4. history.goBack()
  5. history.goForword()

BrowserHistory是使用react-router應用推薦的history。它使用瀏覽器中的HistoryAPI用於處理URL,會創建一個像example.com/some/path這樣的真實的URL。

要使用這個History的話,必須在服務器端做好處理URL的准備。一般從下面幾個方面來考慮:

  • 處理/的請求, Django簡單配置如下:
// urls.py
from apps import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
]
  • 處理打開新頁面的URL跳轉,比如點擊某條新聞,跳轉到新聞詳情頁這樣的URL跳轉(即本文開頭的介紹)。
  • 處理每個URL的路由, 這也是最容易被忽視的一部分。當你在導航欄里點來點去,顯示是沒有問題的,就像下面這樣。

    但是當你刷新頁面的時候,你會得到這樣一個結果:

    這是由於,你的應用是單頁的,當你點擊導航欄的時候,頁面並沒有刷新,只是JavaScript去請求了后台,然后更新了前端路由和組件狀態,但是沒有刷新頁面,導致沒有去請求后台的地址。所以,必須為每個URL在后端配置路由。一般來說,只需要配置簡單的根路由就可以了,除非你需要動態更改模板index.html上的數據。

HashHistory

Hash history使用URL中的hash(#)部分去創建形如: example.com/#/path的路由。

像?_k=ckuvip沒用的URL是什么東西?
  1. 每個URL對應都會對應一個state對象,你可以在對象里存儲數據(比如當前頁面滾動到哪個位置了),但是這個數據不會出現在URL中。實際上,數據被存到了sessionStorage中。

  2. 當一個history通過應用程序的push或者replace作跳轉時,它可以在新的location里存儲location state, 而不用顯示在URL中,它類似於一個HTML中的post的表單數據。

  3. 在DOM API中,這些hash history通過window.location.hash = newHash很簡單地用於被跳轉,且不用存儲它們的location state。但我們想要每個history都能使用location.state, 因此要為每個location創建一個唯一的key, 並把它們的狀態存儲在sessionStorage中。當點擊'后退'和'前進'時,我們就會有一個機制去恢復這些location state。

MemoryHistory

Memory history不會在地址欄里被操作或讀取,這就解釋了是如何實現服務端渲染的。

BrowserHistory與HashHistory的對比

實際上,兩種History都只是人為控制路由的一種手段。

相同點:

它們都只是簡單的改變了地址欄里的URL,如果沒有刷新頁面的話,都不會向后端主動發送請求。

不同點:

當頁面刷新的時候,對於BrowserHistory, 瀏覽器會向后台發送整個URL的請求, 而對於HashHistory, 它只會請求后台的根目錄。

使用場景

  1. 不能完全是單頁應用。
  2. 必要要刷新頁面,因為這樣,才會有向后端發送請求。
  3. 需要動態改變模板里的內容,比如head里的內容。
  4. 不是特別復雜的系統。

其他

推薦閱讀: 關於如何爬取ajax應用

感覺單頁的seo,除了用SSR,就沒有其他的辦法了。

參考

react-guide

深入理解react-router


免責聲明!

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



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