Ember嵌套路由 #
一、嵌套路由概述 #
嵌套路由允许你构建层次化的应用结构,父路由的模板包含子路由的出口。
1.1 基本概念 #
text
/posts → 父路由
/posts/new → 子路由
/posts/123 → 子路由
/posts/123/edit → 更深层子路由
1.2 路由结构图 #
text
application
├── home
├── about
└── posts
├── new
└── show
└── edit
二、定义嵌套路由 #
2.1 路由配置 #
javascript
// app/router.js
Router.map(function () {
this.route('posts', function () {
this.route('new');
this.route('show', { path: '/:post_id' });
this.route('edit', { path: '/:post_id/edit' });
});
});
2.2 生成的路由 #
| 路由名称 | URL | 文件 |
|---|---|---|
posts |
/posts |
routes/posts.js |
posts.new |
/posts/new |
routes/posts/new.js |
posts.show |
/posts/:post_id |
routes/posts/show.js |
posts.edit |
/posts/:post_id/edit |
routes/posts/edit.js |
2.3 生成嵌套路由 #
bash
# 生成posts路由
ember generate route posts
# 生成嵌套路由
ember generate route posts/new
ember generate route posts/show
三、模板嵌套 #
3.1 父路由模板 #
handlebars
{{! app/templates/posts.hbs}}
<div class="posts-layout">
<aside class="sidebar">
<h2>文章管理</h2>
<nav>
<LinkTo @route="posts.new">新建文章</LinkTo>
<LinkTo @route="posts">文章列表</LinkTo>
</nav>
</aside>
<main class="content">
{{outlet}}
</main>
</div>
3.2 子路由模板 #
handlebars
{{! app/templates/posts/index.hbs}}
<h1>文章列表</h1>
<ul>
{{#each @model as |post|}}
<li>
<LinkTo @route="posts.show" @model={{post.id}}>
{{post.title}}
</LinkTo>
</li>
{{/each}}
</ul>
handlebars
{{! app/templates/posts/new.hbs}}
<h1>新建文章</h1>
<PostForm @onSubmit={{this.createPost}} />
handlebars
{{! app/templates/posts/show.hbs}}
<h1>{{@model.title}}</h1>
<p>{{@model.body}}</p>
<LinkTo @route="posts.edit" @model={{@model.id}}>编辑</LinkTo>
四、模型继承 #
4.1 父路由模型 #
javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
export default class PostsRoute extends Route {
model() {
return this.store.findAll('post');
}
}
4.2 子路由访问父模型 #
javascript
// app/routes/posts/index.js
import Route from '@ember/routing/route';
export default class PostsIndexRoute extends Route {
// 继承父路由的模型
// 不需要定义model方法
}
handlebars
{{! app/templates/posts/index.hbs}}
{{! 直接使用父路由的模型}}
{{#each @model as |post|}}
<li>{{post.title}}</li>
{{/each}}
4.3 子路由独立模型 #
javascript
// app/routes/posts/show.js
import Route from '@ember/routing/route';
export default class PostsShowRoute extends Route {
model(params) {
return this.store.findRecord('post', params.post_id);
}
}
五、多层嵌套 #
5.1 三层嵌套 #
javascript
// app/router.js
Router.map(function () {
this.route('admin', function () {
this.route('users', function () {
this.route('show', { path: '/:user_id' });
this.route('edit', { path: '/:user_id/edit' });
});
});
});
5.2 模板结构 #
handlebars
{{! app/templates/admin.hbs}}
<div class="admin-layout">
<AdminSidebar />
{{outlet}}
</div>
handlebars
{{! app/templates/admin/users.hbs}}
<div class="users-layout">
<UsersList @users={{@model}} />
{{outlet}}
</div>
handlebars
{{! app/templates/admin/users/show.hbs}}
<UserDetail @user={{@model}} />
5.3 导航链接 #
handlebars
{{! 三层嵌套链接}}
<LinkTo @route="admin.users.show" @model={{user.id}}>
查看用户
</LinkTo>
六、动态嵌套 #
6.1 动态段嵌套 #
javascript
Router.map(function () {
this.route('categories', function () {
this.route('category', { path: '/:category_id', resetNamespace: true }, function () {
this.route('products');
this.route('product', { path: '/products/:product_id' });
});
});
});
6.2 模型钩子链 #
javascript
// app/routes/category.js
import Route from '@ember/routing/route';
export default class CategoryRoute extends Route {
model(params) {
return this.store.findRecord('category', params.category_id);
}
}
javascript
// app/routes/category/products.js
import Route from '@ember/routing/route';
export default class CategoryProductsRoute extends Route {
model() {
// 访问父路由模型
const category = this.modelFor('category');
return this.store.query('product', { categoryId: category.id });
}
}
七、resetNamespace选项 #
7.1 默认命名空间 #
javascript
Router.map(function () {
this.route('admin', function () {
this.route('users'); // 路由名: admin.users
});
});
7.2 重置命名空间 #
javascript
Router.map(function () {
this.route('admin', function () {
this.route('users', { resetNamespace: true }); // 路由名: users
});
});
八、最佳实践 #
8.1 合理的嵌套层级 #
javascript
// 好的做法 - 2-3层嵌套
Router.map(function () {
this.route('posts', function () {
this.route('show', { path: '/:post_id' });
});
});
// 避免 - 过深嵌套
Router.map(function () {
this.route('a', function () {
this.route('b', function () {
this.route('c', function () {
this.route('d', function () {
// 太深了
});
});
});
});
});
8.2 共享布局 #
handlebars
{{! 父路由模板提供共享布局}}
<div class="app-layout">
<Sidebar />
<main>
{{outlet}}
</main>
</div>
8.3 模型复用 #
javascript
// 父路由加载共享数据
export default class AdminRoute extends Route {
model() {
return {
users: this.store.findAll('user'),
settings: this.store.findRecord('settings', 'current'),
};
}
}
九、总结 #
嵌套路由要点:
| 概念 | 说明 |
|---|---|
function() { } |
定义嵌套路由 |
{{outlet}} |
渲染子路由模板 |
modelFor() |
访问父路由模型 |
resetNamespace |
重置命名空间 |
合理使用嵌套路由可以构建清晰的应用结构。
最后更新:2026-03-28