前端开发入门到精通的在线学习网站

网站首页 > 资源文章 正文

tutorial第二部分/Ember Data(第二版reviewtest1)

qiguaw 2024-11-04 13:27:45 资源文章 16 ℃ 0 评论


本篇继续重构路由里面的代码,通过Ember提供的另外一个非常重要的特性实现——Ember Data。

Ember Data为开发者封装好非常多数据操作函数,比如查询所有findAll(),查询一个findRecord()等等非常实用的API。

本篇学习要点:

  • Ember Data模型
  • 测试模型(model)
  • 在路由中加载model
  • Ember Data Store服务
  • 适配器、序列化器


Ember Data模型

在之前的文章中,我们分别在index.js和rental.js这两个路由里面查询了json文件的数据。然后手动解析JSON数据。现在有了Ember Data之后,不需要这么麻烦的处理数据了。

如果一个项目中有很多路由就很麻烦了,每个路由都要解析JSON数据,维护起来也是非常大的工作量,并且代码不好复用。

废话少说,开始干。。。

根据数据实体创建model。

ember g model rental

打开app/models/rental.js,在model中定义属性,使用@attr定义属性。

// app/models/rental.js
// 导入attr
import Model, { attr } from '@ember-data/model';

const COMMUNITY_CATEGORIES = [
  'Condo',
  'Townhouse',
  'Apartment'
];
// 这个model对应json数据里面的type为rental的数据对象
// 这个模型有点像java语言里面的JavaBean
export default class RentalModel extends Model {
    @attr title;
    @attr owner;
    @attr city;
    @attr location;
    @attr category;
    @attr image;
    @attr bedrooms;
    @attr description;
    // 在模型中定义一个方法
    get type() {
        if (COMMUNITY_CATEGORIES.inclueds(this.category)) {
            return "Community";
        } else {
            return "Standalone";
        }
    }
}

现在数据实体对应的模型已经定义好了,模型的属性和数据格式是对应的,比如rental.json里面的数据,定义属性的时候不需要指定数据的类型,Ember Data自动识别。

public/api/rentals/grand-old-mansion.json
{
  "data": {
    "type": "rentals",
    "id": "grand-old-mansion",
    "attributes": {
      "title": "Grand Old Mansion",
      "owner": "Veruca Salt",
      "city": "San Francisco",
      "location": {
        "lat": 37.7749,
        "lng": -122.4194
      },
      "category": "Estate",
      "bedrooms": 15,
      "image": "https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg",
      "description": "This grand old mansion sits on over 100 acres of rolling hills and dense redwood forests."
    }
  }
}


测试模型(model)

通过测试用例验证模型的定义是否正确了。

// tests/unit/models/rental-test.js
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Model | rental', function(hooks) {
  setupTest(hooks);
  // Replace this with your real tests.
  test('测试RentalModel模型定义', function(assert) {
    let store = this.owner.lookup('service:store');
    // 初始化一个模型实例
    let rental = store.createRecord('rental', {
        id: 'grand-old-mansion',
        title: 'Grand Old Mansion',
        owner: 'Veruca Salt',
        city: 'San Francisco',
        location: {
            lat: 37.7749,
            lng: -122.4194,
        },
        category: 'Estate',
        bedrooms: 15,
        image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg',
        description: 'This grand old mansion sits on over 100 acres of rolling hills and dense redwood forests.'
    });
    assert.ok(rental);
    assert.equal(rental.type, "Standalone");
    rental.category = "Condo";  // 重置值
    assert.equal(rental.type, "Community");
    rental.category = "Townhouse";
    assert.equal(rental.type, "Community");
  });
});



在路由中加载model

通过前面的用例验证,说明model定义没问题了,那么更重要的是怎么使用model,使用model改造路由。

// app/routes/index.js
import Route from '@ember/routing/route';
// 导入Ember Service
import { inject as service } from '@ember/service';
export default class IndexRoute extends Route {
    // 通过Ember Service注入一个全局对象
    @service store;
    async model() {
        // 使用数据model改造路由代码
        // 直接调用Ember Data的API,无需要手动解析数据。
        return this.store.findAll('rental');
    }
}



// app/routes/rental.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class RentalRoute extends Route {
    @service store;
    async model(params) {
        return this.store.find('rental', params.rental_id);
    }
}

