更多內容在我的語雀:www.yuque.com/xiezhongfu/…css
咱們先區分下「頁面」這個詞:html
<Route path="/foo" componet={Foo} />
<Route path="/bar" componet={Bar} /> // Foo 頁面組件 和 Bar 頁面組件都是靜態化組件(這兩個組件裏面的數據都是不變的) .... <Link to="/foo">foo</Link> <Link to="/bar#test">bar</Link> 複製代碼
單頁面應用,在同一個頁面使用 hash 跳轉到頁面不一樣位置(錨點跳轉)。此功能是不正常的。
還能夠看看前人發的 issue 參見:github.com/ReactTraini…。針對這個問題也有相應解決方案:react
順便說一下,scrollIntoView 和 scrollTo 能夠設置平滑滾動;在滾動區域 css 設置 scroll-behavior 也能夠平滑滾動,粗暴一點咱們給 * 設置 scroll-behavior 吧。git
讓咱們在看下這段代碼github
<Route path="/foo" componet={Foo} />
<Route path="/bar" componet={Bar} /> // Foo 頁面組件 和 Bar 頁面組件都是靜態化組件(這兩個組件裏面的數據都是不變的) .... <Link to="/foo">foo</Link> <Link to="/bar#test">bar</Link> 複製代碼
在不一樣的頁面跳轉不一樣錨點,history 有更新,初始化渲染組件,功能正常。
在同一個頁面跳轉不一樣錨點,history 有更新,從新渲染原組件,功能不正常。
原生方式下,就算在同一個頁面的不一樣錨點間跳轉,功能正常。web
場景 A
假設咱們這樣路由了 2 個組件,咱們先點擊了 foo,渲染了 Foo
組件。而後咱們點擊 bar ,pathname 從 /foo
路由到 /bar
。Bar
組件會初始化渲染,頁面效果是直接定位到了 Bar 組件渲染的頁面中的 test 錨點。
場景 B
假設在 Bar 頁面內有一個本身頁面內的錨點,咱們點擊它,雖然這個錨點就在同一個頁面,錨點跳轉失效。效果是 url 上看到錨點變了,可是沒跳到對應的新錨點。
由於使用了 Link
組件,在 Bar 頁面內點擊了一個新的錨點,Link
組件內使用了 push 或者 replace 產生了新的 window.history 記錄,這個新 window.history 的 pathname 和之前同樣,只是有新的 hash。而後就去 Route
裏去找匹配的組件,發現匹配到了 Bar
組件 。由於已經渲染過了,那就從新渲染吧。
由於 protocol,port,pathname 都沒變,只是 hash 變了,瀏覽器認爲這是老頁面上的一個新錨點,那就在老頁面上跳轉吧。可是頁面組件若是在從新渲染,也許在瀏覽器剛想要跳新錨點的時候重現渲染致使錨點沒了,跳轉失敗。chrome
咱們總結下這個過程:
在 react 應用中的錨點跳轉的實現方案是:Link
組件。跳轉新頁面錨點成功,可是跳轉同頁面錨點失敗。
Link
組件默認行爲包括調用 history 庫的 push 或 replace,組件會初始化渲染或者從新渲染。渲染的過程是 js 對象轉爲 html 的過程。若是這個過程還沒結束,html 還沒及時出現,錨點功能就失效了。
。咱們知道錨點能成功,必定是瀏覽器在跳轉的時候有 html 節點上有對應的 id 或 name(好比:a 標籤可使用 name 用來標記別人能夠跳到它這),若是沒有那就跳轉失敗。瀏覽器
以上推理都是根據經驗猜想,咱們懷疑:
場景 A 在由於是新頁面,在瀏覽器跳轉錨點前 react 組件初始化渲染已經完成, html 已經有對應錨點了。
場景 B 因爲是同一個頁面,在瀏覽器跳轉錨點前 react 組件從新渲染還沒完成,跳轉失敗了。
那瀏覽器究竟是怎麼執行錨點跳轉的呢?開始查材料......react-router
咱們先看標準文檔是怎麼解釋錨點的:html.spec.whatwg.org/multipage/b…。oop
在看 chrome 是怎麼處理的:cs.chromium.org/chromium/sr… (這是巢鵬大佬的解答)
咱們再來看回顧下遇到的問題:
Link
組件新跳轉帶錨點的頁面,組件初始化渲染,錨點功能正常Link
組件在同一個頁面跳轉新錨點,組件從新渲染,錨點功能異常從 chrome 的實現和咱們遇到的問題中能體會出關鍵在因而不是新頁面。整個過程說得比較繞,指望是表達清楚了。
在這個過程當中還遇到一些老知識,複習下吧: