В этой части мы рассмотрим перенос вещей в отдельные компоненты.
Вот все части этой серии:
Github: https://github.com/jespr/vue-phoenix-chat
Heroku: https://stormy-inlet-39179.herokuapp.com/
Часть 1 - Введение и получение базового веб-приложения с функциями чата.
Часть 2 - Дайте возможность пользователю идентифицировать себя по имени перед тем, как присоединиться к чату.
Часть 3 - узнайте, кто сейчас в чате с вами
Часть 4 - Красивый дизайн + забавные переходы
Часть 5 - Настойчивость.
Часть 6 (эта статья) - Помещение объектов в отдельные компоненты
Сейчас у нас все в одном компоненте. Все может быстро запутаться, и прежде чем мы перейдем к введению нескольких чатов, давайте разберемся по разным компонентам.
У нас есть две очень четкие вещи, которые можно выделить в компоненты. У нас есть элемент боковой панели, который показывает пользователей, подключенных к чату, и у нас есть список сообщений.
Для создания компонентов было бы неплохо иметь одно место для обработки состояния. Прямо сейчас, поскольку все находится в одном компоненте, мы просто назначаем пользователей, сообщения и т. Д. Как часть data()
в этом компоненте.
Для этого мы представим новый пакет под названием Vuex. Vuex - это (как это красиво сказано) централизованное управление состоянием для vue.js.
Итак, давайте установим это, запустив:
npm install vuex --save
Это также добавит его к нашим зависимостям в package.json.
Теперь нам нужно создать магазин. Итак, давайте создадим новый файл web/static/js/store.js
Этот файл должен содержать следующее. Есть несколько комментариев, чтобы рассказать, за что каждая вещь отвечает.
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) // root state object. // each Vuex instance is just a single state tree. const state = { users: [] } // mutations are operations that actually mutates the state. // each mutation handler gets the entire state tree as the // first argument, followed by additional payload arguments. // mutations must be synchronous and can be recorded by plugins // for debugging purposes. const mutations = { } // actions are functions that causes side effects and can involve // asynchronous operations. const actions = { } // getters are functions const getters = { } // A Vuex instance is created by combining the state, mutations, actions, // and getters. export default new Vuex.Store({ state, getters, actions, mutations })
Теперь мы можем сообщить приложению Vue, что мы используем магазин, добавив его в web/static/js/app.js
, поэтому давайте откроем этот файл и добавим в него магазин.
Вверху добавьте следующее:
import store from "./store.js"
Теперь это выглядит так:
import "phoenix_html" import Vue from 'vue' import MyApp from "../components/my-app.vue" import store from "./store.js"
Затем при инициализации нашего приложения Vue мы добавляем store
непосредственно перед нашим вызовом render
:
// And create the top-level view model: new Vue({ el: '#app', store, render(createElement) { return createElement(MyApp, {}) } });
Идеально! Теперь давайте продолжим и извлечем наш список пользователей в его собственный компонент. Откройте web/static/components/my-app.vue
и найдите div с идентификатором users-list
. Скопируйте все div:
<div id="users-list"> <h3>Online</h3> <ul> <transition-group name="user-appear"> <li v-for="user in users" v-bind:key="user.user"> {{user.user}} ({{user.online_at}}) </li> </transition-group> </ul> </div>
Давайте продолжим и создадим этот новый файл компонента. web/static/components/users-list.vue
Давайте начнем с добавления HTML, который мы только что скопировали, в шаблон этого компонента. Итак, в верхней части нового файла добавьте скопированный HTML-код внутри тегов <template>..</template>
, чтобы он выглядел так:
<template> <div id="users-list"> <h3>Online</h3> <ul> <transition-group name="user-appear"> <li v-for="user in users" v-bind:key="user.user"> {{user.user}} ({{user.online_at}}) </li> </transition-group> </ul> </div> </template>
Давайте добавим наш script
раздел ниже:
<script> export default { computed: { users() { return this.$store.state.users; } } } </script>
Как вы могли заметить, мы добавили новую вычисляемую функцию с именем users()
, которая ссылается на users
, исходящую из глобального состояния. Это прямо сейчас ссылается на пустой массив, который мы добавили в наш store.js
.
Теперь давайте использовать этот новый компонент внутри web/static/components/my-app.vue
вместо того, чтобы иметь логику непосредственно внутри этого одного компонента. Вверху раздела script
мы импортируем новый компонент, добавив:
import UsersList from "./users-list"
Затем нам нужно будет зарегистрировать этот компонент в web/static/components/my-app.vue
, чтобы мы могли использовать его в шаблоне. Итак, добавьте новый объект components
под функцией data()
:
components: { 'users-list': UsersList },
Теперь мы также можем продолжить и удалить users: []
из функции data()
в my-app.vue
. У вас должно получиться что-то вроде:
data(): { return { ... } }, components: { 'users-list': UsersList }, methods: { ... }
Я заменил фактическое содержание data()
и methods
на ...
для простоты.
Теперь вы можете перейти к части шаблона my-app.vue
и заменить <div id="users-list">..</div>
на <users-list/>
. Хороший!
Итак, теперь у вас будет начало блока main-container
, которое выглядит следующим образом:
<div id="main-container" v-else> <users-list/> <div id="messages-list"> <ul> ...
Если вы обновите приложение и введете имя, вы увидите, что пользователи не отображаются. Очевидно, это потому, что мы ссылаемся на пустой массив users
из глобального хранилища состояний.
Поэтому, когда мы получаем пользователей в my-app.vue
, где мы обычно назначаем их локальным данным пользователей, мы будем знать, что вместо этого обновим глобальное хранилище.
Внутри web/static/components/my-app.vue
найдите функцию assignUsers
и измените ее на это:
assignUsers(presences) { let users = Presence.list(presences, (user, {metas: metas}) => { return { name: user, online_at: metas[0].online_at } }) this.$store.commit('addUsers', { users }) }
Как вы можете видеть, теперь мы получаем всех пользователей из списка присутствия и возвращаем объекты, содержащие name
и online_at
. Затем мы передаем это в store
, вызывая commit
- кстати, вы всегда можете ссылаться на глобальное хранилище через this.$store
.
Давайте продолжим, откроем web/static/js/store.js
и добавим туда мутацию addUsers
.
Итак, внутри const mutations = { ... }
вы добавите следующее, чтобы оно выглядело так:
const mutations = { addUsers (state, { users }) { state.users = users } }
Здесь мы просто назначаем массив пользователей атрибуту state.users
.
Затем нам нужно внести изменения в наш users-list
компонент, поэтому откройте web/static/components/users-list.vue
Давайте немного изменим вычисляемую функцию пользователей, чтобы она выглядела так:
computed: { users() { let formatTimestamp = (timestamp) => { timestamp = parseInt(timestamp) let date = new Date(timestamp) return date.toLocaleTimeString() } return this.$store.state.users.map(function(user) { return { name: user.name, online_at: formatTimestamp(user.online_at) } }) } } }
Как видите, я изменил имя пользователя с user
на name
, поэтому давайте внесем это изменение и в наш шаблон:
<li v-for="user in users" v-bind:key="user.name"> {{user.name}} ({{user.online_at}}) </li>
Здесь я изменил {{user.user}}
на {{user.name}}
, а также привязку клавиш с v-bind:key="user.user"
на v-bind:key="user.name"
.
Вуаля! Все должно работать и выглядеть одинаково!
Теперь давайте проделаем то же самое со списком сообщений. Ради того, чтобы этот пост был простым и короче - я сделаю это, а вы можете проверить фиксацию на Github. Хорошим упражнением было бы посмотреть, сможете ли вы сделать это самостоятельно, основываясь на том, что мы только что сделали со списком пользователей.