Как создать приложение Vue Survey с использованием аутентификации и базы данных Firebase

Опубликовано: 2022-03-10
Краткое резюме ↬ В этом учебном пособии вы найдете пошаговое руководство по созданию функционального приложения для опросов с использованием Vue.js и Firebase. От проверки данных пользователя через Vuelidate до аутентификации, хранения данных пользователя, защиты маршрута и отправки данных на серверы Firebase. Все шаги, используемые в руководстве, практичны и могут быть воспроизведены в любом реальном проекте, даже с пользовательским бэкендом.

В этом руководстве вы создадите приложение для опроса, в котором мы научимся проверять данные формы наших пользователей, внедрять аутентификацию в Vue и сможем получать данные опроса с помощью Vue и Firebase (платформа BaaS).

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

Кроме того, приложение будет обрабатывать вход пользователя в систему с помощью API-интерфейсов Restful. Он будет использовать Authguard в маршрутизаторе Vue, чтобы запретить пользователям, не вошедшим в систему, получать доступ к форме опроса и успешно отправлять данные опроса вошедших пользователей в защищенную базу данных.

Просто чтобы мы были на одной странице, давайте проясним, что такое Firebase и что он будет делать в этом руководстве. Firebase — это набор инструментов для «создания, улучшения и расширения вашего приложения». Он дает вам доступ к большей части сервисов, которые разработчикам обычно приходится создавать самим, но они на самом деле не хотят создавать, потому что они сосредоточиться на самом приложении. Это включает в себя такие вещи, как аналитика, аутентификация, базы данных, хранилище файлов, и этот список можно продолжить.

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

Еще после прыжка! Продолжить чтение ниже ↓

Суть в том, что Firebase будет выступать в качестве нашего бэкэнда в этом проекте, предоставляя нам необходимые конечные точки API для обработки наших потребностей в аутентификации и базе данных. В конце концов, вы создадите функциональное приложение для проведения опросов с использованием Vue+ Firebase. После этого вы можете продолжить и создать любое веб-приложение по вашему выбору, используя те же самые процессы, даже с пользовательским бэкэндом.

Чтобы продолжить, на вашем компьютере должны быть установлены Node и npm/yarn. Если вы еще этого не сделали, следуйте этим кратким руководствам, чтобы установить пряжу или npm на свой компьютер. Вам также необходимо иметь общее представление о синтаксисе Vue, Vuex и маршрутизатора Vue для этого руководства.

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

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

Архитектура приложения для опросов
Здесь описывается, как будет работать наше приложение для проведения опросов. (Большой превью)

Не стесняйтесь создавать новый проект, если вы хотите создать этот проект полностью самостоятельно, просто убедитесь, что вы установили Vuex, Vue router, Vuelidate и axios в свой проект Vue. Итак, давайте сразу:

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

Целевая страница Firebase
Целевая страница, на которой вы можете зарегистрироваться и начать свое путешествие по Firebase. (Большой превью)
Создание новых проектов Firebase
Создание проектов Firebase (большой предварительный просмотр)

Теперь, когда у нас есть проект, нужно настроить систему аутентификации и базу данных (базу данных в реальном времени) на Firebase.

  • Нажмите на опцию «аутентификация»;
  • Настройте «метод входа», который нам нужен (в данном случае электронная почта/пароль).
Настроить способ входа
Настройте метод аутентификации по электронной почте/паролю для проекта. (Большой превью)
  • Нажмите на «базу данных».
  • Выберите «База данных в реальном времени» и скопируйте эту ссылку, которая находится прямо сверху.

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

Мы будем называть этот API API базы данных. Чтобы использовать его, вам нужно будет добавить имя базы данных по вашему выбору при отправке. Например, для отправки в базу данных с именем user. Вы просто добавляете user.json в конце:

 {databaseAPI}/user.json
База данных в реальном времени
Используйте API над самой базой данных для отправки данных в базу данных. (Большой превью)

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

Проверка

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

 npm i vuelidate

