你須要瞭解的React Router(hash模式)實現原理

你應該知道的單頁應用

單頁應用是什麼?

單頁Web應用(single page web application,SPA),就是隻有一張Web頁面的應用。是隻加載單個HTML頁面,並在用戶與應用程序交互時動態更新該頁面的Web應用程序。而ReactVue就是構建單頁應用的前端主流框架。css

單頁應用和多頁應用的對比

對比 單頁面應用 多頁面應用
組成 一個外殼頁面和多個頁面組件(片斷)構成 多個完整的頁面組成
資源(css,js) 可共用,只須要在外殼部分加載 須要時向服務器請求,資源獨立
首次加載 首屏加載慢 每次加載區別不大
用戶體驗 用戶體驗好,內容改變不須要從新加載整個頁面,後續服務器壓力小 頁面加載慢,每次加載須要對服務器進行請求
轉場動畫 能夠實現 沒法實現
搜索引擎優化(SEO) 效果較差,可經過SSR服務端渲染,成本較高 效果好

SPA單頁應用的原理(路由的做用)

經過監聽路由的變化,去匹配路由所對應的組件,並將組件映射到路由上。當路由改變時框架能有效地更新並正確地渲染組件。前端

你須要瞭解的HashRouter實現過程

這裏我介紹HashRouter的實現原理、過程react

路由的三大構成

React Router中有三類組件:git

  1. Router(包括HashRouter與BrowserRouter)

對應路由的兩種模式hash和historygithub

  1. route matching組件(Route)

控制路徑對應的顯示組件web

  1. navigation組件(Link)

路由切換,跳轉正則表達式

手動實現過程

以一個demo爲例api

export default class App extends Component {
  render() {
    return (
      <HashRouter>
        <Route path="/home" component={Home}></Route>
        <Route path="/user" component={User}></Route>
      </HashRouter>
    )
  }
}
ReactDOM.render(<App />,
  document.getElementById('root')
);
複製代碼

這裏Route組件得到了對應的路徑和所要展現的組件,並嵌套在Router組件中。瀏覽器

HashRouter組件

首先咱們要知道BOM的一些特性bash

BOM是一套操做瀏覽器的API,而window是BOM中的一個頂級的對象,咱們能夠經過this.props打印掛載在window下的一些信息,以掘金爲例

樣例

而HashRouter的實現就依賴於這些Api,咱們能夠經過window.location.href拿到咱們所在的url值。

固然經過這個就能夠了嗎?等等,Route做爲HashRouter的嵌套組件是怎麼拿到url路徑去匹配path的呢?

這裏React-Router採用了React16.3版本提出的api React.createContext

Context經過組件樹提供一個傳遞數據的方法

能夠解決父組件向子組件、孫子孫子...傳值,爲多組件嵌套數據傳遞提供解決方案。

//咱們經過一個context.js方法引入這個api
import React from 'react';
let { Provider, Consumer } = React.createContext()
export { Provider, Consumer }
複製代碼

而HashRouter便充當了這個生產者的角色,經過window.addEventListener('hashChange',callback)監聽hash值的變化,並傳遞給其嵌套的組件。

具體代碼以下:

import React, { Component } from 'react';
import { Provider } from './context'
// 該組件下Api提供給子組件使用
class HashRouter extends Component {
  constructor() {
    super()
    this.state = {
      location: {
        pathname: window.location.hash.slice(1) || '/'
      }
    }
  }
  // url路徑變化 改變location
  componentDidMount() {
    window.location.hash = window.location.hash || '/'
    window.addEventListener('hashchange', () => {
      this.setState({
        location: {
          ...this.state.location,
          pathname: window.location.hash.slice(1) || '/'
        }
      }, () => console.log(this.state.location))
    })
  }
  render() {
    let value = {
      location: this.state.location
    }
    return (
      <Provider value={value}>
        {
          this.props.children
        }
      </Provider>
    );
  }
}

export default HashRouter;
複製代碼

Route組件

Route組件則充當的消費者的角色,經過一個回調接收到HashRouter傳遞過來的url路徑值,並進行後面的匹配渲染組件

import React, { Component } from 'react';
import { Consumer } from './context'
const { pathToRegexp } = require("path-to-regexp");
class Route extends Component {
  render() {
    return (
      <Consumer>
        {
          state => {
            console.log(state)
            let {path, component: Component} = this.props
            let pathname = state.location.pathname
            let reg = pathToRegexp(path, [], {end: false})
            // 判斷當前path是否包含pathname
            if(pathname.match(reg)) {
              return <Component></Component>
            }
            return null
          }
        }
      </Consumer>
    );
  }
}
export default Route;
複製代碼

因爲官方實現的正則表達式較爲複雜,這裏我藉助了path-to-regexp這個插件去進行正則匹配的處理。

實現效果:

總結

本次只是簡單的模擬了下HashRouter的實現過程,對React-Router的實現原理也有了必定的認識。最後,學習的過程,重在總結,樂在分享,具體的代碼你們能夠看個人github 歡迎你們留言和我交流分享😀。

相關文章
相關標籤/搜索