Создание клона Google Keep с помощью Vue и Firebase, часть 2

В предыдущей части мы создали простой клон Google Keep, в котором заметки красиво оформлены библиотекой Masonry.

Хотя это отличное начало, нам все еще не хватает нескольких частей типичного приложения CRUD. Нам еще нужно написать функции Обновления и Удалить. Другая проблема заключается в том, что наш код Firebase разбросан по разным компонентам нашего проекта. Мы должны избавиться от дублирования и переместить весь код, связанный с Firebase, в один модуль. В результате у нас будет более СУХОЕ приложение (не повторяйтесь).

Создание независимого слоя данных

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

Помещение логики общих данных в модуль имеет множество преимуществ: лучшая абстракция, более простой рефакторинг, меньше дублирования или DRY'er, а также обеспечивается возможность повторного использования.

Создайте новый файл в новой папке: src/data/NoteRepository.js. В этом файле будет храниться весь код Firebase для управления заметками.
Создайте класс NoteRepository. Этот класс будет содержать всю логику для создания, обновления и удаления заметок.

Класс также будет отвечать за прослушивание всех событий Firebase внутри класса. Но компоненты также должны иметь возможность прослушивать эти события. Вы можете добиться этого, наследуя от класса EventEmitter, который предоставляет Node (см. документы). Как только класс расширяет EventEmitter, компоненты могут прикреплять слушателей к NoteRepository через on-method. Внутри класса вы можете запускать события с помощью emit-method. В сочетании с унаследованной функциональностью EventEmitter NoteRepository может прослушивать события Firebase, обрабатывать данные и распространять события наружу, чтобы компоненты получали обработанные данные.

источник/данные/NoteRepository.js

import EventEmitter from 'events' // extend EventEmitter so user of NoteRepository can react to our own defined events (ex: noteRepository.on('added')) class NoteRepository extends EventEmitter { constructor () { super() } } export default new NoteRepository() // this instance will be shared across imports

В конструкторе добавьте свойство для хранения ссылки Firebase на наши заметки. Затем подключите слушателей к событиям Firebase. Эти прослушиватели должны создать объект-заметку и распространить событие + заметку наружу с помощью метода-эмитирования.

Раньше для отображения заметок требовался только заголовок и содержимое. Но для обновления и удаления заметки вам понадобится уникальный идентификатор. В Firebase каждый элемент имеет уникальный ключ, который либо задается программно, либо автоматически генерируется Firebase. Вы можете получить ключ, введя key-function. Перед отправкой события добавьте ключ к заметке.

источник/данные/NoteRepository.js

import Firebase from 'firebase' import EventEmitter from 'events' // extend EventEmitter so user of NoteRepository can react to our own defined events (ex: noteRepository.on('added')) class NoteRepository extends EventEmitter { constructor () { super() // firebase reference to the notes this.ref = new Firebase('https://.firebaseio.com/notes') // will have same result as new Firebase('https://.firebaseio.com/').child('notes') this.attachFirebaseListeners() } // attach listeners to Firebase attachFirebaseListeners () { this.ref.on('child_added', this.onAdded, this) this.ref.on('child_removed', this.onRemoved, this) this.ref.on('child_changed', this.onChanged, this) } onAdded (snapshot) { // process data let note = this.snapshotToNote(snapshot) // propagate event outwards with note this.emit('added', note) } onRemoved (oldSnapshot) { let note = this.snapshotToNote(oldSnapshot) this.emit('removed', note) } onChanged (snapshot) { let note = this.snapshotToNote(snapshot) this.emit('changed', note) } // processes the snapshots to consistent note with key snapshotToNote (snapshot) { // we will need the key often, so we always want to have the key included in the note let key = snapshot.key() let note = snapshot.val() note.key = key return note } } export default new NoteRepository() // this instance will be shared across imports

Теперь компоненты могут прослушивать репозиторий, но функции создания, обновления и удаления по-прежнему отсутствуют.
Создать: напишите метод создания, который имеет сигнатуру, совпадающую с методом отправки Firebase, и передайте параметры этому методу отправки. Он должен принимать примечание и обратный вызов onComplete, который является необязательным.

create ({title = '', content = ''}, onComplete) { this.ref.push({title, content}, onComplete) }

Обновление: напишите метод обновления, который принимает объект примечания (включая ключ) и обратный вызов onComplete. Чтобы обновить заметку в Firebase, вам нужно получить ссылку на конкретный объект. Вы можете создать ссылку, передав уникальный ключ дочерней функции ссылки на заметки. Это создаст новую ссылку Firebase, добавив ключ к URL-адресу родительской ссылки. Затем вы можете вызвать метод обновления для новой дочерней ссылки, передавая объект примечания (убедитесь, что вы не передаете ключ).

update ({key, title = '', content = ''}, onComplete) { this.ref.child(key).update({title, content}, onComplete) // key is used to find the child, a new note object is made without the key, to prevent key being inserted in Firebase // this.ref.child(key) will create new reference like this new Firebase(`https://.firebaseio.com/notes/${key}`) }

Удалить: напишите метод удаления, который принимает объект примечания (включая ключ) и обратный вызов onComplete. Вам понадобится только ключ заметки, чтобы удалить заметку в Firebase, но для простоты использования и согласованности вы можете просто принять объект и взять только ключ. Получите ссылку на заметку через дочерний метод и вызовите метод удаления.

// removes a note remove ({key}, onComplete) { this.ref.child(key).remove(onComplete) }

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

источник/данные/NoteRepository.js

import Firebase from 'firebase' import EventEmitter from 'events' // extend EventEmitter so user of NoteRepository can react to our own defined events (ex: noteRepository.on('added')) class NoteRepository extends EventEmitter { constructor () { super() // firebase reference to the notes this.ref = new Firebase('https://.firebaseio.com/notes') // will have same result as new Firebase('https://.firebaseio.com/').child('notes') this.attachFirebaseListeners() } // creates a note create ({title = '', content = ''}, onComplete) { this.ref.push({title, content}, onComplete) } // updates a note update ({key, title = '', content = ''}, onComplete) { this.ref.child(key).update({title, content}, onComplete) // key is used to find the child, a new note object is made without the key, to prevent key being inserted in Firebase // this.ref.child(key) will create new reference like this new Firebase(`https://.firebaseio.com/notes/${key}`) } // removes a note remove ({key}, onComplete) { this.ref.child(key).remove(onComplete) } // attach listeners to Firebase attachFirebaseListeners () { this.ref.on('child_added', this.onAdded, this) this.ref.on('child_removed', this.onRemoved, this) this.ref.on('child_changed', this.onChanged, this) } // dettach listeners from Firebase detachFirebaseListeners () { this.ref.off('child_added', this.onAdded, this) this.ref.off('child_removed', this.onRemoved, this) this.ref.off('child_changed', this.onChanged, this) } onAdded (snapshot) { // process data let note = this.snapshotToNote(snapshot) // propagate event outwards with note this.emit('added', note) } onRemoved (oldSnapshot) { let note = this.snapshotToNote(oldSnapshot) this.emit('removed', note) } onChanged (snapshot) { let note = this.snapshotToNote(snapshot) this.emit('changed', note) } // processes the snapshots to consistent note with key snapshotToNote (snapshot) { // we will need the key often, so we always want to have the key included in the note let key = snapshot.key() let note = snapshot.val() note.key = key return note } // Finds the index of the note inside the array by looking for its key findIndex (notes, key) { return notes.findIndex(note => note.key === key) } // Finds the note inside the array by looking for its key find (notes, key) { return notes.find(note => note.key === key) } } export default new NoteRepository() // this instance will be shared across imports

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

Рефакторинг компонентов для использования уровня данных

Первоначально опубликовано на www.laravelfeed.com.