
2022 北京冬奥会开幕式
此前一直在疑惑,明明 pushState()
、replaceState()
不触发 popstate
事件,可为什么 React Router 还能挂载对应路由的组件呢?
翻了一下 history.js 源码,终于知道原因了。
源码
假设项目路由设计如下:
import { render } from 'react-dom'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { Mine, About } from './routes'
import App from './App'
const rootElement = document.getElementById('root')
render(
<BrowserRouter>
<Routes>
<Route path="/" exact element={<App />} />
<Route path="/mine" element={<Mine />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>,
rootElement
)
然后我们看下 <BrowserRouter />
的源码(react-router-dom/modules/BrowserRouter.js
),以下省略了一部分无关代码:
import React from 'react'
import { Router } from 'react-router'
import { createBrowserHistory as createHistory } from 'history'
/**
* The public API for a <Router> that uses HTML5 history.
*/
class BrowserRouter extends React.Component {
// 构建 history 对象
history = createHistory(this.props)
render() {
// 将 history 对象等传入 <Router /> 组件
return <Router history={this.history} children={this.props.children} />
}
}
// ...
export default BrowserRouter
接着我们继续看下 <Router />
组件的源码(react-router/modules/Router.js
),如下:
import React from 'react'
import HistoryContext from './HistoryContext.js'
import RouterContext from './RouterContext.js'
/**
* The public API for putting history on context.
*/
class Router extends React.Component {
static computeRootMatch(pathname) {
return { path: '/', url: '/', params: {}, isExact: pathname === '/' }
}
constructor(props) {
super(props)
this.state = {
location: props.history.location
}
// 关键点:
// 当触发 popstate 事件、
// 或主动调用 props.history.push()、props.history.replace() 方法时,
// 都会执行 history 对象的 listen 方法,使得执行 setState 强制更新当前组件
this.unlisten = props.history.listen(location => {
this.setState({ locatio