Статьи

Создайте приложение для чата в реальном времени с помощью Pusher и Vue.js

Приложения, которые общаются в режиме реального времени, становятся все более и более популярными в настоящее время, поскольку они обеспечивают более плавное и естественное взаимодействие с пользователем.

В этом руководстве мы собираемся создать приложение для чата в реальном времени, используя Vue.js на основе ChatKit , службы, предоставляемой Pusher . Сервис ChatKit предоставит нам полный бэкэнд, необходимый для создания приложения чата на любом устройстве, что позволит нам сосредоточиться на создании интерфейсного пользовательского интерфейса, который подключается к сервису ChatKit через клиентский пакет ChatKit .

Хотите узнать Vue.js с нуля? Получите полную коллекцию книг Vue, охватывающих основы, проекты, советы и инструменты и многое другое с SitePoint Premium. Присоединяйтесь сейчас всего за $ 9 / месяц .

Предпосылки

Это учебник среднего и продвинутого уровня. Вы должны быть знакомы со следующими концепциями, чтобы следовать:

  • Основы Vue.js
  • Vuex основы
  • используя CSS-фреймворк

Вам также потребуется узел, установленный на вашем компьютере. Вы можете сделать это, загрузив двоичные файлы с официального сайта или используя менеджер версий . Это, вероятно, самый простой способ, поскольку он позволяет вам управлять несколькими версиями Node на одном компьютере.

Наконец, вам нужно установить Vue CLI глобально с помощью следующей команды:

npm install -g @vue/cli 

На момент написания, Node 10.14.1 и Vue CLI 3.2.1 являются последними версиями.

О проекте

Мы собираемся создать простейшее приложение для чата, похожее на Slack или Discord. Приложение будет делать следующее:

  • есть несколько каналов и комнат
  • список членов комнаты и определение статуса присутствия
  • определить, когда другие пользователи начинают печатать

Как упоминалось ранее, мы просто строим интерфейс. Сервис ChatKit имеет внутренний интерфейс, который позволяет нам управлять пользователями, разрешениями и комнатами.

Вы можете найти полный код этого проекта на GitHub .

Настройка экземпляра ChatKit

Давайте создадим наш экземпляр ChatKit, который похож на экземпляр сервера, если вы знакомы с Discord.

Перейдите на страницу ChatKit на веб-сайте Pusher и нажмите кнопку « Зарегистрироваться» . Вам будет предложено ввести адрес электронной почты и пароль, а также возможность войти в GitHub или Google.

Выберите, какой вариант подходит вам больше всего, затем на следующем экране заполните некоторые данные, такие как Имя , Тип учетной записи , Роль пользователя и т. Д.

Нажмите Complete Onboarding, и вы попадете на главную панель инструментов Pusher. Здесь вы должны нажать на продукт ChatKit.

Панель управления ChatKit

Нажмите кнопку «Создать», чтобы создать новый экземпляр ChatKit. Я собираюсь позвонить в мой VueChatTut .

Создание нового экземпляра ChatKit

Мы будем использовать бесплатный план для этого урока. Он поддерживает до 1000 уникальных пользователей, что более чем достаточно для наших нужд. Перейдите на вкладку Консоль . Вам нужно будет создать нового пользователя, чтобы начать. Идите дальше и нажмите кнопку « Создать пользователя» .

Создание пользователя ChatKit

Я собираюсь назвать мои «Джон» ( идентификатор пользователя ) и «Джон Вик» ( отображаемое имя ), но вы можете назвать свое имя так, как хотите. Следующая часть проста: создать двух или более пользователей. Например:

  • соль, соль эвелин
  • охота, Итан Хант

Создайте три или более комнат и назначьте пользователей. Например:

  • Генерал (Джон, соль, охота)
  • Оружие (Джон, соль)
  • Бой (Джон, Хант)

Вот снимок того, что должен понравиться ваш интерфейс консоли .

Снимок консоли

Далее вы можете перейти на вкладку Комнаты и создать сообщение, используя выбранного пользователя для каждой комнаты. Это для целей тестирования. Затем перейдите на вкладку « Учетные данные » и обратите внимание на локатор экземпляров . Нам нужно активировать провайдера тестовых токенов , который используется для генерации нашей конечной точки HTTP, и принять это к сведению.

Тестовый токен

Наш бэкэнд ChatKit теперь готов. Давайте начнем создавать наш внешний интерфейс Vue.js.

