一、路由用法
1.安裝路由庫
npm i react-router-dom
2.引入
import React from "react"; import ReactDOM from "react-dom"; import {HashRouter as Router, Route} from 'react-router-dom' //路由庫
import Home from './components/Home' import User from './components/User' import Profile from './components/Profile'
/** * Router是路由容器 * Route是路由規則,一個Route代表一個路由規則 * path 代表路徑 component代表要渲染的組件 */ ReactDOM.render( <Router>
<Route path="/" component={Home}></Route>
<Route path="/user" component={User}></Route>
<Route path="/profile" component={Profile}></Route>
</Router>,document.getElementById('root')
)
Home組件 Home.js
import React from "react"; export default function(props) { console.log(props) return ( <div>Home</div>
) }
Profile組件 Profile.js
import React from "react"; export default function(props) { return ( <div>Profile</div>
) }
User組件 User.js
import React from "react"; export default function(props) { return ( <div>User</div>
) }
根據路徑渲染組件component
可以看到不管是渲染哪個組件,path=“/”的這個路徑對應的組件都會渲染出來,這是因為react在渲染的時候會匹配只要是/結尾的,都會渲染出來,要想過濾掉這個home,可以添加一個exact,這個時候在渲染別的組件的時候就不會再渲染了。
ReactDOM.render( <Router>
<Route exact path="/" component={Home}></Route>
<Route path="/user" component={User}></Route>
<Route path="/profile" component={Profile}></Route>
</Router>,document.getElementById('root')
)
打印props屬性:
- length表示當前的路由條目,只有前進的時候才會增加條目,后退並不會增加條目數
- action表示當前路徑是通過push來的還是pop來的
- block表示阻止跳轉,prompt表示彈出提示是否進行跳轉。
- createHref,location對象轉換成字符串或者將字符產轉換成location對象。
- go 表示跳幾步,goBack表示向后跳,goForward表示向前跳
- location 代表當前的路徑對象
接下來實現一個自己的路由庫。
React的路由庫是借助window.history對象實現的,所以我們今天的實現是需要基於window.history來實現。
先來了解一下history:
history 有三種實現方式:
1、createBrowserHistory:用於支持 HTML5 歷史記錄 API 的現代 Web 瀏覽器(請參閱跨瀏覽器兼容性)
2、createHashHistory:用於舊版Web瀏覽器
3、createMemoryHistory:用於node環境下。
history對象的內部基礎方法:
return { listenBefore, // 內部的hook機制,可以在location發生變化前執行某些行為,AOP的實現
listen, // location發生改變時觸發回調
transitionTo, // 執行location的改變
push, // 改變location
replace, go, goBack, goForward, createKey, // 創建location的key,用於唯一標示該location,是隨機生成的
createPath, createHref, createLocation, // 創建location
}
以上三種實現方法,都是在history內部方法的基礎上進行了改寫(覆蓋)。
二、路由實現
1、createBrowserHistory
export default function createBrowserHistory(){ const globalHistory = window.history const initialLocation = { pathname:window.location.pathname, state:globalHistory.state //歷史對象上的狀態
} const history = { length: globalHistory.length, action: "POP", //當前路徑是通過push、pop、replace哪個方法來的
location: initialLocation, createHref, //通過location得到一個字符串
push, // 改變location 添加新條目
replace, //新路徑替換當前條目
go, goBack, goForward, block, listen //監聽路由變化
}; retrun history }
整體代碼是這樣的,下面主要看看里面每個方法的實現
1.1createHref()
這個方法是根據當前history對象返回一個轉換后的字符串
function createHref (location) { return location.protocol + location.host + location.pathname + location.search + location.hash }
1.2.push方法
用於路由跳轉的方法
function setState (state) { //Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
Object.assign(history, state) //把action,location覆蓋到對應的history對象的值
history.length = globalHistory.length } function push (path,state) { //path新的路徑 state 新的狀態
const action = 'PUSH' //改變action的值
const location = {push, state} //新的對象
globalHistory.pushState(state,null,path) //調用history的pushState方法實現跳轉
setState({action,location}) //setState這個setState並不是react自帶的setState方法,
}
1.3.listen方法
監聽路由的變化
function setState (state) { //Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
Object.assign(history, state) //把action,location覆蓋到對應的history對象的值
history.length = globalHistory.length listeners.forEach(listener => listener()) } var listeners= [] function listen (listener) { listeners.push(listener) }
1.4.replace 方法
替換當前路徑
function replace (path,state) { //path新的路徑 state 新的狀態
const action = 'REPLACE' //改變action的值
const location = {push, state} //新的對象
globalHistory.replaceState(state,null,path) //調用history的replaceState方法實現跳轉
setState({action,location}) //setState這個setState並不是react自帶的setState方法,
} function setState (state) { //Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
Object.assign(history, state) //把action,location覆蓋到對應的history對象的值
history.length = globalHistory.length }
5.go() goBack() goForward()
go表示跳轉幾步
function go (n) { globalHistory.go(n) //調用原始的go方法
} function goBack () { go(-1) } function goForward () { go(1) }
6.block
表示詢問是否跳轉
let isBlock //是否跳轉
function block (prompt) { isBlock = prompt }
全部實現:
export default function createBrowserHistory(){ const globalHistory = window.history const initialLocation = { pathname:window.location.pathname, state:globalHistory.state //歷史對象上的狀態
} function createHref (location) { return location.protocol + location.host + location.pathname + location.search + location.hash } function setState (state) { //Object.assign() 方法用於將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
Object.assign(history, state) //把action,location覆蓋到對應的history對象的值
history.length = globalHistory.length listeners.forEach(listener => listener()) } var listeners= [] function listen (listener) { listeners.push(listener) } function push (path,state) { //path新的路徑 state 新的狀態
const action = 'PUSH' //改變action的值
const location = {push, state} //新的對象
globalHistory.pushState(state,null,path) //調用history的pushState方法實現跳轉
setState({action,location}) //setState這個setState並不是react自帶的setState方法,
} function replace (path,state) { //path新的路徑 state 新的狀態
const action = 'REPLACE' //改變action的值
const location = {push, state} //新的對象
globalHistory.replaceState(state,null,path) //調用history的replaceState方法實現跳轉
setState({action,location}) //setState這個setState並不是react自帶的setState方法,
} function go (n) { globalHistory.go(n) //調用原始的go方法
} function goBack () { go(-1) } function goForward () { go(1) } let isBlock //是否跳轉
function block (prompt) { isBlock = prompt } const history = { length: globalHistory.length, action: "POP", //當前路徑是通過push、pop、replace哪個方法來的
location: initialLocation, createHref, //通過location得到一個字符串
push, // 改變location 添加新條目
replace, //新路徑替換當前條目
go, goBack, goForward, block, listen //監聽路由變化
}; }
總結:
react-router是在history基礎上實現了URL與UI的同步,其中URL對應的是location,UI對應的是react component。
下篇我們實現一下HashRouter。