Ember动态路由 #
一、动态路由概述 #
动态路由允许URL包含可变部分,用于加载特定资源。
1.1 基本语法 #
javascript
Router.map(function () {
// :post_id 是动态段
this.route('post', { path: '/posts/:post_id' });
});
1.2 URL示例 #
| URL | 动态参数 |
|---|---|
/posts/1 |
post_id = "1" |
/posts/abc |
post_id = "abc" |
/posts/hello-world |
post_id = "hello-world" |
二、定义动态路由 #
2.1 路由配置 #
javascript
// app/router.js
Router.map(function () {
this.route('posts');
this.route('post', { path: '/posts/:post_id' });
// 多个动态段
this.route('user-post', { path: '/users/:user_id/posts/:post_id' });
// 嵌套动态路由
this.route('posts', function () {
this.route('show', { path: '/:post_id' });
this.route('edit', { path: '/:post_id/edit' });
});
});
2.2 路由文件 #
javascript
// app/routes/post.js
import Route from '@ember/routing/route';
export default class PostRoute extends Route {
model(params) {
// params.post_id 包含URL中的值
return this.store.findRecord('post', params.post_id);
}
}
三、模型钩子 #
3.1 model钩子参数 #
javascript
// app/routes/posts/show.js
import Route from '@ember/routing/route';
export default class PostsShowRoute extends Route {
model(params, transition) {
// params - 动态段参数
console.log(params.post_id);
// transition - 路由过渡信息
console.log(transition.from);
console.log(transition.to);
return this.store.findRecord('post', params.post_id);
}
}
3.2 异步模型加载 #
javascript
// app/routes/post.js
import Route from '@ember/routing/route';
export default class PostRoute extends Route {
async model(params) {
const post = await this.store.findRecord('post', params.post_id);
const comments = await this.store.query('comment', {
postId: params.post_id,
});
return { post, comments };
}
}
3.3 错误处理 #
javascript
import Route from '@ember/routing/route';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
export default class PostRoute extends Route {
@service router;
async model(params) {
try {
return await this.store.findRecord('post', params.post_id);
} catch (error) {
if (error.errors?.[0]?.status === '404') {
this.router.transitionTo('not-found');
}
throw error;
}
}
@action
error(error, transition) {
console.error('路由错误:', error);
return true; // 继续冒泡
}
}
四、多个动态段 #
4.1 定义多动态段 #
javascript
Router.map(function () {
this.route('user-post', { path: '/users/:user_id/posts/:post_id' });
});
4.2 访问多参数 #
javascript
// app/routes/user-post.js
import Route from '@ember/routing/route';
export default class UserPostRoute extends Route {
model(params) {
return {
user: this.store.findRecord('user', params.user_id),
post: this.store.findRecord('post', params.post_id),
};
}
}
4.3 链接到多动态段 #
handlebars
<LinkTo
@route="user-post"
@models={{array user.id post.id}}
>
查看文章
</LinkTo>
五、序列化与反序列化 #
5.1 serialize钩子 #
当使用模型对象生成URL时,Ember调用serialize:
javascript
// app/routes/post.js
import Route from '@ember/routing/route';
export default class PostRoute extends Route {
model(params) {
return this.store.findRecord('post', params.post_id);
}
serialize(model) {
// 将模型转换为URL参数
return { post_id: model.id };
}
}
5.2 默认行为 #
默认情况下,Ember使用动态段名称查找模型属性:
javascript
// 动态段 :post_id
// Ember 自动使用 model.id
// 等价于
serialize(model) {
return { post_id: model.get('id') };
}
5.3 自定义序列化 #
javascript
export default class PostRoute extends Route {
serialize(model) {
// 使用slug代替id
return { post_id: model.slug };
}
}
六、链接到动态路由 #
6.1 使用模型 #
handlebars
{{! 传递模型对象}}
<LinkTo @route="post" @model={{@post}}>
{{@post.title}}
</LinkTo>
6.2 使用ID #
handlebars
{{! 传递ID字符串}}
<LinkTo @route="post" @model={{@post.id}}>
{{@post.title}}
</LinkTo>
6.3 多个模型 #
handlebars
{{! 使用@models数组}}
<LinkTo
@route="user-post"
@models={{array @user.id @post.id}}
>
查看文章
</LinkTo>
七、动态路由与嵌套 #
7.1 嵌套动态路由 #
javascript
Router.map(function () {
this.route('posts', function () {
this.route('show', { path: '/:post_id' }, function () {
this.route('comments');
this.route('comment', { path: '/comments/:comment_id' });
});
});
});
7.2 访问父路由模型 #
javascript
// app/routes/posts/show/comment.js
import Route from '@ember/routing/route';
export default class PostsShowCommentRoute extends Route {
model(params) {
// 获取父路由的模型
const post = this.modelFor('posts.show');
return this.store.findRecord('comment', params.comment_id, {
adapterOptions: { postId: post.id },
});
}
}
八、通配符路由 #
8.1 定义通配符路由 #
javascript
Router.map(function () {
// 匹配 /pages/任意路径
this.route('page', { path: '/pages/*path' });
});
8.2 使用通配符参数 #
javascript
// app/routes/page.js
import Route from '@ember/routing/route';
export default class PageRoute extends Route {
model(params) {
// params.path 包含完整路径
console.log(params.path); // "docs/guide/intro"
return this.store.queryRecord('page', { path: params.path });
}
}
九、最佳实践 #
9.1 参数命名 #
javascript
// 好的命名 - 清晰表达含义
this.route('post', { path: '/posts/:post_id' });
this.route('user', { path: '/users/:user_id' });
// 避免 - 模糊的命名
this.route('item', { path: '/:id' });
9.2 模型验证 #
javascript
export default class PostRoute extends Route {
@service router;
async model(params) {
// 验证ID格式
if (!/^\d+$/.test(params.post_id)) {
this.router.transitionTo('not-found');
return;
}
return this.store.findRecord('post', params.post_id);
}
}
9.3 加载状态 #
handlebars
{{! app/templates/post-loading.hbs}}
<div class="loading-spinner">
加载中...
</div>
十、总结 #
动态路由要点:
| 概念 | 说明 |
|---|---|
:param_name |
定义动态段 |
model(params) |
访问动态参数 |
serialize(model) |
模型转URL参数 |
modelFor(route) |
访问父路由模型 |
@model={{obj}} |
链接传递模型 |
动态路由是构建RESTful风格应用的关键。
最后更新:2026-03-28