做者:Michael Wanyoike
原文:www.sitepoint.com/pusher-vue-…javascript
現現在,即時通迅已經愈來愈廣泛,而且用戶體驗也愈來愈天然和流暢。css
本文將使用ChatKit增強過的Vue.js建立一個實時聊天應用,ChatKit服務爲咱們提供了一個建立聊天應用的後端,而且能夠運行於任何設備上,讓咱們只需關注前端用戶接口,這個接口經過ChatKit client包鏈接到ChatKit服務。前端
這是一篇中到高級的教程,理解本文須要對如下概念都比較熟悉:vue
還須要安裝Node.js,能夠直接在官網上下載安裝包。 最後須要使用如下命令安裝全局的Vue CLI。java
npm install -g @vue/cli
複製代碼
在寫這篇文章的時候Node版本是 10.14.1,Vue CLI的最新版本是 3.2.1。node
咱們要建立一個基礎的聊天應用,應用須要有以下功能:git
就像先前提到的,這裏只建立前端,ChatKit服務有個能夠管理用戶、受權和房間的後端接口。github
能夠在GitHub上找到完整的代碼。web
建立ChatKit實例,相似於建立服務端實例。進入Puser網站的ChatKit頁面,先註冊,完成登陸後進入Pusher的儀表板,而後選擇ChatKit產品。vue-router
點擊Create按鈕建立一個新的ChatKit實例,好比輸入VueChatTut。 在這一教程中使用免費版,支持1000個用戶,足夠咱們在本例中使用了,轉到Console選項卡,須要建立一個新的用戶開始咱們的應用。直接點擊Create User按鈕。 能夠添加兩到三個用戶,例如:再建立三個房間並指定相應的用戶。例如:
最後,控制檯界面是這樣子:
下一步,能夠進入Rooms選項卡並選擇用戶,而後輸入消息測試一下。接下來,進入Credentials選項卡記錄下Instance Locator,而且激活Test Token Provider,它是用來生成HTTP終端結點的,這個也記錄下來。 ChatKit的後端也準備好了,如今開始構建Vue.js的前端。打開終端,像下面這樣建立項目
vue create vue-chatkit
複製代碼
選擇Manually select features而且像下面這樣選擇相關問題。
確保選擇了Babel, Vuex和Vue Router做爲附加功能。接下來,建立以下結構的文件夾和文件: 確保建立了上圖中全部的文件夾和文件,刪除不須要的文件也就是上圖中不存在的文件。對於loading-btn.css他loading.css這兩個文件,能夠在loading.io上找到,這兩件文件沒法經過npm倉庫獲取,因此須要本身手動下載,而後放在項目中。在此最好知道這兩個文件是作什麼的,怎樣定製化加載條。
下面,安裝下面依賴:
npm i @pusher/chatkit-client bootstrap-vue moment vue-chat-scroll vuex-persist
複製代碼
能夠點擊連接看看每一個包都是作什麼的,怎樣配置。
如今配置Vue.js項目。打開src/main.js更新代碼爲以下內容:
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import VueChatScroll from 'vue-chat-scroll'
import App from './App.vue'
import router from './router'
import store from './store/index'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import './assets/css/loading.css'
import './assets/css/loading-btn.css'
Vue.config.productionTip = false
Vue.use(BootstrapVue)
Vue.use(VueChatScroll)
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
複製代碼
更新src/router.js:
import Vue from 'vue'
import Router from 'vue-router'
import Login from './views/Login.vue'
import ChatDashboard from './views/ChatDashboard.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'login',
component: Login
},
{
path: '/chat',
name: 'chat',
component: ChatDashboard,
}
]
})
複製代碼
更新src/store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'
import mutations from './mutations'
import actions from './actions'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
const vuexLocal = new VuexPersistence({
storage: window.localStorage
})
export default new Vuex.Store({
state: {
},
mutations,
actions,
getters: {
},
plugins: [vuexLocal.plugin],
strict: debug
})
複製代碼
Vue-persist是爲了讓Vuex的state在頁面刷新和從新加載的時候可以保存下來。
目前,代碼應該是可以編譯並無錯誤的,但如今不執行,還須要建立用戶界面。
如今開始更新src/App.vue:
<template>
<div id="app">
<router-view/>
</div>
</template>
複製代碼
接下來須要定義UI組件運行所須要的Vuex store的state ,經過進入src/store/index.js,更新一下state和getters部分,像下面這樣:
state: {
loading: false,
sending: false,
error: null,
user: [],
reconnect: false,
activeRoom: null,
rooms: [],
users: [],
messages: [],
userTyping: null
},
getters: {
hasError: state => state.error ? true : false
},
複製代碼
這是這個聊天應用所須要的全部的state變量了,loading state用於在UI上決定是否顯示CSS 加載條。error state用於存儲剛發生的錯誤信息,其餘的變量會在用到的時候再解釋。
接下來打開src/view/Login.vue更新以下:
<template>
<div class="login">
<b-jumbotron header="Vue.js Chat"
lead="Powered by Chatkit SDK and Bootstrap-Vue"
bg-variant="info"
text-variant="white">
<p>For more information visit website</p>
<b-btn target="_blank" href="https://pusher.com/chatkit">More Info</b-btn>
</b-jumbotron>
<b-container>
<b-row>
<b-col lg="4" md="3"></b-col>
<b-col lg="4" md="6">
<LoginForm />
</b-col>
<b-col lg="4" md="3"></b-col>
</b-row>
</b-container>
</div>
</template>
<script>
import LoginForm from '@/components/LoginForm.vue'
export default {
name: 'login',
components: {
LoginForm
}
}
</script>
複製代碼
而後,向src/components/LoginForm.vue插入以下代碼:
<template>
<div class="login-form">
<h5 class="text-center">Chat Login</h5>
<hr>
<b-form @submit.prevent="onSubmit">
<b-alert variant="danger" :show="hasError">{{ error }} </b-alert>
<b-form-group id="userInputGroup"
label="User Name"
label-for="userInput">
<b-form-input id="userInput"
type="text"
placeholder="Enter user name"
v-model="userId"
autocomplete="off"
:disabled="loading"
required>
</b-form-input>
</b-form-group>
<b-button type="submit"
variant="primary"
class="ld-ext-right"
v-bind:class="{ running: loading }"
:disabled="isValid">
Login <div class="ld ld-ring ld-spin"></div>
</b-button>
</b-form>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
name: 'login-form',
data() {
return {
userId: '',
}
},
computed: {
isValid: function() {
const result = this.userId.length < 3;
return result ? result : this.loading
},
...mapState([
'loading',
'error'
]),
...mapGetters([
'hasError'
])
}
}
</script>
複製代碼
正如先前提到的,這是高級教程,若是理解這些代碼有任何問題,能夠看準備條件或項目依賴的相關信息。
如今能夠經過npm run serve啓動Vue dev服務端來確認一下應用執行沒有任何兼容性問題。
能夠輸入用戶名確認一下校驗功能是否有效,輸入三個單詞後就能夠看到Login按鈕被激活了。Login按鈕如今是不起做用的,由於咱們尚未針對這部分的編碼,後面咱們會繼續這部分。如今繼續構建聊天的用戶界面。打開src/vie/ChatDashboard.vue插入如下代碼:
<template>
<div class="chat-dashboard">
<ChatNavBar />
<b-container fluid class="ld-over" v-bind:class="{ running: loading }">
<div class="ld ld-ring ld-spin"></div>
<b-row>
<b-col cols="2">
<RoomList />
</b-col>
<b-col cols="8">
<b-row>
<b-col id="chat-content">
<MessageList />
</b-col>
</b-row>
<b-row>
<b-col>
<MessageForm />
</b-col>
</b-row>
</b-col>
<b-col cols="2">
<UserList />
</b-col>
</b-row>
</b-container>
</div>
</template>
<script>
import ChatNavBar from '@/components/ChatNavBar.vue'
import RoomList from '@/components/RoomList.vue'
import MessageList from '@/components/MessageList.vue'
import MessageForm from '@/components/MessageForm.vue'
import UserList from '@/components/UserList.vue'
import { mapState } from 'vuex';
export default {
name: 'Chat',
components: {
ChatNavBar,
RoomList,
UserList,
MessageList,
MessageForm
},
computed: {
...mapState([
'loading'
])
}
}
</script>
複製代碼
ChatDashboard至關於下面子組件的一個用於佈局的父頁面。
<template>
<b-navbar id="chat-navbar" toggleable="md" type="dark" variant="info">
<b-navbar-brand href="#">
Vue Chat
</b-navbar-brand>
<b-navbar-nav class="ml-auto">
<b-nav-text>{{ user.name }} | </b-nav-text>
<b-nav-item href="#" active>Logout</b-nav-item>
</b-navbar-nav>
</b-navbar>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'ChatNavBar',
computed: {
...mapState([
'user',
])
},
}
</script>
<style>
#chat-navbar {
margin-bottom: 15px;
}
</style>
複製代碼
向src/components/RoomList.vue添加以下樣例代碼:
<template>
<div class="room-list">
<h4>Channels</h4>
<hr>
<b-list-group v-if="activeRoom">
<b-list-group-item v-for="room in rooms"
:key="room.name"
:active="activeRoom.id === room.id"
href="#"
@click="onChange(room)">
# {{ room.name }}
</b-list-group-item>
</b-list-group>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'RoomList',
computed: {
...mapState([
'rooms',
'activeRoom'
]),
}
}
</script>
複製代碼
向src/components/UserList.vue添加以下樣例代碼:
<template>
<div class="user-list">
<h4>Members</h4>
<hr>
<b-list-group>
<b-list-group-item v-for="user in users" :key="user.username">
{{ user.name }}
<b-badge v-if="user.presence"
:variant="statusColor(user.presence)"
pill>
{{ user.presence }}</b-badge>
</b-list-group-item>
</b-list-group>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'user-list',
computed: {
...mapState([
'loading',
'users'
])
},
methods: {
statusColor(status) {
return status === 'online' ? 'success' : 'warning'
}
}
}
</script>
複製代碼
向src/components/MessageList.vue添加以下樣例代碼:
<template>
<div class="message-list">
<h4>Messages</h4>
<hr>
<div id="chat-messages" class="message-group" v-chat-scroll="{smooth: true}">
<div class="message" v-for="(message, index) in messages" :key="index">
<div class="clearfix">
<h4 class="message-title">{{ message.name }}</h4>
<small class="text-muted float-right">@{{ message.username }}</small>
</div>
<p class="message-text">
{{ message.text }}
</p>
<div class="clearfix">
<small class="text-muted float-right">{{ message.date }}</small>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'message-list',
computed: {
...mapState([
'messages',
])
}
}
</script>
<style>
.message-list {
margin-bottom: 15px;
padding-right: 15px;
}
.message-group {
height: 65vh !important;
overflow-y: scroll;
}
.message {
border: 1px solid lightblue;
border-radius: 4px;
padding: 10px;
margin-bottom: 15px;
}
.message-title {
font-size: 1rem;
display:inline;
}
.message-text {
color: gray;
margin-bottom: 0;
}
.user-typing {
height: 1rem;
}
</style>
複製代碼
向src/components/MessageForm.vue添加以下樣例代碼:
<template>
<div class="message-form ld-over">
<small class="text-muted">@{{ user.username }}</small>
<b-form @submit.prevent="onSubmit" class="ld-over" v-bind:class="{ running: sending }">
<div class="ld ld-ring ld-spin"></div>
<b-alert variant="danger" :show="hasError">{{ error }} </b-alert>
<b-form-group>
<b-form-input id="message-input"
type="text"
v-model="message"
placeholder="Enter Message"
autocomplete="off"
required>
</b-form-input>
</b-form-group>
<div class="clearfix">
<b-button type="submit" variant="primary" class="float-right">
Send
</b-button>
</div>
</b-form>
</div>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
export default {
name: 'message-form',
data() {
return {
message: ''
}
},
computed: {
...mapState([
'user',
'sending',
'error',
'activeRoom'
]),
...mapGetters([
'hasError'
])
}
}
</script>
複製代碼
檢查一下代碼確保沒有什麼是很神祕的。導航到http://localhost:8080/chat檢查一下全部內容都能正常執行。檢查一下終端和瀏覽器的控制面板確保在這裏沒有錯誤,那麼如今頁面看起來是下圖這個樣子的。
很空是否是?進入src/store/index.js而後在state上插入一些Mock數據:state: {
loading: false,
sending: false,
error: 'Relax! This is just a drill error message',
user: {
username: 'Jack',
name: 'Jack Sparrow'
},
reconnect: false,
activeRoom: {
id: '124'
},
rooms: [
{
id: '123',
name: 'Ships'
},
{
id: '124',
name: 'Treasure'
}
],
users: [
{
username: 'Jack',
name: 'Jack Sparrow',
presence: 'online'
},
{
username: 'Barbossa',
name: 'Hector Barbossa',
presence: 'offline'
}
],
messages: [
{
username: 'Jack',
date: '11/12/1644',
text: 'Not all treasure is silver and gold mate'
},
{
username: 'Jack',
date: '12/12/1644',
text: 'If you were waiting for the opportune moment, that was it'
},
{
username: 'Hector',
date: '12/12/1644',
text: 'You know Jack, I thought I had you figured out'
}
],
userTyping: null
},
複製代碼
保存這個文件後,就能夠看到下圖的內容了。
這個簡單的測試確保全部的組件和state是正常綁定的。如今能夠恢復到原來的state代碼:state: {
loading: false,
sending: false,
error: null,
user: null,
reconnect: false,
activeRoom: null,
rooms: [],
users: [],
messages: [],
userTyping: null
}
複製代碼
如今開始實現具體特性,從登陸表單開始。
這部分將引入一個無密碼非安全的認證系統。本文不涉及合適的安全認證方面。首先,須要開始構建本身的接口,它將經過@pusher/ ChatKit -client包與ChatKit服務進行交互。
回到ChatKit控制面板,將原來提到的instance和測試token參數拷到項目根目錄下的.env.local文件中並保存:
VUE_APP_INSTANCE_LOCATOR=
VUE_APP_TOKEN_URL=
VUE_APP_MESSAGE_LIMIT=10
複製代碼
咱們添加了MESSAGE_LIMIT參數,這個值是限制聊天應用將獲取的消息數量。而後確保把credentials選項卡中的其餘參數也填上了。
接下來,進入src/chatkit.js開始構建聊天應用的基礎:
import { ChatManager, TokenProvider } from '@pusher/chatkit-client'
const INSTANCE_LOCATOR = process.env.VUE_APP_INSTANCE_LOCATOR;
const TOKEN_URL = process.env.VUE_APP_TOKEN_URL;
const MESSAGE_LIMIT = Number(process.env.VUE_APP_MESSAGE_LIMIT) || 10;
let currentUser = null;
let activeRoom = null;
async function connectUser(userId) {
const chatManager = new ChatManager({
instanceLocator: INSTANCE_LOCATOR,
tokenProvider: new TokenProvider({ url: TOKEN_URL }),
userId
});
currentUser = await chatManager.connect();
return currentUser;
}
export default {
connectUser
}
複製代碼
注意咱們須要將常量MESSAGE_LIMIT轉換成數值,由於默認狀況下process.env對象會強制全部的屬性是字符串類型的。 向src/store/mutations插入以下代碼:
export default {
setError(state, error) {
state.error = error;
},
setLoading(state, loading) {
state.loading = loading;
},
setUser(state, user) {
state.user = user;
},
setReconnect(state, reconnect) {
state.reconnect = reconnect;
},
setActiveRoom(state, roomId) {
state.activeRoom = roomId;
},
setRooms(state, rooms) {
state.rooms = rooms
},
setUsers(state, users) {
state.users = users
},
clearChatRoom(state) {
state.users = [];
state.messages = [];
},
setMessages(state, messages) {
state.messages = messages
},
addMessage(state, message) {
state.messages.push(message)
},
setSending(state, status) {
state.sending = status
},
setUserTyping(state, userId) {
state.userTyping = userId
},
reset(state) {
state.error = null;
state.users = [];
state.messages = [];
state.rooms = [];
state.user = null
}
}
複製代碼
mutations中的代碼至關簡單,就是一堆setters,在後面的幾節裏,你很快就會理解每一個mutation函數的用途。接下來,更新src/store/actions.js的代碼:
import chatkit from '../chatkit';
// Helper function for displaying error messages
function handleError(commit, error) {
const message = error.message || error.info.error_description;
commit('setError', message);
}
export default {
async login({ commit, state }, userId) {
try {
commit('setError', '');
commit('setLoading', true);
// Connect user to ChatKit service
const currentUser = await chatkit.connectUser(userId);
commit('setUser', {
username: currentUser.id,
name: currentUser.name
});
commit('setReconnect', false);
// Test state.user
console.log(state.user);
} catch (error) {
handleError(commit, error)
} finally {
commit('setLoading', false);
}
}
}
複製代碼
像下面這樣更新src/components/LoginForm.vue的內容:
import { mapState, mapGetters, mapActions } from 'vuex'
//...
export default {
//...
methods: {
...mapActions([
'login'
]),
async onSubmit() {
const result = await this.login(this.userId);
if(result) {
this.$router.push('chat');
}
}
}
}
複製代碼
爲了加載env.local的數據須要重啓Vue.js服務,若是看到任何未使用變量的錯誤,先忽略它們,一旦完成這些,導航到http://localhost:8080/測試一下登陸功能:
在上面的例子中,我使用不正確的用戶名,就是要確認一下錯誤處理功能能夠成功執行。 上面的截屏中,使用的是正確的用戶名。我還打開了瀏覽器的console選項卡確保user對象有值。若是你在Chrome或Firefox上安裝了Vue.js Dev Tools的話會更好,能夠看到更多詳細的信息。 到目前爲止,若是全部的功能正確執行的話,請看下一步。如今已經成功驗證過登陸功能,須要將用戶重定向到ChatDashboard視圖。使用this.$router.push('chat');進行跳轉。然而login操做須要返回一個Boolean值來決定何時是能夠跳轉到ChatDashboard視圖,還須要從ChatKit服務上獲取實際的數據填充RoomList和UserList組件。
更新src/chatkit.js的代碼:
//...
import moment from 'moment'
import store from './store/index'
//...
function setMembers() {
const members = activeRoom.users.map(user => ({
username: user.id,
name: user.name,
presence: user.presence.state
}));
store.commit('setUsers', members);
}
async function subscribeToRoom(roomId) {
store.commit('clearChatRoom');
activeRoom = await currentUser.subscribeToRoom({
roomId,
messageLimit: MESSAGE_LIMIT,
hooks: {
onMessage: message => {
store.commit('addMessage', {
name: message.sender.name,
username: message.senderId,
text: message.text,
date: moment(message.createdAt).format('h:mm:ss a D-MM-YYYY')
});
},
onPresenceChanged: () => {
setMembers();
},
onUserStartedTyping: user => {
store.commit('setUserTyping', user.id)
},
onUserStoppedTyping: () => {
store.commit('setUserTyping', null)
}
}
});
setMembers();
return activeRoom;
}
export default {
connectUser,
subscribeToRoom
}
複製代碼
若是看過hooks這一節,就知道ChatKit服務有用於和客戶端應用進行通迅的事件處理器,能夠在這裏看完整的文檔。我將快速的總結一下每一個鉤子方法的做用:
用下面的代碼更新src/store/actions.js中的login函數:
//...
try {
//... (place right after the `setUser` commit statement)
// Save list of user's rooms in store const rooms = currentUser.rooms.map(room => ({ id: room.id, name: room.name })) commit('setRooms', rooms); // Subscribe user to a room const activeRoom = state.activeRoom || rooms[0]; // pick last used room, or the first one commit('setActiveRoom', { id: activeRoom.id, name: activeRoom.name }); await chatkit.subscribeToRoom(activeRoom.id); return true; } catch (error) { //... } 複製代碼
在保存代碼以後,回到登陸頁,再輸入正確的用戶名,應該是看到下面這樣的頁面。
若是遇到了問題,能夠嘗試如下操做:
這部分很是簡單,由於基礎已經打好了。首先,建立一個容許用戶切換房間的方法,打開src/store/actions.js在login方法處理器後添加該函數:
async changeRoom({ commit }, roomId) {
try {
const { id, name } = await chatkit.subscribeToRoom(roomId);
commit('setActiveRoom', { id, name });
} catch (error) {
handleError(commit, error)
}
},
複製代碼
接下來,打開src/componenents/RoomList.vue更新script部分代碼以下:
import { mapState, mapActions } from 'vuex'
//...
export default {
//...
methods: {
...mapActions([
'changeRoom'
]),
onChange(room) {
this.changeRoom(room.id)
}
}
}
複製代碼
回想一下,已經在b-list-group-item元素中定義了@click="onChange(room)",點擊RoomList組件中的項測試一下這個新功能。
點擊每一個房間,UI應該都會更新,每次選擇房間,MessageList和UserList組件都應該顯示正確的信息。下一節,將一次實現多個功能。
你可能注意到了,當對store/index.js作一些更新,或者刷新頁面的時候,會出現以下 錯誤:Cannot read property 'subscribeToRoom' of null,這是由於應用的state進行了重置。幸虧,在頁面刷新時,vuex-persist包將Vuex state維護在了瀏覽器的本地存儲裏。
鏈接應用和ChatKit服務端的引用也被置回了null值,爲了解決這個問題,須要執行重連操做。同時須要一種方式告訴應用頁面進行過刷新,爲了繼續進行正常的功能應用須要重連。在src/components/ChatNavbar.vue中實現了這部分的代碼,更新腳本以下:
<script>
import { mapState, mapActions, mapMutations } from 'vuex'
export default {
name: 'ChatNavBar',
computed: {
...mapState([
'user',
'reconnect'
])
},
methods: {
...mapActions([
'logout',
'login'
]),
...mapMutations([
'setReconnect'
]),
onLogout() {
this.$router.push({ path: '/' });
this.logout();
},
unload() {
if(this.user.username) { // User hasn't logged out this.setReconnect(true); } } }, mounted() { window.addEventListener('beforeunload', this.unload); if(this.reconnect) { this.login(this.user.username); } } } </script> 複製代碼
分析一下事件的順序,以便可以理解從新鏈接到ChatKit服務背後的邏輯:
1.unload 當頁面刷新時,該方法會被調用,它先檢查user.username state是否進行過設置,若是是,意味着用戶沒有登出,reconnect state設置爲true
2. mounted 每次ChatNavbar.vue完成渲染該方法就會被調用,它先向事件監聽器分派一個處理器(unload),在頁面卸載前調用這個處理器(unload)。mounted內還檢查了若是 state.reconnect是true的話,登陸程序會被執行,經過這樣將聊天應用重連到ChatKit服務上。
還有個Logout功能,後面會細述這個功能。
作了以上更新以後,再試關刷新一下頁面,會看到頁面會自動,由於重連的過程是在後臺完成的,當切換房間的時候,也能完美的運行。
先添加以下代碼來實現以上功能:
//...
async function sendMessage(text) {
const messageId = await currentUser.sendMessage({
text,
roomId: activeRoom.id
});
return messageId;
}
export function isTyping(roomId) {
currentUser.isTypingIn({ roomId });
}
function disconnectUser() {
currentUser.disconnect();
}
export default {
connectUser,
subscribeToRoom,
sendMessage,
disconnectUser
}
複製代碼
函數sendMessage和disconnectUser會打包在ChatKit的模塊裏,isTyping函數會被單獨export出來。這是爲了容許MessageForm在不涉及Vuex存儲的狀況下直接發送鍵入事件。
對於sendMessage和disconnectUser,須要更新存儲以知足錯誤處理和加載狀態通知等要求。打開src/store/actions.js在changeRoom後插入以下代碼:
async sendMessage({ commit }, message) {
try {
commit('setError', '');
commit('setSending', true);
const messageId = await chatkit.sendMessage(message);
return messageId;
} catch (error) {
handleError(commit, error)
} finally {
commit('setSending', false);
}
},
async logout({ commit }) {
commit('reset');
chatkit.disconnectUser();
window.localStorage.clear();
}
複製代碼
對於logout函數,咱們調用commit('reset')來將state重置爲原始state。這是一個基礎的從瀏覽器移除用戶信息和消息的安全功能。
下面開始更新src/components/MessageForm.vue內的表單文本框,經過添加@input指令來觸發鍵入事件。
<b-form-input id="message-input"
type="text"
v-model="message"
@input="isTyping"
placeholder="Enter Message"
autocomplete="off"
required>
</b-form-input>
複製代碼
如今更新src/components/MessageForm.vue中的script部分,爲了處理消息發送和觸發鍵入事件。更新以下:
<script>
import { mapActions, mapState, mapGetters } from 'vuex'
import { isTyping } from '../chatkit.js'
export default {
name: 'message-form',
data() {
return {
message: ''
}
},
computed: {
...mapState([
'user',
'sending',
'error',
'activeRoom'
]),
...mapGetters([
'hasError'
])
},
methods: {
...mapActions([
'sendMessage',
]),
async onSubmit() {
const result = await this.sendMessage(this.message);
if(result) {
this.message = '';
}
},
async isTyping() {
await isTyping(this.activeRoom.id);
}
}
}
</script>
複製代碼
還有在src/MessageList.vue中:
import { mapState } from 'vuex'
export default {
name: 'message-list',
computed: {
...mapState([
'messages',
'userTyping'
])
}
}
複製代碼
如今發送消息的功能應該實現了。爲了顯示另外用戶的輸入,須要一個顯示這些信息的元素。在src/components/MessageList.vue的template中添加以下代碼片斷,添加到message-troup div以後。
<div class="user-typing">
<small class="text-muted" v-if="userTyping">@{{ userTyping }} is typing....</small>
</div>
複製代碼
爲了測試這一功能,只須要使用另一個瀏覽器登陸其餘用戶並開始輸入內容,會看到在其餘用戶的聊天窗口中有通知出現。
完成本文只須要再完成最後一個功能logout。Vuex存儲已經有登出程序必要的代碼,咱們只須要更新一下src/components/ChatNavBar.vue,將Logout按鈕與以前指定好的onLogout函數關聯起來:<b-nav-item href="#" @click="onLogout" active>Logout</b-nav-item>
複製代碼
這樣就能夠了,如今能夠登出而後再用另外的用戶登陸。
終於到了文章的最後,ChatKit API讓咱們能在很短的時間內快速的建立一個聊天應用。若是要重頭構建一個聊天程序可能須要好幾周的時間,由於咱們還得把後臺補上。這個解決方案的優勢是咱們沒必要處理託管、數據庫管理和其餘基礎設施問題。咱們能夠構建併發布前端代碼到web、Android和IOS平臺的客戶端設備上。