Ember路由基础 #
一、路由概述 #
Ember的路由系统是框架的核心特性之一,它将URL映射到应用状态,实现了URL驱动的应用架构。
1.1 路由的作用 #
- 将URL映射到路由处理器
- 加载模型数据
- 渲染对应模板
- 管理应用状态
1.2 路由架构 #
text
URL → Router → Route → Model → Controller → Template
二、路由配置 #
2.1 基本配置 #
javascript
// app/router.js
import EmberRouter from '@ember/routing/router';
import config from 'my-app/config/environment';
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
}
Router.map(function () {
this.route('home', { path: '/' });
this.route('about');
this.route('contact');
});
2.2 路由选项 #
javascript
Router.map(function () {
// 基本路由
this.route('about');
// 自定义路径
this.route('users', { path: '/people' });
// 重定向路径
this.route('blog', { path: '/posts' });
// 带参数的路由
this.route('post', { path: '/posts/:post_id' });
});
2.3 生成路由 #
bash
# 生成路由
ember generate route about
# 生成带嵌套路由
ember generate route posts/show
三、路由文件 #
3.1 基本路由 #
javascript
// app/routes/about.js
import Route from '@ember/routing/route';
export default class AboutRoute extends Route {
}
3.2 加载模型 #
javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
export default class PostsRoute extends Route {
async model() {
const response = await fetch('/api/posts');
return response.json();
}
}
3.3 使用Ember Data #
javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
export default class PostsRoute extends Route {
model() {
return this.store.findAll('post');
}
}
3.4 加载单个模型 #
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);
}
}
四、路由钩子 #
4.1 钩子执行顺序 #
javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
export default class PostsRoute extends Route {
beforeModel(transition) {
// 1. 模型加载前执行
console.log('beforeModel');
}
async model(params, transition) {
// 2. 加载模型
console.log('model');
return this.store.findAll('post');
}
afterModel(resolvedModel, transition) {
// 3. 模型加载后执行
console.log('afterModel');
}
setupController(controller, model) {
// 4. 设置控制器
super.setupController(controller, model);
console.log('setupController');
}
renderTemplate(controller, model) {
// 5. 渲染模板(可选覆盖)
console.log('renderTemplate');
}
activate() {
// 6. 路由激活
console.log('activate');
}
deactivate() {
// 路由离开时
console.log('deactivate');
}
}
4.2 beforeModel #
用于权限检查、重定向:
javascript
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class AdminRoute extends Route {
@service session;
@service router;
beforeModel(transition) {
if (!this.session.isAuthenticated) {
// 保存目标URL
this.session.set('attemptedTransition', transition);
// 重定向到登录页
this.router.transitionTo('login');
}
}
}
4.3 afterModel #
用于数据验证、条件重定向:
javascript
import Route from '@ember/routing/route';
export default class PostEditRoute extends Route {
@service router;
afterModel(post) {
if (post.isPublished) {
// 已发布的文章不能编辑,重定向到查看页
this.router.transitionTo('posts.show', post.id);
}
}
}
五、导航链接 #
5.1 LinkTo组件 #
handlebars
{{! 基本链接}}
<LinkTo @route="about">关于我们</LinkTo>
{{! 带参数的链接}}
<LinkTo @route="posts.show" @model={{@post.id}}>
查看文章
</LinkTo>
{{! 带查询参数}}
<LinkTo @route="posts" @query={{hash page=1 sort="date"}}>
文章列表
</LinkTo>
{{! 多个模型参数}}
<LinkTo @route="posts.comments.show" @models={{array @post.id @comment.id}}>
查看评论
</LinkTo>
5.2 LinkTo属性 #
handlebars
<LinkTo
@route="posts"
class="nav-link"
id="posts-link"
disabled={{this.isDisabled}}
>
文章
</LinkTo>
5.3 激活状态 #
handlebars
{{! 激活时自动添加 active 类}}
<LinkTo @route="home" class="nav-link">
首页
</LinkTo>
{{! 自定义激活类名}}
<LinkTo @route="home" @activeClass="current">
首页
</LinkTo>
5.4 编程式导航 #
javascript
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class PostsController extends Controller {
@service router;
@action
goToPost(post) {
this.router.transitionTo('posts.show', post.id);
}
@action
goToPage(page) {
this.router.transitionTo({ queryParams: { page } });
}
}
六、查询参数 #
6.1 定义查询参数 #
javascript
// app/controllers/posts.js
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';
export default class PostsController extends Controller {
queryParams = ['page', 'sort', 'category'];
@tracked page = 1;
@tracked sort = 'date';
@tracked category = null;
}
6.2 使用查询参数 #
handlebars
{{! 链接带查询参数}}
<LinkTo @route="posts" @query={{hash page=2 sort="title"}}>
第2页
</LinkTo>
{{! 更新查询参数}}
<Input @value={{this.category}} {{on "input" this.updateCategory}} />
6.3 查询参数选项 #
javascript
// app/controllers/posts.js
import Controller from '@ember/controller';
export default class PostsController extends Controller {
queryParams = [
{ page: { type: 'number' } },
{ sort: { replace: true } },
{ category: { as: 'cat' } },
];
page = 1;
sort = 'date';
category = null;
}
6.4 查询参数刷新 #
javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
export default class PostsRoute extends Route {
queryParams = {
page: { refreshModel: true },
sort: { refreshModel: true },
category: { refreshModel: true },
};
model(params) {
return this.store.query('post', {
page: params.page,
sort: params.sort,
category: params.category,
});
}
}
七、路由模板 #
7.1 基本模板 #
handlebars
{{! app/templates/posts.hbs}}
<h1>文章列表</h1>
<ul>
{{#each @model as |post|}}
<li>
<LinkTo @route="posts.show" @model={{post.id}}>
{{post.title}}
</LinkTo>
</li>
{{/each}}
</ul>
{{outlet}}
7.2 应用模板 #
handlebars
{{! app/templates/application.hbs}}
<header>
<nav>
<LinkTo @route="home">首页</LinkTo>
<LinkTo @route="posts">文章</LinkTo>
<LinkTo @route="about">关于</LinkTo>
</nav>
</header>
<main>
{{outlet}}
</main>
<footer>
<p>© 2024 My App</p>
</footer>
八、路由错误处理 #
8.1 错误子路由 #
javascript
// app/router.js
Router.map(function () {
this.route('posts', function () {
this.route('show', { path: '/:post_id' });
this.route('error', { path: '/error' });
});
});
8.2 错误处理 #
javascript
// app/routes/posts/show.js
import Route from '@ember/routing/route';
import { action } from '@ember/object';
export default class PostsShowRoute extends Route {
model(params) {
return this.store.findRecord('post', params.post_id);
}
@action
error(error, transition) {
if (error.status === 404) {
this.router.transitionTo('posts.error');
}
}
}
8.3 加载子状态 #
javascript
// app/routes/posts.js
import Route from '@ember/routing/route';
export default class PostsRoute extends Route {
async model() {
await new Promise((resolve) => setTimeout(resolve, 2000));
return this.store.findAll('post');
}
}
handlebars
{{! app/templates/posts-loading.hbs}}
<div class="loading">
加载中...
</div>
九、路由最佳实践 #
9.1 路由命名 #
javascript
// 好的命名
this.route('posts');
this.route('posts/show');
this.route('posts/new');
// 避免
this.route('p');
this.route('viewPost');
9.2 模型加载 #
javascript
// 好的做法 - 在路由中加载模型
export default class PostsRoute extends Route {
model() {
return this.store.findAll('post');
}
}
// 避免 - 在控制器中加载数据
9.3 权限控制 #
javascript
// 在beforeModel中统一处理
export default class AdminRoute extends Route {
@service session;
beforeModel(transition) {
if (!this.session.isAdmin) {
throw new Error('Unauthorized');
}
}
}
十、总结 #
Ember路由核心概念:
| 概念 | 说明 |
|---|---|
| Router | URL到路由的映射 |
| Route | 加载模型、处理逻辑 |
| LinkTo | 声明式导航链接 |
| queryParams | URL查询参数 |
| {{outlet}} | 渲染嵌套路由 |
掌握路由系统是构建Ember应用的基础。
最后更新:2026-03-28