context
在v5路由器组件中,使用context来获取当前正在活跃的location、 history、match
等对象。比如historyContext:
// historyContext.js
const createNamedContext = name => {
/*
createContext 函数由“mini-create-react-context” 提供,
包所作的事情就是对React.createContext的包装,在不存在时提供一个内部实现的context构建函数
*/
const context = createContext();
context.displayName = name;
return context;
};
const historyContext = createNamedContext("Router-History");
export default historyContext;
// hook.js
export function useHistory() {
// react.useContext
return useContext(HistoryContext);
}
// router.js
<HistoryContext.Provider
children={this.props.children || null}
value={this.props.history}
/>
使用的route组件:
class Route extends React.Component {
render() {
return (
<RouterContext.Consumer>
{context => {
// render
}}
</RouterContext.Consumer>
);
}
}
匹配规则
v5中的匹配规则使用正则来匹配,使用path-to-regexp
包
在定义路由时会将提供的path转成正则, 与当前location.pathname
进行匹配。
const cache = {};
/*
cache = {
key : {
regexp, keys
}
}
*/
// 最大缓存存储数。 如果超过以后的结果不会进入缓存,为此都是实时计算
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;
}
function matchPath(pathname, options = {}) {
// 去除参数初始化
// path 为使用route组件时提供的path属性
const { path, exact = false, strict = false, sensitive = false } = options;
const paths = [].concat(path);
return paths.reduce((matched, path) => {
if (!path && path !== "") return null;
// 如果匹配到return
if (matched) return matched;
// 根据path转换成正则
const { regexp, keys } = compilePath(path, {opts});
const match = regexp.exec(pathname);
// 不存在匹配的路由
if (!match) return null;
const [url, ...values] = match;
const isExact = pathname === url;
if (exact && !isExact) return null;
return {
// 使用路由组件后的props
};
// 初始化为空
}, null);
}
路由的匹配全部采用正则的形式, router里面只有Route
组件是用来定义路由的
Route对于children、 render、component
的选择:
// 如果Route组件使用时提供的children是一个react数组是不会显示的
if (Array.isArray(children) && isEmptyChildren(children)) {
children = null;
}
// ------------------------
{
props.match
? children
? typeof children === "function"
? children(props)
: children
: component
? React.createElement(component, props)
: render
? render(props)
: null
: typeof children === "function"
? children(props)
: null
}
// 大致的意思就是
function () {
// 使用pathname 和path转成正则以后匹配的结果为null 就使用children 如果不是函数就是null
if ( props.match !== null ) {
if (typeof children === “functin") {
return children(props);
}
return null;
}
if (children !== undefined) {
if (typeof children === "function") {
return children(props);
}
return children; // children为React.ReactElement类型
}
if (component !== undefined) {
return React.createElment(component, props);
}
if (render !== undefined) {
return render(props) ;
}
return null;
}
匹配的顺序优先级就是 children -> component -> render
和history的结合
react router 依赖history。 而一个应用里面应该只存在一个reactRouter根节点。 一个react应用里面也应该只存在history实例。 假如是多个,路由器信息的改变时,并不会对第二个实例产生影响
// router 组件的构造函数
function constructor(props) {
super(props);
this.state = {
location: props.history.location
};
this._isMounted = false;
this._pendingLocation = null;
if (!props.staticContext) {
// 由props传进来的history实例,选择的一种路由模式。
// 设置监听, 变化时更新state。 会导致context下, 所有的消费者更新
this.unlisten = props.history.listen(location => {
if (this._isMounted) {
this.setState({ location });
} else {
this._pendingLocation = location;
}
});
}
}
对于browserRouter模式的404问题
如果使用这种模式,出现这种问题由一下几种方式
- 开发时 webpackdevserver选项存在对于单页面应用的支持。 去瞅瞅
在测试或者其他线上环境时,比如nginx做的服务器 ```nginx server { listen 80 default_server; server_name /var/www/example.com; listen 8080;
root /wwwroot;location / {
root /wwwroot; index index.html; try_files $uri $uri/ /wwwroot/index.html;
}
root /var/www/example.com; index index.html index.htm;
location ~* .(?:manifest|appcache|html?|xml|json)$ { expires -1;
access_log logs/static.log; # I don’t usually include a static log
}
location ~* .(?:css|js)$ { try_files $uri =404; expires 1y; access_log off; add_header Cache-Control “public”; }
location ~ ^.+..+$ { try_files $uri =404; }
# 最后匹配时,继续返回index.html。 路由的信息由本资源在浏览器计算
location / { try_files $uri $uri/ /index.html; }
} ```