使用Vue & deepstream構建實時CRUD應用
JavaScript是當前最流行的語言,因為你可以用它為任何平臺(包括網站,服務器,移動設備和桌面)構建應用程序。
Vue是一個漸進式的JavaScript UI工具,很容易上手,您可以在一天之內就學會它。另一方面,它擁有足夠強大的功能以滿足您的前端需求。
實時技術正在逐漸展現出新的形態;實時服務器現在用作處理實時相關任務的抽象。 deepstream 是一個 開源 ,免費和極速的實時服務器,您可以在您的機器上安裝。
本文演示了使用deepstream和Vue作為我們的前端工具來構建實時應用程序。下面的GIF就是我們將要實現的內容:
在開始代碼之前,讓我們說服自己為什么非得如此。
為什么要使用Vue
依我拙見,Vue是一個迄今為止最簡單的而且值得一試的UI框架。首先它很容易上手,其次它幫我們實現了數據綁定,服務端渲染和狀態管理等在考慮UI庫時所遇到的復雜的概念。
Vue考慮到現有UI庫的復雜性,并簡化了這些復雜性,使我們的生活不像軟件工程師那么沮喪。它也支持我們最喜歡的后端工具, Laravel --使用的集成更簡單。這當然不意味著你不能與任何其他后端平臺集成。
為什么是Deepsteam
deepstream是一個獨立的服務器--它比大多數的實時解決方案都要 快 --它允許您使用持久狀態作為數據同步,pub/sub模式作為事件或者請求/響應模式作為RPC。
您可以放心,通過豐富的選項,deepstream可以集成在你正在開發的任何性質的應用程序中。包括但不限于聊天,實時新聞更新,庫存信息,CRUD/CMS應用,數據可視化,數據監控等。
安裝Vue和deepstream
安裝Vue和deepstream相當簡單,您只需要幾條CLI命令就可以完成安裝。
deepstream是跨平臺的,可以安裝在Linux,Windows和OSX上。與此同時,你也可以通過Docker和npm來安裝它。對于這篇文章,我們將下載操作系統相對應的deepstream。解壓下載的文件,然后在解壓后的文件夾根目錄執行以下命令來啟動服務器:
./deepstream
vue-cli 是一個可以讓你快速創建Vue項目的CLI工具。我們需要安裝這個工具以便創建我們的demo應用。
npm i -g vue-cli
通過全局安裝vue-cli工具,我們可以在任何地方使用這個命令。Vue腳手架有不同的模版,我們只需要通過以下命令使用最簡單的模版即可:
vue init webpack my-project
用Vue實現CRUD
在我們嘗試構建一個實時應用之前,讓我們先創建一個平臺。一個圖書的CRUD(增刪查改)應用 看起來是個好主意。
Creating
src 文件夾中的 App.vue 文件是我們的主要和唯一的組件,這對于我們正在嘗試構建的應用而言是足夠的。打開 app.vue 文件創建一個簡單的form:
<!-- ./src/App.vue -->
<template>
<div id="app">
<h1> {{ title }} </h1>
<h3>New Book</h3>
<form v-on:submit.prevent="onSubmit">
<div>
<input name="title" type="text" placeholder="title" v-model="book.title" />
</div>
<div>
<input name="year" type="text" placeholder="year" v-model="book.year" />
</div>
<div>
<input name="author" type="text" placeholder="author" v-model="book.author" />
</div>
<div>
<label for="read">Read?</label>
<input type="checkbox" v-model="book.read" id="read" name="read" />
</div>
<button v-if="updating">Update</button>
<button v-else>Add</button>
</form>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
title: 'My Books Manager',
updating: false,
book: {
title: '',
year: '',
author: '',
read: false
}
}
}
}
</script>
app.vue 就是我們熟知的Vue 單文件組件 。這是Vue應用的一個很好的策略,它允許每個組件將其模版,樣式和業務邏輯保存在一個文件中。
我們用Vue數據綁定實現了一個基本的表單。每一個表單項都通過 v-model 與Vue data 方法返回的book對象的屬性進行綁定。Vue data 方法還返回了用作APP頭部的標題和切換 add 和 update 按鈕的更新標識。通過 v-if...v-else 指令可以實現切換。
當有按鈕被點擊時, onSubmit 方法便會觸發,因為在上面我們通過 v-on:submit 指令給form綁定了這個方法。因此接下來我們需要創建這樣一個方法:
export default {
name: 'app',
data () {
return {
// ...
updateIndex: 0,
books: [],
book: {
title: '',
year: '',
author: '',
read: false
}
}
},
methods: {
onSubmit () {
if (this.updating) {
this.onUpdate();
return;
}
this.book.push(this.book);
this.book = {
title: '',
year: '',
author: '',
read: false
}
}
}
}
onSubmit 會檢查是否是更新操作。如果是更新操作,它將調用 onUpdate 方法來處理更新。反之,它將把新的book對象push到books數組里面。
Reading
通過使用 v-for 指令迭代books數組,我們可以通過表格將其展示出來。
<template>
<div id="app">
<h1> {{ title }} </h1>
<h3>New Book</h3>
<!-- For markup truncated -->
<h3>All Books</h3>
<table>
<tr>
<th>Title</th>
<th>Year</th>
<th>Author</th>
<th>Read</th>
<td>Update</td>
<td>Delete</td>
</tr>
<tr v-for="(b, index) in books">
<td>{{ b.title }}</td>
<td>{{ b.year }}</td>
<td>{{ b.author }}</td>
<td v-if="b.read">?</td>
<td v-else> </td>
<td v-on:click.prevent="onEdit(index)"><a>?</a></td>
<td v-on:click.prevent="onDelete(index)"><a>?</a></td>
</tr>
</table>
</div>
</template>
表格數據行中額外的兩個鏈接來處理編輯(不是更新)和刪除記錄,它們分別調用 onEdit 和 onDelete 方法。您可以在表單中輸入一些信息,然后在表格中查看結果:
Updating
更新操作需要兩個步驟 - 從表中選擇我們需要更新的記錄,這將使它展現在表單中,并更改數組以更新值。
onEdit 方法負責第一個階段:
data () {
return {
updating: false;
updateIndex: 0,
books: [],
book: {
title: '',
year: '',
author: '',
read: false
}
}
},
methods: {
// ...
onEdit (index) {
this.updating = true;
this.updateIndex = index;
this.book = this.books[index];
}
}
onEdit 方法首先將更新標識設置為 true ,然后將 updateIndex 設置為正在編輯的索引,并用在更新的索引中找到的記錄替換書模型。
updateIndex 用于跟蹤調用 onUpdate 時正在更新的內容.
onUpdate () {
this.updating = false;
this.book[this.updateIndex] = this.book;
this.book = {
title: '',
year: '',
author: '',
read: false
}
},
onUpdate 重置了更新標識,并更新了數組中位置為 updateIndex 的book對象,然后將book對象清空了。
Deleting
這是最簡單的一部分;我們使用數組splice方法從數組中刪除一個項目:
onDelete (index) {
// Remove one item starting at
// the specified index
this.book.splice(index, 1)
}
使用deepstream實現實時
我們已經創建了一個可以運行的應用,但這并不是我們的最終目的,當表格中的數據發生變化時我們需要讓所有連接的客戶端都可以接收到變化,這正是deepstream的用武之地。目前我們這么做,其他客戶端并不會接受到更新。
deepstream客戶端 記錄 & 列表
在這篇文章的開頭,我們安裝并啟動了deepstream服務器。實際上,這個服務器運行在本機的 6020 端口上,且處于空閑狀態等待客戶端連接交換數據。
deepstream客戶端可以是任何形式的,從Web,桌面,移動,甚至物聯網。我們今天所關心的只是Web,所以我們需要使用deepstream的JS SDK來連接服務器。您可以通過執行下面的命令來安裝SDK:
npm i --save deepstream.io-client-js
deepstream中的記錄和任何其他表示數據的形式的記錄一樣,它是單個實體并存儲給定的信息項。唯一的區別在于deepstream中的記錄是實時的,這意味著記錄中存儲的數據可以被客戶端訂閱,并且以最小的有效載荷通知客戶端。
另一方面,列表可以很好的像集合那樣組織數據。列表也是實時的,所以他們的實時更新也可以被客戶端訂閱。我們將在我們的應用程序中使用deepstream的這些功能,使應用程序實時。
身份驗證
身份驗證就是在任何情況下您都可以確認用戶的身份是否和她表明的身份一致。通常的HTTP驗證在處理這個問題時和deepstream有那么一點不同。然而,好消息是,你可以很簡單的把它們 結合在一起使用 。
對于每一個deepstream的連接客戶端,身份驗證是必須的,但這并不意味著必須提供憑據。登錄可以是匿名的,并適用于我們當前的情況。
import * as ds from 'deepstream.io-client-js';
export default {
name: 'app',
data () {
return {
ds: ds('localhost:6020'),
books$$: null
// . . .
}
},
created () {
this.ds.login({}, () => {
console.log('logged in');
});
},
methods: {/* . . .*/}
}
首先我們導入了client模塊,然后創建了一個成員變量 ds 來保存對deepstream的引用,同時傳遞服務器的URL。
created 方法在組件處于 ready 狀態時被Vue調用。這使得我們可以在這個方法中處理deepstream身份驗證,通過調用接收憑證對象和回調的 deepstream.login 方法來執行認證。
books$$ 屬性將引用我們尚未創建的deepstream中的數據列表。
我們將改寫CRUD過程,并用deepstream對其進行更新。但是在這之前,我們需要使所有連接的客戶端能夠監聽到值的改變,以便它們可以相應的更新。
訂閱List和Records
created () {
this.ds.login({}, () => {
console.log('logged in');
});
this.books$$ = this.ds.record.getList('books');
/*
* Entry added
*/
this.books$$.on('entry-added', (recordName, index) => {
this.ds.record.getRecord(recordName).whenReady(record => {
// The scond paramaeter,
// a boolean, is a flag to specify whether
// the callback should be invoked immediatly
// with the current value
record.subscribe(data => {
if(!data.id) {
if(data.title) {
data.id = record.name;
this.books.push(data);
}
} else {
this.books = this.books.map(b => {
if(data.id == b.id) {
b = data;
}
console.log(b)
return b;
});
}
}, true)
});
});
/*
* Entry removed
*/
this.books$$.on('entry-removed', (recordName, index) => {
this.ds.record.getRecord(recordName).whenReady(record => {
record.subscribe(data => {
this.books.splice(this.books.indexOf(data, 1));
}, true)
});
});
},
- Lists和records API依賴 record 對象,我們可以通過給getList方法傳入一個名稱來創建或檢索列表。
- entry-added 事件在記錄添加到列表中時觸發。
- 當記錄已經添加, whenReady 方法確保該記錄在我們通過 subscribe 方法訂閱之前處于 ready 狀態。
- subscribe 方法接受一個回調來檢查數據,如果數據存在,則更新它。反之則用傳入的數據更新記錄,同時將數據的id設置為記錄名稱(id),books數組隨著數據的進入而更新。
- entry-removed 正好和 entry-added 事件剛好相反。它在我們刪除記錄的時候觸發,以便從books數組中刪除記錄的數據。
Creating
onSubmit 方法需要重寫。我們不會直接將data push到 books 數組中因為這部分的工作已經被我們的訂閱處理了。我們僅僅需要調用方法創建一個帶數據的record對象并將它添加到列表中:
onSubmit() {
const recordName = this.book.id || 'book/' + this.ds.getUid();
this.ds.record.has(recordName, (err, has) => {
if(has){
this.onUpdate();
return;
} else {
const bookRecord = this.ds.record.getRecord(recordName);
bookRecord.set(this.book);
this.books$$.addEntry(recordName)
this.book = {
title: '',
year: '',
author: '',
read: false
}
}
})
},
如果數據存在,則使用 UUID 或附加到數據的Id來創建/獲取記錄。 has 用于檢查該記錄是否存在,如果存在,我們調用 onUpdate 方法;反之,我們將n new book 設置為記錄,并使用 addEntry 更新 book$$ 列表.
Updating
onUpdate() {
const recordName = this.books[this.updateIndex].id;
const bookRecord = this.ds.record.getRecord(recordName);
bookRecord.set(this.book);
this.book = {
title: '',
year: '',
author: '',
read: false
}
this.updating = false;
}
在 onSubmit 中,如果記錄名字存在則調用了 onUpdate 方法進行更新。
onUpdate 通過 updateIndex 在books數組里檢索記錄名稱。我們得到相應的記錄并用 book 去更新記錄。這里不需要對list進行任何操作,它會自動的進行相應的更新。
Deleting
onDeleting 方法僅僅調用了list的 removeEntry 來刪除一個記錄:
onDelete(index) {
this.books$$.removeEntry(this.books[index].id);
}
總結
隨著更先進的解決方案的出現,構建實時應用程序變得越來越好。 您還可以使用HTTP認證或JWT認證,以及當您需要持久化數據時將您的數據庫連接到deepstream。
令人著迷的是我們可以使用Vue和deepstream構建更好的UI應用程序。Vue并不是唯一一個受到社區歡迎的框架。您可以使用相同的初始化和身份驗證策略將deepstream與任何其他UI庫集成在一起。
來自:http://www.w3ctech.com/topic/1985