编辑页面

介绍


该指南的这一部分描述了ember的基本特征 数据,一组强大的工具 用于格式化请求,规范化响应,有效 管理数据的本地缓存。

Ember.js本身与任何类型的后端配合使用:休息, JSON:API,GraphQL或其他任何东西。 要了解其他方法来处理数据和查找扩展, 查看指南制作API请求, look for plugins on ember观察者和搜索 对于社区制作的教程。

什么是ember数据模型?

在Ember数据中,模型是代表底层数据的对象 您的申请给用户提供。 注意,Ember数据模型是不同的概念 model路线上的方法, 虽然它们共享同名。

不同的应用可能很有 不同的模型,取决于他们试图解决的问题。 例如,照片共享应用程序可能有一个Photo model to represent a particular photo, and a PhotoAlbum that 代表一组照片。相比之下,在线购物应用程序将会 probably have different models, like ShoppingCart, Invoice, or LineItem.

模特往往是执着的。这意味着用户不期望 在关闭浏览器窗口时,模型数据将丢失。确保;确定 如果用户对模型进行更改,则无需丢失数据,则需要存储 在某处的模型数据不会丢失。

通常,大多数模型都被加载并保存到使用a的服务器 数据库存储数据。通常你会发送json表示 模型来回到写入的HTTP服务器。然而, Ember可以轻松使用其他耐用存储,例如储蓄 用户的硬盘indexeddb.或托管存储解决方案,可让您 避免写作和托管您自己的服务器。

从存储中加载模型后,组件知道如何 将模型数据转换为您可以与之交互的UI。为了 有关组件如何获取模型数据的详细信息,请参阅 指定路线的型号指导。

起初,使用Ember数据可能会与您所使用的方式不同 编写JavaScript应用程序。许多开发人员熟悉 使用Ajax从端点获取原始JSON数据,可能出现 一开始就容易。然而,随着时间的推移,复杂性泄漏到你的 应用程序代码,使其难以维护。

使用Ember数据,管理模型随着应用程序的增长 simpler 更轻松。

一旦你了解ember数据,你就会有很多 更好的方法来管理应用程序中数据加载的复杂性。 这将允许您的代码进化和成长,具有更好的可维护性。

Ember数据灵活性

归功于使用适配器模式,可以配置Ember数据 使用许多不同的后果。有整个适配器生态系统和几个内置适配器 这允许您的Ember应用程序与不同类型的服务器交谈。

默认情况下,Ember数据旨在使用框中的工作JSON:API.. JSON:API是建立常规,强大,表演的正式规范 允许客户端和服务器传达模型数据的API。

JSON:API标准化JavaScript应用程序如何与服务器交谈,因此 您可以减少前端和后端之间的耦合,并具有 更改堆栈的碎片更自由。

如果您需要将Ember.js应用程序与没有的服务器集成 have an 适配器可用(例如,您手工滚动API服务器 没有遵守任何JSON规范),设计Ember数据 to 可配置使用您服务器返回的任何数据。

Ember数据也旨在与流式服务器一起使用,如那些 由WebSocket提供支持。您可以向服务器打开套接字并推送 无论何时发生,都会变为ember数据,将您的应用实时提供 用户界面始终是最新的。

商店和一个单一的真理来源

建立Web应用程序的一种常见方式是紧密地耦合用户 对数据获取的接口元素。例如,想象一下你是 编写一个博客应用程序的管理部分,它具有一个功能 列出当前登录用户的草稿。

您可能会诱惑使负责提取的组件 data and storing it:

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import fetch from 'fetch';

export default class ListOfDraftsComponent extends Component {
  @tracked drafts;

  constructor() {
    super(...arguments);

    fetch('/drafts').then(data => {
      this.drafts = data;
    });
  }
}