Строительные леса проекта Vue.js

Откройте свой терминал и создайте проект следующим образом:

 vue create vue-chatkit 

Выберите Вручную выберите функции и ответьте на вопросы, как показано ниже.

Вопросы для ответа

Вдвойне убедитесь, что вы выбрали Babel, Vuex и Vue Router в качестве дополнительных функций. Затем создайте следующие папки и файлы следующим образом:

Файлы и папки для создания

Убедитесь, что вы создали все папки и файлы, как показано. Удалите все ненужные файлы, которые не отображаются на рисунке выше.

Для тех из вас, кто находится дома в консоли, вот команды, чтобы сделать все это:

 mkdir src/assets/css mkdir src/store touch src/assets/css/{loading.css,loading-btn.css} touch src/components/{ChatNavBar.vue,LoginForm.vue,MessageForm.vue,MessageList.vue,RoomList.vue,UserList.vue} touch src/store/{actions.js,index.js,mutations.js} touch src/views/{ChatDashboard.vue,Login.vue} touch src/chatkit.js rm src/components/HelloWorld.vue rm src/views/{About.vue,Home.vue} rm src/store.js 

Когда вы закончите, содержимое папки src должно выглядеть так:

 . ├── App.vue ├── assets │ ├── css │ │ ├── loading-btn.css │ │ └── loading.css │ └── logo.png ├── chatkit.js ├── components │ ├── ChatNavBar.vue │ ├── LoginForm.vue │ ├── MessageForm.vue │ ├── MessageList.vue │ ├── RoomList.vue │ └── UserList.vue ├── main.js ├── router.js ├── store │ ├── actions.js │ ├── index.js │ └── mutations.js └── views ├── ChatDashboard.vue └── Login.vue 

Для файлов loading-btn.css и loading.css их можно найти на веб-сайте loading.io . Эти файлы недоступны в репозитории npm, поэтому вам необходимо вручную загрузить их и поместить в ваш проект. Обязательно прочитайте документацию, чтобы понять, что они из себя представляют и как использовать настраиваемые загрузчики.

Далее мы собираемся установить следующие зависимости:

  • @ pusher / chatkit-client , клиентский интерфейс реального времени для сервиса ChatKit
  • bootstrap-vue , CSS-фреймворк
  • момент , утилита форматирования даты и времени
  • vue-chat-scroll , которая автоматически прокручивается вниз при добавлении нового контента
  • vuex-persist , который сохраняет состояние Vuex в локальном хранилище браузера
 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 }) 

Пакет vuex-persist гарантирует, что наше состояние Vuex сохраняется между перезагрузками страниц или обновлениями.

Наш проект должен быть в состоянии скомпилировать сейчас без ошибок. Однако пока не запускайте его, так как нам нужно создать пользовательский интерфейс.

Создание интерфейса UI

Начнем с обновления src/App.vue следующим образом:

 <template> <div id="app"> <router-view/> </div> </template> 

Далее нам нужно определить наши состояния хранилища Vuex, поскольку они требуются для работы наших компонентов пользовательского интерфейса. Мы сделаем это, перейдя в наш магазин Vuex в 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 }, 

Это все переменные состояния, которые нам понадобятся для нашего приложения чата. Состояние loading используется пользовательским интерфейсом, чтобы определить, должен ли он запускать загрузчик CSS. Состояние error используется для хранения информации об ошибке, которая только что произошла. Мы обсудим остальные переменные состояния, когда пересечем их мосты.

Затем откройте 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> 

Как упоминалось ранее, это продвинутый учебник. Если у вас возникли проблемы с пониманием какого-либо кода здесь, перейдите к предварительным условиям или зависимостям проекта для получения информации.

Теперь мы можем запустить сервер Vue dev с помощью npm run serve чтобы убедиться, что наше приложение работает без проблем с компиляцией.

Наш логин

Вы можете подтвердить, что проверка работает, введя имя пользователя. Вы должны увидеть активированную кнопку входа после ввода трех символов. Кнопка входа в систему на данный момент не работает, так как мы не кодировали эту часть. Мы рассмотрим это позже. А пока давайте продолжим создание нашего пользовательского интерфейса чата.

Перейдите в src/view/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 будет действовать как родительский макет для следующих дочерних компонентов:

  • ChatNavBar , базовая панель навигации
  • RoomList , в котором перечислены комнаты, к которым имеет доступ зарегистрированный пользователь, и который также является селектором комнат
  • UserList , в котором перечислены члены выбранной комнаты
  • MessageList , в котором отображаются сообщения, опубликованные в выбранной комнате
  • MessageForm , форма для отправки сообщений в выбранную комнату

