首頁>技術>

本篇繼續重構路由裡面的程式碼,通過Ember提供的另外一個非常重要的特性實現——Ember Data。

Ember Data為開發者封裝好非常多資料操作函式,比如查詢所有findAll(),查詢一個findRecord()等等非常實用的API。

本篇學習要點:

Ember Data模型測試模型(model)在路由中載入modelEmber 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// 匯入attrimport Model, { attr } from '@ember-data/model';const COMMUNITY_CATEGORIES = [ 'Condo', 'Townhouse', 'Apartment'];// 這個model對應json資料裡面的type為rental的資料物件// 這個模型有點像java語言裡面的JavaBeanexport 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.jsimport { 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.jsimport Route from '@ember/routing/route';// 匯入Ember Serviceimport { 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.jsimport 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 applicationember 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 {\t// 請求字首,會構建成這樣的一個URL:/api/rentals.json\tnamespace = 'api';\tbuildURL(...args) {\t\treturn `${super.buildURL(...args)}.json`;\t}}// app/serializers/application.jsimport 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.jsimport { 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的!

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • YARN 在快手的應用實踐與技術演進之路