Перейдите в src/components/auth/signup.vue и в теге script импортируйте vuelidate и все необходимые события, которые нам понадобятся, из библиотеки, как показано ниже.

Примечание . Полный обзор библиотеки и всех доступных событий можно найти в документации.

 import { required, email, numeric, minValue, minLength, sameAs } from 'vuelidate/lib/validators'

Краткое объяснение:

Описание
Стоимость
required Значение является обязательным
email Значение должно быть адресом электронной почты.
numeric Должны быть числом
minValue Наименьшее числовое значение, которое может ввести пользователь.
sameAs Используется для сравнения двух значений, чтобы убедиться, что они одинаковы.
Также импортируйте [`axios`](https://github.com/axios/axios), чтобы иметь возможность отправлять HTTP-запрос на сервер:
 import axios from 'axios'
Прежде чем мы продолжим, нам нужно добавить некоторые правила в базу данных, чтобы иметь возможность проверять электронную почту, как мы должны, как показано ниже:
Правила Firebase
Правила базы данных помогают решить, можете ли вы получить доступ к базе данных в любой момент времени. (Большой превью)
 "read" = "true"
Это означает, что база данных может быть прочитана без каких-либо препятствий со стороны клиента.
 "write" = "auth" !== null
Вы не можете писать в базу данных, если вы не являетесь аутентифицированным пользователем.
 "Users" = { "onIndex" : ["email"] }
Это позволяет нам запрашивать документ «users» с индексом «email». То есть вы можете буквально фильтровать базу данных по уникальному имейлу. Затем добавьте пользовательское вычисляемое свойство с именем `validations` так же, как у нас есть методы, вычисленные и т. д. В разделе `validations` у нас будут методы для проверки необходимых данных, начиная с `email`, где это требуется и, очевидно, должно быть адресом электронной почты. . Кроме того, мы хотим иметь возможность сообщать пользователю, когда электронное письмо уже было получено кем-то другим, проверяя базу данных после того, как пользователь ввел его, используя что-то, называемое асинхронными валидаторами, все в пользовательском валидаторе, и все это поддерживается [vuelidate. ] (https://vuelidate.js.org/#sub-asynchronous-validation)
 validations : { email: { required, email, unique: val => { if (val === '') return true return axios.get('https://vue-journal.firebaseio.com/users.json?orderBy="email"&equalTo="' + val + '"') .then(res => { return Object.keys(res.data).length === 0 }) } } }
Затем под уникальным запросите базу данных с помощью axios и используйте Object.keys по умолчанию, чтобы вернуть ответ, только если его длина равна 0. Для возраста вы добавите обязательное, числовое и минимальное значение 18, которое назначено `minVal ` как его свойства.
 age: { required, numeric, minVal: minValue(18) }
Требуются свойства пароля с минимальной длиной 6, назначенной `minLen`.
 password: { required, minLen: minLength(6) }
Свойства `confirmPassword` в основном должны совпадать с паролем.
 confirmPassword: { sameAs: sameAs(vm => { return vm.password }) }
Чтобы сообщить пользователю, что электронная почта принята, используйте `v-if`, чтобы проверить, является ли `unique` истинным или ложным. Если это правда, то это означает, что длина возвращаемого объекта равна 0, и электронная почта все еще может использоваться, а также наоборот. Таким же образом вы можете проверить, является ли ввод пользователя реальным электронным письмом, используя `v-if`. И для всех окружающих div на отдельном вводе мы добавим класс недопустимых значений, который становится активным при возникновении ошибки на этом вводе. Чтобы привязать события проверки к каждому из входных данных в HTML, мы используем [`$touch()`](https://vuelidate.js.org/#sub-without-v-model), как показано в `email ` ниже.
 <div class="input" :class="{invalid: $v.email.$error}"> <h6 v-if="!$v.email.email">Please provide a valid email address.</h6> <h6 v-if="!$v.email.unique">This email address has been taken.</h6> <input type="email" placeholder="Email" @blur="$v.email.$touch()" v-model="email"> </div>
`Age`, `password` и `confirmPassword` будут привязаны к их HTML-вводу так же, как и `email`. И мы сделаем кнопку «Отправить» неактивной, если в каком-либо из входных данных есть ошибка.
 <button type="submit" :disabled="$v.$invalid">create</button>
Вот полный [пример CodePen](https://codepen.io/atanda1/pen/Yzyqrjv) для этого раздела проверки.
Проверить реализацию
Vuelidate используется здесь для определения типа данных, отправленных в базу данных. (Большой превью)
## Аутентификация Это приложение является SPA и не перезагружается, как традиционные сайты, поэтому мы будем использовать Vuex в качестве нашего единственного «источника правды», чтобы позволить каждому компоненту в нашем приложении знать об общем статусе аутентификации. Мы идем в наш файл магазина и создаем оба метода входа/регистрации в действиях. Ответ («токен» и «userId»), полученный при отправке данных пользователей, будет храниться в нашем состоянии. Это важно, потому что токен будет использоваться, чтобы узнать, вошли ли мы в систему или нет в любом месте нашего приложения. Токен, userId и пользователь создаются в состоянии с начальным значением null. Мы вернемся к пользователю гораздо позже, а пока сосредоточимся на первых двух.
 state: { idToken: null, userId: null, user: null }
Затем создаются мутации для изменения состояния при необходимости.
authUser Сохраняет токен и userId
storeUser Сохраняет информацию о пользователе
clearAuthData Стирает данные обратно в исходное состояние
 mutations: { authUser (state, userData) { state.idToken = userData.token state.userId = userData.userId }, storeUser (state, user) { state.user = user }, clearAuthData (state) { state.idToken = null state.userId = null state.user = null } }
Для регистрации/входа нам нужно будет создать отдельные действия для обоих, где мы отправляем наши запросы на аутентификацию на сервер. После чего наш ответ (токен и идентификатор пользователя) от регистрации/входа фиксируется в authUser и сохраняется в локальном хранилище.
 signup ({commit, dispatch}, authData) { axios.post('https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyCFr-OMMzDGp4Mmr0t66w2cTGfNazYjptQ', { email: authData.email, password: authData.password, returnSecureToken: true }) .then(res => { console.log(res) commit('authUser', { token: res.data.idToken, userId: res.data.localId }) localStorage.setItem('token', res.data.idToken) localStorage.setItem('userId', res.data.localId) localStorage.setItem('email', res.data.email) dispatch('storeUser', authData) setTimeout(function () { router.push('/dashboard') }, 3000) }) .catch(error => console.log(error)) }
 login ({commit}, authData) { axios.post('https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=AIzaSyCFr-OMMzDGp4Mmr0t66w2cTGfNazYjptQ', { email: authData.email, password: authData.password, returnSecureToken: true }) .then(res => { console.log(res) localStorage.setItem('token', res.data.idToken) localStorage.setItem('userId', res.data.localId) localStorage.setItem('email', res.data.email) commit('authUser', { token: res.data.idToken, userId: res.data.localId }) router.push('/dashboard') }) .catch(error => console.log(error.message)) }
Но вот сложная часть, то, что мы будем делать с действием регистрации, в частности, будет отправлять только адрес электронной почты и пароль для регистрации в базе данных аутентификации. На самом деле у нас нет доступа к данным в этой базе данных аутентификации, и мы не отправляли никаких наших регистрационных данных, кроме адреса электронной почты/пароля. Итак, что мы сделаем, так это создадим еще одно действие для отправки полных регистрационных данных в другую базу данных. В этом отдельном документе базы данных у нас будет полный доступ ко всей информации, которую мы решили сохранить там. Мы назовем это новое действие storeUser. Затем мы переходим к нашему действию регистрации и отправляем весь объект, содержащий наши регистрационные данные, в базу данных, к которой у нас теперь есть доступ через storeUser. **Примечание.** Вы можете не захотеть отправлять пароль пользователя с `storeUser` в базу данных из соображений безопасности.
 storeUser ({ state}, userData) { if (!state.idToken) { return } axios.post('https://vue-journal.firebaseio.com/users.json' + '?auth=' + state.idToken, userData) .then(res => console.log(res)) .catch(error => console.log(error)) } }
`storeUser` добавляет запрос, используя наш недавно полученный токен и API базы данных при публикации в базе данных. Это потому, что мы не можем писать в нашу базу данных, за исключением того, что мы аутентифицированы с помощью нашего доказательства (токена). Это правило, которое мы дали Firebase в начале, помните?
 “write” = “auth” !== null
Полный код действий регистрации/входа находится прямо [здесь](https://codepen.io/atanda1/pen/mdePKqj). Затем отправьте как регистрацию, так и вход из их компонентов в методе onSubmit на соответствующие действия в магазине.
 methods : { onSubmit () { const signupData = { email : this.email, name : this.name, age : this.age, password : this.password, confirmPassword : this.co nfirmPassword } this.$store.dispatch('signup', signupData) } } }
**Примечание:** `signupData` содержит данные формы.
 methods : { onSubmit = { const formData = { email : this.email, password : this.password } this.$store.dispatch('login', {email: formData.email, password: formData.password}) } }
## AuthGuard Необходимо, чтобы AuthGuard не позволял пользователям, не вошедшим в систему, получить доступ к информационной панели, на которую они отправят опрос. Перейдите к файлу маршрута и импортируйте наш магазин.
 import store from './store'
В маршруте перейдите к пути панели управления и добавьте следующее:
 const routes = [ { path: '/', component: WelcomePage }, { path: '/signup', component: SignupPage }, { path: '/signin', component: SigninPage }, { path: '/dashboard', component: DashboardPage, beforeEnter (to, from, next) { if (store.state.idToken) { next() } else { next('/signin') } } } ]
Все это делает, это проверяет есть ли токен в состоянии, если да, то даем доступ к дашборду и наоборот. ## LogOut Чтобы создать нашу опцию выхода из системы, мы будем использовать `clearAuth`, который мы создали ранее в `mutations`, который просто устанавливает для `token` и `userId` значение `null`. Теперь мы создаем новое действие `logout`, которое фиксирует `clearAuth`, удаляет локальное хранилище и добавляет `router.replace('/')`, чтобы полностью перенаправить пользователя.
 actions: { logout ({commit}) { commit('clearAuth') localStorage.removeItem('token') localStorage.removeItem('userId') router.replace('/') } }
В компоненте заголовка у нас есть метод `onLogout`, который отправляет наше действие выхода из магазина.
 methods: { onLogout() { this.$store.dispatch('logout') } }
Затем мы добавляем `@click` к кнопке, которая запускает метод `onLogout`, как мы можем видеть [здесь] (https://codepen.io/atanda1/pen/jObqKNd).
 <ul @click="onLogout">Log Out</ul>
## UI_State Теперь, когда мы предоставили условный доступ к информационной панели, следующим шагом будет ее удаление из панели навигации, чтобы ее могли просматривать только аутентифицированные пользователи. Для этого мы добавим новый метод под «геттерами» под названием «ifAuthenticated», который проверяет, является ли токен в нашем состоянии нулевым. Когда есть токен, он показывает, что пользователь аутентифицирован, и мы хотим, чтобы они видели опцию панели опроса на панели навигации.
 getters: { isAuthenticated (state) { return state.idToken !== null } }
После этого вы возвращаетесь к заголовочному компоненту и создаете метод auth под вычислением, который отправляет на наш `isAuthenticated` в пределах `геттеров`, которые мы только что создали в хранилище. Это означает, что `isAuthenticated` вернет false, если токена нет, что означает, что `auth` также будет нулевым, и наоборот.
 computed: { auth () { return this.$store.getters.ifAuthenticated } }
После этого мы добавляем `v-if` в наш HTML, чтобы проверить, является ли `auth` нулевым или нет, определяя, будет ли эта опция отображаться на панели навигации.
 <li v-if='auth'> <router-link to="/dashboard">Dashboard</router-link> </li> <li v-if='!auth'> <router-link to="/signup">Register</router-link> </li> <li v-if='!auth'> <router-link to="/signin">Log In</router-link> </li>
- Вы найдете полный код раздела состояния пользовательского интерфейса [здесь] (https://codepen.io/atanda1/pen/QWjNxyo).
Состояние пользовательского интерфейса
В заголовке есть изменения в зависимости от статуса аутентификации пользователя. (Большой превью)

Автоматическая авторизация

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

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

Создается новый метод actions под названием AutoLogin , где мы будем получать токен и userId из локального хранилища и передавать наши данные методу authUser в мутациях.

 actions : { AutoLogin ({commit}) { const token = localStorage.getItem('token') if (!token) { return } const userId = localStorage.getItem('userId') const token = localStorage.getItem('token') commit('authUser', { idToken: token, userId: userId }) } }

Затем мы переходим к нашему App.vue и пишем created метод, который будет отправлять autoLogin из нашего хранилища каждый раз при загрузке приложения.

 created () { this.$store.dispatch('AutoLogin') }

Fetch_User_Data

Мы хотим приветствовать пользователя на приборной панели, отображая имя пользователя. Итак, создается еще одно действие с именем fetchUser , которое сначала проверяет наличие токена, как обычно. Затем он получает электронную почту из локального хранилища и запрашивает базу данных, как это делалось ранее, с проверкой электронной почты.

Это возвращает объект, содержащий данные пользователя, первоначально отправленные во время регистрации. Затем мы конвертируем этот объект в массив и фиксируем его в изначально созданной мутации storeUser .

 fetchUser ({ commit, state}) { if (!state.idToken) { return } const email = localStorage.getItem('email') axios.get('https://vue-journal.firebaseio.com/users.json?orderBy="email"&equalTo="' + email + '"') .then(res => { console.log(res) // const users = [] console.log(res.data) const data = res.data const users = [] for (let key in data) { const user = data[key] user.id = key users.push(user) console.log(users) } commit('storeUser', users[0]) }) .catch(error => console.log(error)) }

После чего мы создаем еще один геттер с именем user , который возвращает state.user уже зафиксированный через storeUser .

 getters: { user (state) { return state.user }, isAuthenticated (state) { return state.idToken !== null } }

Вернемся к панели инструментов. Мы создаем новый вычисляемый метод с именем name , который возвращает state.user.name , только если пользователь существует.

 computed: { name () { return !this.$store.getters.user ? false : this.$store.getters.user.name } }, created () { this.$store.dispatch('fetchUser') } }

И мы также добавим created вычисляемое свойство для отправки действия fetchUser после загрузки страницы. Затем мы используем v-if в нашем HTML, чтобы отобразить имя, если оно существует.

 <p v-if="name">Welcome, {{ name }} </p>

Отправить_опрос

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

 postData ({state}, surveyData) { if (!state.idToken) { return } axios.post('https://vue-journal.firebaseio.com/survey.json' + '?auth=' + state.idToken , surveyData) .then(res => { console.log(res) }) .catch(error => console.log(error)) }

Мы возвращаемся к компоненту панели инструментов и отправляем данные нашему действию postData в хранилище.

 methods : { onSubmit () { const postData = { price: this.price, long: this.long, comment: this.comment } console.log(postData) this.$store.dispatch('postData', postData) } }

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

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

  • Демо для учебника находится здесь.
Приложение для опросов Vue
Завершенное приложение для опроса (большой предварительный просмотр)

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

  • Чтобы узнать больше о Firebase и других сервисах, которые он предлагает, ознакомьтесь со статьей Криса Эсплина «Что такое Firebase?»
  • Vuelidate — действительно хорошая библиотека, в которую стоит покопаться. Вы должны прочитать его документацию, чтобы получить полное представление. https://vuelidate.js.org/.
  • Вы также можете исследовать аксиомы отдельно, особенно если вы хотите использовать их в более крупных проектах.