Давайте поместим некоторый шаблонный код в каждый компонент, чтобы все отображалось.

Вставьте шаблонный код для src/components/ChatNavBar.vue следующим образом:

 <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 и src/store/index.js некоторые фиктивные данные в состояние:

 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: { loading: false, sending: false, error: null, user: null, reconnect: false, activeRoom: null, rooms: [], users: [], messages: [], userTyping: null } 

Давайте начнем реализовывать конкретные функции, начиная с формы входа в систему.

Аутентификация без пароля

В этом руководстве мы будем использовать незащищенную систему аутентификации без пароля. Надлежащая, безопасная система аутентификации выходит за рамки данного руководства. Для начала нам нужно начать создавать собственный интерфейс, который будет взаимодействовать со службой ChatKit через пакет @pusher/chatkit-client .

Вернитесь на панель управления ChatKit и скопируйте экземпляр и протестируйте параметры токена . Сохраните их в файле .env.local в корне вашего проекта следующим образом:

 VUE_APP_INSTANCE_LOCATOR= VUE_APP_TOKEN_URL= VUE_APP_MESSAGE_LIMIT=10 

Я также добавил параметр MESSAGE_LIMIT . Это значение просто ограничивает количество сообщений, которые может получить наше приложение чата. Обязательно заполните другие параметры на вкладке учетных данных.

Затем перейдите в 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 константу MESSAGE_LIMIT к числу, так как по умолчанию объект process.env заставляет все его свойства иметь тип string.

Вставьте следующий код для 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 } } 

Код для мутаций действительно прост — просто набор сеттеров. Вскоре вы поймете, для чего нужна каждая функция мутации, в следующих разделах. Затем обновите 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'); } } } } 

Вам нужно будет перезапустить сервер Vue.js, чтобы загрузить данные env.local . Если вы видите какие-либо ошибки в отношении неиспользуемых переменных, игнорируйте их сейчас. После этого перейдите по адресу http: // localhost: 8080 / и протестируйте функцию входа в систему:

Ошибка входа

В приведенном выше примере я использовал неправильное имя пользователя, чтобы убедиться, что функция обработки ошибок работает правильно.

Успешный вход

На этом скриншоте я использовал правильное имя пользователя. Я также открыл вкладку консоли браузера, чтобы убедиться, что user объект заполнен. Более того, если вы установили Vue.js Dev Tools в Chrome или Firefox , вы сможете увидеть более подробную информацию.

Более подробная информация для входа

Если у вас все работает правильно, перейдите к следующему шагу.

Подписка на номер

Теперь, когда мы успешно убедились, что функция входа в систему работает, нам нужно перенаправить пользователей в представление ChatDashboard . Код this.$router.push('chat'); делает это для нас. Однако для login необходимо вернуть логическое значение, чтобы определить, когда можно перейти к представлению ChatDashboard . Нам также необходимо UserList компоненты RoomList и UserList фактическими данными из службы ChatKit.

Обновите 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 для связи с нашим клиентским приложением. Вы можете найти полную документацию здесь . Я быстро подведу итог каждому методу подключения:

  • onMessage получает сообщения
  • onPresenceChanged получает событие, когда пользователь входит или выходит
  • onUserStartedTyping получает событие, которое печатает пользователь
  • onUserStoppedTyping получает событие, когда пользователь прекратил печатать

Чтобы onUserStartedTyping работал, нам нужно onUserStartedTyping событие ввода из нашей MessageForm пока пользователь печатает. Мы рассмотрим это в следующем разделе.

Обновите функцию login в login в src/store/actions.js следующим кодом:

 //... 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) { //... } 

После сохранения кода вернитесь к экрану входа и введите правильное имя пользователя. Вы должны перейти на следующий экран.

Текущая панель чата

Ницца! Почти все компоненты работают без дополнительных усилий, так как мы правильно подключили их к магазину Vuex. Попробуйте отправить сообщение через интерфейс консоли ChatKit. Создать сообщение и опубликовать его в General комнате. Вы должны увидеть, что новые сообщения автоматически MessageList компоненте MessageList . Вскоре мы реализуем логику отправки сообщений из нашего приложения Vue.js.

Если у вас возникли проблемы

