实现BrowserRouter
BrowserRouter:历史记录管理对象history初始化及向下传递,location变更监听
/*
用Provider包裹后,返回所包裹的children,并将history、location传下去。history主要用于link跳转,location用于比对path路径,显示正确路由
*/
import { createBrowserHistory } from "history";
const RouterContext = React.createContext();
class BrowserRouter extends Component {
constructor(props) {
super(props);
this.history = createBrowserHistory(this.props);
this.state = {
location: this.history.location
};
this.unlisten = this.history.listen(location => {
this.setState({ location });
});
}
componentWillUnmount() {
if (this.unlisten) this.unlisten();
}
render() {
return (
<RouterContext.Provider
children={this.props.children || null}
value={{
history: this.history,
location: this.state.location
}}
/>
);
}
}
实现Route
路由配置,匹配检测,内容渲染
export function Route(props) {
//接受传入的history、location
const ctx = useContext(RouterContext);
const { location } = ctx;
const { path, component, children, render } = props;
const match = matchPath(location.pathname, props);
console.log("match", match);
const matchCurrent = match && match.isExact;
//const matchCurrent = path === location.pathname;
const cmpProps = { ...ctx, match };
console.log("render", render);
if (matchCurrent && typeof children === "function") {
return children(cmpProps);
}
return (
<>
{typeof children === "function" && children(cmpProps)}
{matchCurrent && component
? React.createElement(component, cmpProps)
: null}
{matchCurrent && !component && render && render(cmpProps)}
</>
);
}
依赖:matchPath.js
import pathToRegexp from "path-to-regexp";
const cache = {};
const cacheLimit = 10000;
let cacheCount = 0;
function compilePath(path, options) {
const cacheKey = `${options.end}${options.strict}${options.sensitive}`;
const pathCache = cache[cacheKey] || (cache[cacheKey] = {});
if (pathCache[path]) return pathCache[path];
const keys = [];
const regexp = pathToRegexp(path, keys, options);
const result = { regexp, keys };
if (cacheCount < cacheLimit) {
pathCache[path] = result;
cacheCount++;
}
return result;
}
/**
* Public API for matching a URL pathname to a path.
*/
function matchPath(pathname, options = {}) {
if (typeof options === "string") options = { path: options };
const { path, exact = false, strict = false, sensitive = false } =
options;
const paths = [].concat(path);
return paths.reduce((matched, path) => {
if (!path) return null;
if (matched) return matched;
const { regexp, keys } = compilePath(path, {
end: exact,
strict,
sensitive
});
const match = regexp.exec(pathname);
if (!match) return null;
const [url, ...values] = match;
const isExact = pathname === url;
if (exact && !isExact) return null;
return {
path, // the path used to match
url: path === "/" && url === "" ? "/" : url, // the matched portion ofthe URL
isExact, // whether or not we matched exactly
params: keys.reduce((memo, key, index) => {
memo[key.name] = values[index];
return memo;
}, {})
};
}, null);
}
export default matchPath;
实现Link
Link.js: 跳转链接,处理点击事件
/*
link解析后其实是a标签,传入的props解构出 to,children(href,a标签显示内容)
返回一个a标签
跳转功能实现:a标签绑定一个click事件 ,该函数通过history.push('跳转地址')实现跳转
(this.props.to)
history通过createBrowserHistory()方法在BrowserRouter中生成,
Provider传出,Consumer接受
*/
export class Link extends Component {
//跳转事件
handleClick(event, history) {
event.preventDefault();
history.push(this.props.to);
}
render() {
const { to, children } = this.props;
return (
<RouterContext.Consumer>
{context => {
return (
<a
{...rest}
onClick={event => this.handleClick(event, context.history)}
href={to}
>
{children}
</a>
);
}}
</RouterContext.Consumer>
);
}
}