[更新]
在处理了一堆 React/flux 应用程序之后,我得出的结论是,我更喜欢将路由单独处理并与 Flux 正交。策略是 URL/路由应该确定哪些组件被挂载,组件根据路由参数和其他应用程序状态从存储中请求数据。
[原答案]
我在最近的一个项目中尝试使用 Flux 时采用的一种方法是让路由层只是另一个存储。这意味着所有更改 URL 的链接实际上都会通过调度程序触发一个动作,请求更新路由。ARouteStore
通过在浏览器中设置 URL 并设置一些内部数据(通过route-recognizer)来响应此调度,以便视图可以在change
从商店触发事件时查询新的路由数据。
对我来说,一个不明显的部分是如何确保 URL 更改触发操作;我最终创建了一个 mixin 来为我管理这个(注意:这不是 100% 健壮的,但适用于我正在使用的应用程序;您可能需要进行修改以满足您的需要)。
// Mix-in to the top-level component to capture `click`
// events on all links and turn them into action dispatches;
// also manage HTML5 history via pushState/popState
var RoutingMixin = {
componentDidMount: function() {
// Some browsers have some weirdness with firing an extra 'popState'
// right when the page loads
var firstPopState = true;
// Intercept all bubbled click events on the app's element
this.getDOMNode().addEventListener('click', this._handleRouteClick);
window.onpopstate = function(e) {
if (firstPopState) {
firstPopState = false;
return;
}
var path = document.location.toString().replace(document.location.origin, '');
this.handleRouteChange(path, true);
}.bind(this);
},
componentWillUnmount: function() {
this.getDOMNode().removeEventListener('click', this._handleRouteClick);
window.onpopstate = null;
},
_handleRouteClick: function(e) {
var target = e.target;
// figure out if we clicked on an `a` tag
while(target && target.tagName !== 'A') {
target = target.parentNode;
}
if (!target) return;
// if the user was holding a modifier key, don't intercept
if (!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) {
e.preventDefault();
var href = target.attributes.href.value;
this.handleRouteChange(href, false);
}
}
};
它会这样使用:
var ApplicationView = React.createClass({
mixins: [RoutingMixin],
handleRouteChange: function(newUrl, fromHistory) {
this.dispatcher.dispatch(RouteActions.changeUrl(newUrl, fromHistory));
},
// ...
});
商店中的处理程序可能类似于:
RouteStore.prototype.handleChangeUrl = function(href, skipHistory) {
var isFullUrl = function(url) {
return url.indexOf('http://') === 0 || url.indexOf('https://') === 0;
}
// links with a protocol simply change the location
if (isFullUrl(href)) {
document.location = href;
} else {
// this._router is a route-recognizer instance
var results = this._router.recognize(href);
if (results && results.length) {
var route = results[0].handler(href, results[0].params);
this.currentRoute = route;
if (!skipHistory) history.pushState(href, '', href);
}
this.emit("change");
}
}