Если у вас возникли проблемы, попробуйте следующее:

  • перезапустите сервер Vue.js
  • очистить кеш браузера
  • выполнить полный сброс / обновление (доступно в Chrome, если открыта вкладка « Консоль » и вы удерживаете кнопку « Обновить» в течение пяти секунд)
  • очистить localStorage с помощью консоли браузера

Если все работает нормально до этого момента, переходите к следующему разделу, где мы реализуем логику для смены комнат.

Примерочные

Эта часть довольно проста, так как мы уже заложили основы. Сначала мы создадим action , которое позволит пользователям менять комнаты. Перейдите в 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 и обновите раздел скрипта следующим образом:

 import { mapState, mapActions } from 'vuex' //... export default { //... methods: { ...mapActions([ 'changeRoom' ]), onChange(room) { this.changeRoom(room.id) } } } 

Если вы помните, мы уже определили @click="onChange(room)" в элементе b-list-group-item . Давайте протестируем эту новую функцию, щелкнув элементы в компоненте RoomList .

Смена чатов

Ваш пользовательский интерфейс должен обновляться с каждым щелчком комнаты. Компоненты MessageList и UserList должны отображать правильную информацию для выбранной комнаты. В следующем разделе мы реализуем несколько функций одновременно.

Повторное подключение пользователя после обновления страницы

Возможно, вы заметили, что когда вы store/index.js некоторые изменения в store/index.js или обновляете страницу, вы получаете следующую ошибку: Cannot read property 'subscribeToRoom' of null . Это происходит потому, что состояние вашего приложения сбрасывается. К счастью, пакет vuex-persist поддерживает наше состояние Vuex между перезагрузками страницы, сохраняя его в локальном хранилище браузера.

К сожалению, ссылки, которые связывают наше приложение с сервером ChatKit, возвращаются к нулю. Чтобы это исправить, нам нужно выполнить операцию повторного подключения. Нам также нужен способ сообщить нашему приложению, что перезагрузка страницы только что произошла и что наше приложение должно повторно подключиться, чтобы продолжить нормальную работу. Мы реализуем этот код в 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 . Если это так, это означает, что пользователь не вышел из системы. Состояние reconnect установлено в значение true.
  2. mounted Этот метод ChatNavbar.vue каждый раз, когда ChatNavbar.vue только что закончил рендеринг. Сначала он назначает обработчик для прослушивателя событий, который вызывается непосредственно перед выгрузкой страницы. Он также выполняет проверку, если для state.reconnect установлено значение true. Если так, то процедура входа в систему выполняется, таким образом, повторно подключая наше приложение чата к нашей службе ChatKit.

Я также добавил функцию Logout из Logout , которую мы рассмотрим позже.

После внесения этих изменений попробуйте обновить страницу. Вы увидите автоматическое обновление страницы, поскольку она выполняет процесс переподключения за кулисами. Когда вы меняете комнаты, это должно работать без нареканий.

Отправка сообщений, обнаружение ввода пользователя и выхода из системы

Давайте начнем с реализации этих функций в src/chatkit.js , добавив следующий код:

 //... 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 будут включены в экспорт модуля isTyping функция isTyping будет экспортироваться отдельно. Это позволяет 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 из logout мы вызываем commit('reset') чтобы вернуть наше хранилище в исходное состояние. Это базовая функция безопасности для удаления информации пользователя и сообщений из кэша браузера.

Давайте начнем с обновления входных данных формы в src/components/MessageForm.vue для @input событий ввода, добавив директиву @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> 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 сразу после div message-group :

 <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> 

Вот и все. Теперь вы можете выйти и войти снова как другой пользователь.

Переключение между учетными записями участников

Резюме

Теперь мы подошли к концу урока. API ChatKit позволил нам быстро создать приложение для чата за короткое время. Если бы мы создавали подобное приложение с нуля, это заняло бы у нас несколько недель, так как нам пришлось бы также детализировать серверную часть. Самое замечательное в этом решении то, что нам не нужно заниматься хостингом, управлением базами данных и другими проблемами инфраструктуры. Мы можем просто создавать и развертывать интерфейсный код на клиентских устройствах на веб-платформах, платформах Android и IOS.

Пожалуйста, ознакомьтесь с документацией, так как есть множество серверных функций, которые я не смог показать вам в этом уроке. Со временем вы можете легко создать полнофункциональное приложение для чата, которое может конкурировать с популярными продуктами чата, такими как Slack и Discord.