第一次写关于Meteor的中文技术文,如果有些术语的翻译不妥请指正。
浏览器端路由概述
所谓浏览器端路由,即Client-side Router。那么浏览器端的路由有什么不同呢?我们在下面简称CR。
- 在客户端(浏览器)上执行。CR在浏览器上执行,调用HTML5 API来改变地址栏里的地址。
- 无刷新。链接等跳转的东西由CR接管,站内链接直接调用JS代码重新渲染页面,站外才直接跳出。
- 服务器不考虑路径。URL的解析也交给CR,服务器不处理URL,不管什么URL都返回带CR的HTML页面。
iron:router V.S. meteorhacks:flow-router
(下面iron:router
简称IR,meteorhacks:flow-router
简称FR)
IR是一个完整的路由,写出来的代码像极了服务器端路由。同时它还封装了页面渲染、发布/订阅。如果你要用Meteor做一个比较传统的app,完全可以采用它。但是它的缺陷,就是灵活度不高。
钩子
处理一些切换动画或者会话变量(Sessions)设置时就需要各种名目繁多的钩子(hooks)。下面是摘自IR文档的钩子列表:
Available Hook Methods
-
onRun: Called when the route is first run. It is not called again if the
route reruns because of a computation invalidation. This makes it a good
candidate for things like analytics where you want be sure the hook only runs once. Note that this hook won't run again if the route is reloaded via hot code push. -
onRerun: Called if the route reruns because its computation is
invalidated. -
onBeforeAction: Called before the route or "action" function is run. These
hooks behave specially. If you want to continue calling the next function you
must callthis.next()
. If you don't, downstream onBeforeAction hooks and
your action function will not be called. -
onAfterAction: Called after your route/action function has run or had a
chance to run. These hooks behave like normal hooks and you don't need to call
this.next()
to move from one to the next. -
onStop: Called when the route is stopped, typically right before a new
route is run.
泥垢啊!我就写个简单的切换动画,我就得用两个钩子!onBeforeAction
用于显示载入动画或者去除原页面内容,onAfterAction
用于切入动画……
让我们来看看在FR里我们如何处理它:
FlowRouter.route('/', {
action: function(params) {
doSthBeforeRendering();
render('xxx');
doSthAfterRendering();
}
});
其中的render()
只是个例子,FR并不支持渲染模版——FR的一个原则就是只做真正的路由,至于模版渲染,交给你自己来完成。当然,你也可以通过另一个包meteorhacks:flow-layout
简化你的模版渲染。这个包不在这篇文章的讨论范围内。
上面的代码非常清晰,意思就是遇上/
URL请求就做action
。
订阅
IR包下了做订阅要的活,但是只在订阅同步完成后才渲染页面,就不能分区域延迟加载了。这对于用户体验来讲也是种损失。FR不等待订阅,而是注册订阅:
FlowRouter.route('/blog/:postId', {
subscriptions: function(params) {
console.log("subscribe and register this subscription as 'myPost'");
this.register('myPost', Meteor.subscribe('blogPost', params.postId));
},
action: function(params) {
console.log("Yeah! We are on the post:", params.postId);
}
});
并且提供了几个helper来检测订阅的情况。这样就可以做到分区域延迟加载了:
好棒!这样就不用自己在IR的一堆钩子里模拟了!
FR还支持分组,比如:
//Ideapad.
P.routes.ideapad = FlowRouter.group({
prefix: '/ideapad'
});
P.routes.ideapad.route('/', {
action: function() {
//xxx
}
});
P.routes.ideapad.route('/new', {
action: function() {
//xxx
}
});
P.routes.ideapad.route('/:_id', {
subscriptions: function(params) {
this.register('ideapadContent', P.subs.subscribe('ideapadContent', params._id));
},
action: function(params) { //We just fetch the data in template helpers
//xxx
},
});
而且还支持多层嵌套分组。
结论
你当然可以继续用着IR,因为上手非常容易。对用户体验要求不高并且不大介意有一堆多出来的用不着的代码,那尽可继续用着它。不过IR太臃肿,又因为IR的路径处理是响应性的,经常出现一些无限执行钩子的现象,甚至一些看起来很无关的小事都有可能导致无限执行钩子(比如import不存在的html)。这给调试也带来很大的麻烦。我建议诸君还是早日投靠FR大法,自己多写的内容也就是几行而已。感谢Arunoda的努力,使我们除了IR以外有了一个成熟的选择。
本文引用了IronRouter、FlowRouter的部分示例代码、Piet项目部分非关键代码、MeteorHacks的GIF图片。除FR分组的示例代码外,其他代码和图片均可遵守各自的协议使用。FR分组的示例代码来自Piet项目。Piet项目并不开源,此处引用已做处理,不允许将该代码用于任何未经授权的用途。