改造完之后,还不能使用,你打开浏览器调式控制台就可以看到有报错信息。先暂且不管,继续往下。

比如:

ember-fetch.js:762 GET http://localhost:4200/rentals 404 (Not Found)

使用Ember Data重构之后,代码变得非常简洁。也不需要手动解析数据,直接调API即可。如果你有注意到现在调用数据都是通过store提供的API查询的。那么这个store又是个什么东东呢??


Ember Data Store服务

简单讲,这个store是Ember Data提供的一个Ember应用全局服务,在任何一个组件或者路由里面都可以引用。Ember Data会自动和后端接口对接,然后按照JSONAPI规范转换数据成model。开发者直接通过model引用就可以获取数据。对于已经请求的果的数据Ember Data会缓存到Ember应用里面的,下次再请求同样模型数据的时候直接从缓存拿而不是再发请求到后端。

打开浏览器调试窗口,刷新页面就会看到一个404的请求。虽然是报错了,但起码说明了调用findAll方法之后Ember Data自动发送了一个请求。这个请求的效果就和前面路由里面的/api/rentals.json是同样的。

继续改造,我们还需要添加一个全局的请求设置就可以访问到后端的服务。


适配器、序列化器

之所以不用手动解析数据是因为Ember Data里面Adapter和Serializer帮我们转换了数据格式。自动根据后端返回的JSONAPI格式数据转化成model。

同样的,通过EmberCLI创建一个适配器,一个序列化器。

ember g adapter application
ember g serializer application
// app/adapters/application.js
// 修改导入的类,使用JSONAPI适配器
import JSONAPIAdapter from '@ember-data/adapter/json-api';
// application是Ember应用的全局适配器,你还可以创建和model一一对应的适配器。
// 比如在当前目录创建一个rental.js的适配器,那么这个适配器只作用域rental这个模型
export default class ApplicationAdapter extends JSONAPIAdapter {
	// 请求前缀,会构建成这样的一个URL:/api/rentals.json
	namespace = 'api';
	buildURL(...args) {
		return `${super.buildURL(...args)}.json`;
	}
}
// app/serializers/application.js
import JSONAPISerializer from '@ember-data/serializer/json-api';
// 显式指定JSONAPI序列化,
// 和适配器一样,也是可以指定某一个model的序列化。
export default class ApplicationSerializer extends JSONAPISerializer {
}

项目刷新完之后,神奇事情发生了。我们的Super-rental恢复正常了,浏览器控制台也没有报错了。首页数据也显示正确,详情页面数据显示正确。




adapter和serializer是成对出现的。在adapter中重写了父类的buildURL方法,因为现在的后端服务提供的是json后后缀的访问地址,比如之前是直接在路由中使用http://localhost:4200/api/rentals.json。一般后端api不会使用.json最为url后缀,所以要适配后端服务重写buildURL方法。

同样的,因为返回的数据就是JOSNAPI格式的数据,所以适配器和序列化器都不需要做特殊处理,因为Ember Data默认就是使用JSONAPI数据格式。如果你后端只是返回的简单REST数据,那么你需要手动处理数据格式,就像前面的文章,在路由里面手动解析数据一样。

最后用两个简单的测试用例验证。

// tests/serializers/application-test.js
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Serializer | application', function(hooks) {
  setupTest(hooks);
  // Replace this with your real tests.
  test('验证Serializer是否正确', function(assert) {
    let store = this.owner.lookup('service:store');
    let serializer = store.serializerFor('rental');
    assert.ok(serializer);
  });
  test('创建空对象验证Serializer。', function(assert) {
    let store = this.owner.lookup('service:store');
    let record = store.createRecord('rental', {});
    let serializedRecord = record.serialize();
    assert.ok(serializedRecord);
  });
});


Ember Data提供了许多功能(例如管理不同模型之间的关系,很类似于一个ORM框架),并且我们可以了解更多信息。例如,如果您的后端在不同的端点之间存在一些不一致,则Ember Data允许您也定义更具体的,按模型的适配器和序列化器!我们在这里只是表面。如果您想了解有关Ember Data的更多信息,在后面的指南中会有一章专门讲解model的!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表