然后,您可以在组件的模板中显示Trafate列表 this:

    {{#each this.drafts key="id" as |draft|}}
  • {{draft.title}}
  • {{/each}}

This works great for the list-of-drafts 成分。 However, your app 可能由许多不同的组成部分组成。在另一个页面上 可能希望组件显示草稿的数量。你可能会 tempted to copy and paste your existing willRender code into the new component.

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import fetch from 'fetch';

export default class DraftsButtonComponent extends Component {
  @tracked drafts;

  constructor() {
    super(...arguments);

    fetch('/drafts').then(data => {
      this.drafts = data;
    });
  }
}

  Drafts ({{this.drafts.length}})

不幸的是,该应用程序现在将为两个单独的请求进行 相同的信息。不仅冗余数据取代昂贵 浪费带宽和影响您的感知速度的条款 应用程序,这两个值很容易得到同步。你自己 可能使用了一个Web应用程序,其中物品列表出来 与工具栏中的计数器同步,导致令人沮丧的和 经验不一致。

还有一个紧耦合在您的应用程序的UI和 网络代码。如果URL或JSON有效载荷的格式更改,则 很可能以难以承到的方式打破所有UI组件 track down.

良好设计的坚实原则告诉我们物体应该有一个 单一责任。组件的责任应该是 将模型数据呈现给用户,而不是获取模型。

好的ember应用程序采取不同的方法。 ember数据给你一个 店铺这是应用程序中模型的中央存储库。 路线及其相应的控制器可以要求商店进行模型,商店是 负责知道如何获取它们。

它还意味着商店可以检测到两个不同的组件 询问相同的型号,允许您的应用只获取数据 从服务器一次。您可以将商店视为读取缓存 为您的应用程序。两个路线及其相应的控制器都可以访问 这个共享商店;当他们需要显示或修改模型时,它们 首先要求商店。

Zoey说......
Ember数据将商店服务遗弃到每条路线和控制器 so that you can immediately write this.store! 如果要在组件或其他服务中访问商店, 你需要注入商店服务。

楷模

In Ember Data, each model is represented by a subclass of Model that 定义数据的属性,关系和行为 present to the user.

模型定义了服务器提供的数据类型。为了 example, a Person model might have a name attribute that is a string, and a birthday attribute that is a date:

import Model, { attr } from '@ember-data/model';

export default class PersonModel extends Model {
  @attr('string') name;
  @attr('date') birthday;
}

模型还描述了与其他对象的关系。为了 example, an order may have many line-items, and a line-item may belong to a particular order.

import Model, { hasMany } from '@ember-data/model';


export default class OrderModel extends Model {
  @hasMany('line-item') lineItems;
}
import Model, { belongsTo } from '@ember-data/model';

export default class LineItemModel extends Model {
  @belongsTo('order') order;
}

模型本身没有任何数据,它们定义属性, 特定实例的关系和行为,称为 记录.

记录

A 记录是包含从中加载的数据的模型的实例 服务器。您的应用程序还可以创建新记录并保存回来 to the server.

通过其模型唯一标识了记录类型ID .

例如,如果您正在编写联系人管理应用程序,您可能会 have a Person model. An individual record in your app might have a type of person和an ID of 1 or steve-buscemi.

this.store.findRecord('person', 1); // => { id: 1, name: 'steve-buscemi' }

保存时,通常将ID分配给服务器的记录 第一次,但您也可以生成IDS客户端。

适配器

一个适配器是一个转换来自ember的请求的对象(例如 “找到ID为1”的用户)请求到服务器请求。

For example, if your application asks for a Person with an ID of 1,雾化应该如何加载它?在HTTP或WebSocket上?如果 it's HTTP, is the URL /person/1 or /resources/people/1?

适配器负责回答所有这些问题。 每当您的应用程序询问商店的记录没有 缓存,它会问适配器。如果更改记录并保存 它,商店将把记录交给适配器以发送 适当的数据到您的服务器并确认保存是 successful.

适配器让您完全更改您的API的实施方式 影响您的Ember应用程序代码。

缓存

商店将自动缓存您的记录。如果记录已经 装载后,第二次询问它将永远返回相同 对象实例。这最大限度地减少了往返的往返的数量 服务器,并允许您的应用程序快速将其UI呈现给用户 possible.

例如,您的应用程序第一次向商店询问 person 记录 with an ID of 1, it will fetch that information from your server.

However, the next time your app asks for a person with ID 1, the 商店将注意到它已经检索并缓存了 来自服务器的信息。而不是发送另一个请求 相同的信息,它将提供您的申请相同的记录 第一次提供它。此功能 - 总是返回相同的 记录对象,无论你看多少次 - 有时候 called an 身份地图.

使用身份映射很重要,因为它确保了更改了您 在UI的一部分中,传播到UI的一部分。它 还意味着您不必手动保留同步记录 - 您可以 要求纪录的纪录,不必担心是否其他部分 您的应用程序已经要求并加载它。

返回缓存的记录的一个缺点是您可能会找到状态 数据已经改变,因为它首先将其加载到商店中 身份映射。为了防止这种陈旧的数据是一个问题 长时间,备用数据将自动提出请求 背景每次从商店返回缓存的记录。什么时候 新数据进来,更新了记录,如果出现了 自最初渲染以来的记录更改,模板是 用新信息重新渲染。

建筑概述

您的申请首次询问商店录制,商店 看到它没有本地副本并请求它 适配器。您的适配器将去除您的录制 持久层;通常,这将是json表示 从HTTP服务器提供的记录。

图显示了查找卸载记录的过程

如上图所示,适配器不能总是返回 请求立即记录。在这种情况下,适配器必须制作 异步请求服务器,只有在该请求完成时 加载可以使用其背衬数据创建记录。

由于这种异步,商店立即返回一个 承诺 from the findRecord() method. Similarly, any request that the 商店给适配器也返回承诺。

一旦对服务器的请求返回JSON有效载荷 请求的记录,适配器解析了它返回的承诺 store with the JSON.

该商店然后认为json,初始化记录 JSON数据,并解决了返回申请的承诺 随着新装载的记录。

图显示了在从服务器返回后找到卸载记录的过程

让我们来看看会发生什么,如果你要求商店的记录 已经在缓存中。

图显示了在从服务器返回后找到卸载记录的过程

在这种情况下,因为商店已经知道了记录,它 返回它立即通过录制解析的承诺。它确实如此 不需要询问适配器(以及因此,服务器)进行副本 由于它已经在本地保存。


模型,记录,适配器和商店是您的核心概念 应该理解,充分利用ember数据。以下 部分对每个概念进行更多深度,以及如何 use them together.