Ember服务基础 #
一、服务概述 #
Service是Ember中的单例对象,用于在整个应用中共享功能和状态。
1.1 服务特点 #
- 单例模式:整个应用只有一个实例
- 依赖注入:通过装饰器注入到需要的地方
- 生命周期:与应用生命周期一致
- 响应式:支持@tracked追踪
1.2 常见用途 #
| 用途 | 示例 |
|---|---|
| 状态管理 | 用户登录状态、主题设置 |
| API通信 | HTTP请求封装 |
| 本地存储 | localStorage封装 |
| 工具函数 | 日期格式化、验证 |
二、创建服务 #
2.1 生成服务 #
bash
ember generate service session
ember generate service theme
ember generate service api
2.2 基本服务 #
javascript
// app/services/session.js
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class SessionService extends Service {
@tracked isAuthenticated = false;
@tracked currentUser = null;
login(user) {
this.isAuthenticated = true;
this.currentUser = user;
}
logout() {
this.isAuthenticated = false;
this.currentUser = null;
}
}
三、注入服务 #
3.1 在组件中注入 #
javascript
// app/components/user-menu.js
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
export default class UserMenuComponent extends Component {
@service session;
@action
logout() {
this.session.logout();
}
}
handlebars
{{! app/components/user-menu.hbs}}
{{#if this.session.isAuthenticated}}
<p>欢迎,{{this.session.currentUser.name}}</p>
<button {{on "click" this.logout}}>退出</button>
{{else}}
<LinkTo @route="login">登录</LinkTo>
{{/if}}
3.2 在路由中注入 #
javascript
// app/routes/admin.js
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) {
this.router.transitionTo('login');
}
}
}
3.3 在控制器中注入 #
javascript
// app/controllers/posts.js
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class PostsController extends Controller {
@service store;
@service session;
@tracked posts = [];
@action
async loadPosts() {
this.posts = await this.store.findAll('post');
}
}
3.4 在其他服务中注入 #
javascript
// app/services/api.js
import Service from '@ember/service';
import { inject as service } from '@ember/service';
export default class ApiService extends Service {
@service session;
async request(url, options = {}) {
const headers = {
'Content-Type': 'application/json',
...options.headers,
};
if (this.session.isAuthenticated) {
headers.Authorization = `Bearer ${this.session.token}`;
}
const response = await fetch(url, {
...options,
headers,
});
return response.json();
}
}
四、服务属性 #
4.1 追踪属性 #
javascript
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class ThemeService extends Service {
@tracked current = 'light';
get isDark() {
return this.current === 'dark';
}
toggle() {
this.current = this.isDark ? 'light' : 'dark';
}
}
4.2 计算属性 #
javascript
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class CartService extends Service {
@tracked items = [];
get total() {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}
get itemCount() {
return this.items.reduce((sum, item) => sum + item.quantity, 0);
}
get isEmpty() {
return this.items.length === 0;
}
}
五、服务方法 #
5.1 基本方法 #
javascript
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class NotificationService extends Service {
@tracked notifications = [];
add(message, type = 'info') {
const notification = {
id: Date.now(),
message,
type,
timestamp: new Date(),
};
this.notifications = [...this.notifications, notification];
return notification;
}
remove(id) {
this.notifications = this.notifications.filter((n) => n.id !== id);
}
clear() {
this.notifications = [];
}
success(message) {
return this.add(message, 'success');
}
error(message) {
return this.add(message, 'error');
}
warning(message) {
return this.add(message, 'warning');
}
}
5.2 异步方法 #
javascript
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class AuthService extends Service {
@tracked isAuthenticated = false;
@tracked currentUser = null;
@tracked isLoading = false;
async login(email, password) {
this.isLoading = true;
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
throw new Error('登录失败');
}
const user = await response.json();
this.currentUser = user;
this.isAuthenticated = true;
return user;
} finally {
this.isLoading = false;
}
}
async logout() {
await fetch('/api/logout', { method: 'POST' });
this.currentUser = null;
this.isAuthenticated = false;
}
}
六、服务生命周期 #
6.1 构造函数 #
javascript
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class StorageService extends Service {
@tracked data = {};
constructor() {
super(...arguments);
this.loadFromStorage();
}
loadFromStorage() {
const stored = localStorage.getItem('app_data');
if (stored) {
this.data = JSON.parse(stored);
}
}
saveToStorage() {
localStorage.setItem('app_data', JSON.stringify(this.data));
}
set(key, value) {
this.data = { ...this.data, [key]: value };
this.saveToStorage();
}
get(key) {
return this.data[key];
}
willDestroy() {
this.saveToStorage();
super.willDestroy(...arguments);
}
}
七、服务别名 #
7.1 使用别名 #
javascript
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
export default class MyComponent extends Component {
@service('session') auth; // 使用别名
get isLoggedIn() {
return this.auth.isAuthenticated;
}
}
八、最佳实践 #
8.1 单一职责 #
javascript
// 好的做法 - 单一职责
export default class SessionService extends Service {
// 只处理用户会话
}
export default class ThemeService extends Service {
// 只处理主题
}
// 避免 - 职责过多
export default class AppService extends Service {
// 处理会话、主题、通知、购物车...
}
8.2 命名约定 #
javascript
// 好的命名
@service session;
@service theme;
@service cart;
@service notifications;
// 避免
@service s;
@service t;
8.3 文档化 #
javascript
/**
* Session服务
*
* 管理用户会话状态
*
* @property {boolean} isAuthenticated - 是否已认证
* @property {User|null} currentUser - 当前用户
* @method login - 登录
* @method logout - 登出
*/
export default class SessionService extends Service {
}
九、总结 #
服务核心概念:
| 概念 | 说明 |
|---|---|
| 单例 | 应用唯一实例 |
| @inject | 依赖注入 |
| @tracked | 响应式状态 |
| 生命周期 | constructor/willDestroy |
服务是Ember应用中共享状态和功能的核心机制。
最后更新:2026-03-28