Добро пожаловать, чтобы продолжить чтение серии «Крупномасштабное сражение по разработке мини-программы Таро», обзор предыдущей ситуации:
- Знакомый React, знакомые хуки: Мы реализовали очень простой прототип для добавления постов с помощью React и Hooks.
- Многостраничный прыжок и библиотека компонентов Taro UI: мы используем собственную функцию маршрутизации Taro для перехода на несколько страниц и используем библиотеку компонентов пользовательского интерфейса Taro для обновления интерфейса приложения.
- Реализовать многотерминальный вход в WeChat и Alipay: Реализованы WeChat, Alipay и обычный вход и выход
- Крюки + двойные мечи Redux: Рефакторинг управления состоянием приложения с использованием Hooks-версии Redux.
-
Крупномасштабное управление состоянием приложения с использованием Hooks-версии Redux (часть 1): Реализовано с использованием версии Redux для хуков.
user
Рефакторинг управления логическим состоянием -
Крупномасштабное управление состоянием приложения с использованием Hooks-версии Redux (часть 2): Реализовано с использованием версии Redux для хуков.
post
Рефакторинг управления логическим состоянием
Если вы нажмете здесь, вы обнаружите, что контент, за которым мы будем следить, представляет собой чистую логику внешнего интерфейса (сторона мини-программы). Полное онлайн-приложение мини-программы также должно иметь серверную часть. В этой статье мы будем использовать WeChat. мини-программы.Облако программ в качестве нашего бэкенда, а затем мы введемredux-saga
Чтобы помочь Redux изящно обрабатывать асинхронные процессы, окончательный результат реализации этой статьи выглядит следующим образом:
Если вы не знакомы с Redux, мы рекомендуем прочитать нашу серию руководств «Redux Package Tutorial»:
- Встреча с обучающим пакетом Redux (1): спасение кризиса состояния React
- Встреча с учебным пакетом Redux (2): Куй железо, пока горячо, полный рефакторинг
- Образовательный пакет Redux (3): выполнение своих обязанностей и восстановление своих первоначальных устремлений.
Если вы хотите начать прямо с этого шага, выполните следующую команду:
git clone -b miniprogram-start https://github.com/tuture-dev/ultra-club.git
cd ultra-club
Исходный код, задействованный в этой статье, размещен вGithubНа, если вы считаете, что мы написали неплохо, я надеюсь, что вы можете поставить лайк этой статье ❤️ + звезда репозитория Github ❤️ о~
Чтобы постоянно хранить данные и эффективно выполнять запросы, нам необходимо хранить данные в базе данных.Чтобы реализовать удобный опыт разработки на стороне апплета, появилось большое количество бессерверных сервисов апплета, а также облако апплета WeChat. именно для апплета WeChat.Рожденный для быстрой разработки. В этой статье мы будем использовать облако апплетов WeChat в качестве нашей серверной части и объясним, как внедрить и реализовать асинхронный рабочий процесс Redux для реализации управления состоянием доступа на стороне апплета к облаку апплета.
Первые пользователи облака апплетов WeChat
В предыдущем коде мы завершаем персистентность данных, сохраняя данные в хранилище, что может решить проблему хранения небольших данных и запросов.Как только объем данных становится большим, запросы и хранилища должны полагаться на специальную базу данных. , Это решено. Как правило, мы можем решить эту проблему, создав собственный сервер и базу данных. Однако, когда небольшие программы становятся все более и более популярными, был предложен и постепенно стал популярным режим под названием «Бессерверный», который можно резюмировать в виде в народном смысле это «без бэкенда», то есть бэкенд передается поставщикам облачных услуг (Alibaba Cloud, Tencent Cloud, JD Cloud и т. д.), а разработчикам нужно сосредоточиться только на логике фронтенда и доставлять функции быстро.
Общий бессерверный сервис апплета включает в себя три функции:
- База данных: обычно хранится в формате данных JSON, а данные могут храниться в облачной базе данных.
- Хранилище: поддерживает хранение пользовательского контента, такого как текст и изображения, и может получать ссылки на ресурсы для использования.
- Облачные функции: вы можете использовать Node.js для разработки, самостоятельно написать соответствующую внутреннюю логику и перенести написанный код в облако, а затем использовать API для вызова внешнего интерфейса апплета.
Подробное описание мини-программы Serverless можно найти в рекомендуемой статье Заинтересованные студенты могут узнать о ней больше:Что такое бессерверная мини-программа?
В этом разделе мы используем облако апплетов WeChat в качестве нашего «бэкенда». Облако апплетов WeChat и учетная запись апплета связаны друг с другом. Одна учетная запись апплета может открыть одно облачное пространство апплета. Далее мы рассмотрим подробности. Объясните, как активировать Mini Program Cloud.
Открытое мини-облако программ
- Сначала убедитесь, что вы зарегистрировали официальную учетную запись WeChat для мини-программы:Зарегестрированный адрес.
- После входа найдите его в строке меню «Разработка» > «Настройки разработки».
AppID
, он должен быть 18-битной строкой. - использоватьИнструменты разработчика WeChatоткрыть наш
ultra-club
папку проекта, а затем выберите «Настройки» > «Настройки проекта» в строке меню «Инструменты разработчика WeChat», чтобы открыть панель настроек:
4. Найдите основную информацию на панели настроек и измените ее на AppID выше в столбце AppID следующим образом:
5. Когда AppID установлен, кнопка «Облачная разработка» в наших инструментах разработчика должна стать кликабельной, найдите кнопку «Облачная разработка» в верхнем левом углу и нажмите ее, как на следующем рисунке:
4. После нажатия кнопки «Облачная разработка» появится окно подтверждения, нажмите «Согласен», чтобы войти в мини-консоль облачной разработки программы:
После входа первое, что мы видим, это интерфейс «Анализ операций» консоли облачной разработки, который представляет собой интерфейс, используемый для визуализации использования различных ресурсов облачной разработки.Мы не будем объяснять этот аспект в этом руководстве. В основном речь идет о части, отмеченной красным на рисунке:
- Серийный номер 1 — это наша облачная база данных, представляющая собой базу данных JSON, в которой хранятся данные, необходимые нам во время разработки.
- Серийный номер 2 — это хранилище, то есть мы можем загрузить какой-то текст, картинки, аудио/видео, а потом вернуть нам ссылку для доступа к этим ресурсам.
- № 3 — это облачная функция, то есть мы можем управлять частью внутренней логики Node.js, которую мы написали в ней, она работает в облаке, и мы можем вызывать их через API на стороне апплета.
- На этот раз серийный номер 4 — это идентификатор, представляющий нашу облачную среду, который можно использовать для обозначения облачной среды, вызываемой в данный момент, при вызове ресурсов облачной разработки с помощью API на стороне апплета.
В этом руководстве мы будем использовать базу данных и облачные функции, упомянутые выше.
Создать таблицу базы данных
После знакомства с интерфейсом облака апплетов давайте сразу потренируемся в создании нужных нам таблиц базы данных, потому что логика нашего фронтенда в основном делится наuser
а такжеpost
Два типа логики, поэтому мы создаем две таблицы в базе данных:
Здесь мы специально объясняем значение этого интерфейса работы с базой данных:
- Как видите, нажмите вторую кнопку в левом верхнем углу консоли облачной разработки, а затем нажмите кнопку «+», отмеченную красной цифрой 1 на рисунке, чтобы создать два набора.
user
а такжеpost
, поэтому мы создали нашу таблицу базы данных. - Серийный номер — 2, что означает, что мы можем выбрать коллекцию и щелкнуть правой кнопкой мыши, чтобы удалить ее.
- Серийный номер — 3, что означает, что мы можем добавлять записи в коллекцию.Поскольку это база данных JSON, каждая запись в коллекции может быть разной.
- № 4 означает, что мы можем выбрать запись и щелкнуть правой кнопкой мыши, чтобы удалить ее.
- Порядковый номер 5 означает, что мы можем добавлять поля в одну запись.
- № 6 означает, что мы можем выбрать одну запись для удаления/изменения
- Порядковый номер 7 означает, что мы можем запросить запись в этом наборе
Создайтеpost
Записывать
Здесь мы добавляем значение по умолчаниюpost
запись, что означает данные по умолчанию на нашем предыдущем апплете, эти данные записываютсяpost
связанная информация:
-
_id
: уникальный идентификатор этого фрагмента данных. -
title
: название статьи -
content
: содержание статьи -
user
: пользователь, опубликовавший эту статью, здесь мы напрямую сохраняем полную информацию о пользователе для удобства._id
свойства, а затем в запросеpost
, получить_id
свойства, а затем проверьтеuser
Получите полную информацию о пользователе. -
updatedAt
: время последнего обновления этой записи -
createdAt
: время создания этой записи
Создайтеuser
Записывать
Выше мы упоминали, что мы сохранили информацию об авторе сообщения в этой записи статьи, поэтому, конечно же, нашаuser
Новая порция информации об этом авторе будет создана в сборнике следующим образом:
Можно видеть, что мы добавляем запись пользователя, его поля следующие:
-
_id
: Этот пользователь находится вuser
Уникальный идентификатор в коллекции -
avatar
: адрес аватара этого пользователя -
nickName
: псевдоним этого пользователя, который мы будем использовать для входа -
createdAt
: время создания этой записи -
updatedAt
: последний раз, когда эта запись была обновлена
Инициализировать облачную среду апплета на стороне апплета
После открытия облака апплета нам также необходимо инициализировать среду облака апплета во внешнем коде апплета, чтобы API апплета можно было вызывать во внешнем интерфейсе апплета.
Открытьsrc/index/index.jsx
файл, добавьте в него следующий код:
import Taro, { useEffect } from '@tarojs/taro'
// ... 其余代码一致
export default function Index() {
// ... 其余代码一致
useEffect(() => {
const WeappEnv = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
if (WeappEnv) {
Taro.cloud.init()
}
// ...其余代码一致
return (
<View className="index">
...
</View>
)
}
Видно, что мы добавили получение и оценку среды апплета WeChat.Когда текущая среда является средой апплета WeChat, нам нужно вызватьTaro.cloud.init()
Инициализация облачной среды апплета
резюме
До сих пор мы объясняли, как активировать облако апплета, а затем объяснили интерфейс консоли облака апплета.В то же время мы объяснили интерфейс функции базы данных, который будет использоваться, и создали две таблицы (коллекцию), необходимые для нашего приложения. . ):post
а такжеuser
, и каждый инициализирует запись.
Что ж, облако апплетов готово, мы готовы получить к нему доступ в приложении, но перед этим, поскольку мы хотим получить доступ к облаку апплетов, оно должно инициировать асинхронный запрос, который требует понимания потока асинхронной обработки Redux, в следующий раздел, мы будем использоватьredux-saga
Промежуточное ПО для упрощения процесса обработки асинхронности Redux.
Анализ асинхронного рабочего процесса Redux
Давайте посмотрим на диаграмму потока данных Redux:
Серая фигура над этим путем — это карта потока данных Redux, которую мы использовали ранее, то есть синхронная диаграмма потока данных Redux:
-
view
серединаdispatch(syncAction)
синхронное действие для обновленияstore
данные в -
reducer
реагировать на действия, обновлятьstore
условие -
connect
Передайте обновленное состояниеview
-
view
Получать новые данные и перенаправить
Уведомление
Студенты, не знакомые с Redux, могут узнать о сообществе Tuque.Серия руководств по пакету ReduxОй.
Теперь мы собираемся сделать запрос к облаку апплета, этот запрос является асинхронным запросом, он не получит ответ сразу, поэтому нам нужно промежуточное состояние (здесь мы используемSaga
) назад и вперед, чтобы обработать этот асинхронный запрос и получить данные, а затем выполнить путь, аналогичный предыдущему синхронному запросу, то есть зеленую часть + оставшуюся серую часть на нашем рисунке выше, поэтому асинхронный рабочий процесс становится таким:
-
view
серединаdispatch(asyncAction)
Асинхронное действие для получения данных бэкенда (здесь облако апплета) -
saga
Обработайте асинхронное действие и дождитесь ответа данных -
saga
получить данные ответа,dispatch(syncAction)
Синхронное действие для обновления состояния хранилища -
reducer
реагировать на действия, обновлятьstore
условие -
connect
Передайте обновленное состояниеview
-
view
Получение новых данных и повторный рендеринг
Уведомление
Сообщество Tuque в будущем опубликует учебник для объяснения асинхронного рабочего процесса Redux.Я не буду здесь подробно изучать принцип всего асинхронного процесса, а только объясню, как интегрировать этот асинхронный рабочий процесс. Пожалуйста, ждите этого ✌️~
Реальный асинхронный рабочий процесс Redux
Установить
Мы используемredux-saga
Это промежуточное программное обеспечение берет на себя часть обработки асинхронных запросов асинхронного рабочего процесса Redux, сначала установленного в корневом каталоге проекта.redux-saga
Мешок:
$ npm install redux-saga
После установки нашpackage.json
Это становится таким:
{
"dependencies": {
...
"redux-saga": "^1.1.3",
"taro-ui": "^2.2.4"
},
}
redux-saga
даredux
Промежуточное ПО, которое обрабатывает асинхронные процессы, так что же такое Saga? Определение Saga — «долгоживущая транзакция» (Long Lived Transaction, далее LLT). Это концепция, предложенная профессором ГЕКТОРОМ ГАРСИА-МОЛИНОЙ из Принстонского университета в статье 1987 года о распределенных базах данных.Официально сага уподобляется отдельному потоку в приложении, который отвечает за независимую обработку побочных эффектов.В JavaScript побочные эффекты относятся к внешним операциям, таким как асинхронные сетевые запросы и локальное чтение localStorage/Cookie.
настроитьredux-saga
промежуточное ПО
После установки нам нужно настроитьredux-saga
использовать его, открытьsrc/store/index.js
файл и внесите соответствующие изменения в его содержимое следующим образом:
import { createStore, applyMiddleware } from 'redux'
import { createLogger } from 'redux-logger'
import createSagaMiddleware from 'redux-saga'
import rootReducer from '../reducers'
import rootSaga from '../sagas'
const sagaMiddleware = createSagaMiddleware()
const middlewares = [sagaMiddleware, createLogger()]
export default function configStore() {
const store = createStore(rootReducer, applyMiddleware(...middlewares))
sagaMiddleware.run(rootSaga)
return store
}
Как видите, в наш файл выше были внесены следующие четыре изменения:
- Сначала мы экспортировали
createSagaMiddleware
- Затем мы начинаем с
src/store/sagas
экспортировано из папкиrootSaga
, который объединяет всеsaga
файл, который похож на комбинациюreducer
изcombineReducers
, мы напишем их в последующих шагахsagas
. - Затем мы звоним
createSagaMiddleware
генерироватьsagaMiddleware
промежуточное ПО и поместите его вmiddleware
массив, поэтому Redux зарегистрирует это промежуточное ПО, и при ответе на асинхронные действияsagaMiddleware
вмешается и передаст его нашему определенномуsaga
функция для обработки. - Наконец в
createStore
внутри функции, при созданииstore
После этого звонимsagaMiddleware.run(rootSaga)
положить всеsagas
Запустите и начните слушать и реагировать на асинхронные действия.
Инициировать асинхронный запрос в представлении
Настроить с помощьюredux-saga
промежуточное ПО и будетsagas
После запуска мы можем начать отправку асинхронных действий в React.
Давайте следовать предыдущей последовательности рефакторинга, сначала чтобы получить асинхронную обработку потока данных входа в систему, открытьsrc/components/LoginForm/index.jsx
файл и внесите соответствующие изменения в его содержимое следующим образом:
import Taro, { useState } from '@tarojs/taro'
import { View, Form } from '@tarojs/components'
import { AtButton, AtImagePicker } from 'taro-ui'
import { useDispatch } from '@tarojs/redux'
import { LOGIN } from '../../constants'
import './index.scss'
export default function LoginForm(props) {
// 其他逻辑不变 ...
async function handleSubmit(e) {
// 其他逻辑不变 ...
// 缓存在 storage 里面
const userInfo = { avatar: files[0].url, nickName: formNickName }
// 清空表单状态
setFiles([])
setFormNickName('')
// 向后端发起登录请求
dispatch({ type: LOGIN, payload: { userInfo: userInfo } })
}
return (
// 返回的组件...
)
}
Как видите, мы вносим следующие три изменения в приведенный выше код:
- Мы установим информацию для входа пользователя ранее
SET_LOGIN_INFO
и установите всплывающий слой окна входа в системуSET_IS_OPENED
заменяетсяLOGIN
Константа означает, что нам нужно сначала инициировать запрос на вход в облако апплета, затем получить данные для входа, затем установить информацию для входа и закрыть всплывающий слой окна входа в систему (на самом деле, всплывающий слой также может быть закрыт прямо здесь, что немного ошибочно (⊙o⊙)…). - Затем мы удаляем предыдущие операции установки информации для входа и закрытия всплывающего слоя окна входа.
- Наконец мы будем
dispatch
Одинaction.type
дляLOGIN
действие, с информацией, необходимой для входа в системуuserInfo
.
Добавить константу действия
мы использовали на предыдущем шагеLOGIN
постоянный, открытыйsrc/constants/user.js
, в котором увеличениеLOGIN
постоянный:
export const SET_IS_OPENED = 'MODIFY_IS_OPENED'
export const SET_LOGIN_INFO = 'SET_LOGIN_INFO'
export const LOGIN = 'LOGIN'
Saga обрабатывает асинхронные запросы
В Saga есть много способов обработки асинхронных запросов.В зависимости от проекта могут использоваться разные способы.Здесь мы выбираем лучшие практики, рекомендованные официальным лицом:
- watcherSaga прослушивает асинхронные действия
- handlerSaga обрабатывает асинхронные действия
-
dispatch
Синхронное действие, обновить состояние, в котором началось асинхронное действие. -
dispatch
Синхронные действия, обновить состояние успеха / неудачи асинхронных действий
-
После применения последней практики предыдущая диаграмма потока данных Redux становится следующей:
Хорошо, объяснилredux-saga
Теперь, когда мы рассмотрели рекомендации по обработке асинхронных действий, давайте применим рекомендации по написанию файлов Saga, обрабатывающих асинхронные действия.
В нашем приложении может быть несколько асинхронных запросов, поэтомуredux-saga
Рекомендуется создать отдельныйsagas
Папка для хранения всех асинхронных запросовsagas
файл и вспомогательные файлы, которые могут быть использованы.
На предыдущем шаге мы излучали из представленияLOGIN
Асинхронный запрос на вход, далее мы должны написать соответствующую обработкуLOGIN
просилsaga
файл, вsrc
создать папкуsagas
папку и создать в нейuser.js
, в котором напишите следующее:
import Taro from '@tarojs/taro'
import { call, put, take, fork } from 'redux-saga/effects'
import { userApi } from '../api'
import {
SET_LOGIN_INFO,
LOGIN_SUCCESS,
LOGIN,
LOGIN_ERROR,
SET_IS_OPENED,
} from '../constants'
/***************************** 登录逻辑开始 ************************************/
function* login(userInfo) {
try {
const user = yield call(userApi.login, userInfo)
// 将用户信息缓存在本地
yield Taro.setStorage({ key: 'userInfo', data: user })
// 其实以下三步可以合成一步,但是这里为了讲解清晰,将它们拆分成独立的单元
// 发起登录成功的 action
yield put({ type: LOGIN_SUCCESS })
// 关闭登录框弹出层
yield put({ type: SET_IS_OPENED, payload: { isOpened: false } })
// 更新 Redux store 数据
const { nickName, avatar, _id } = user
yield put({
type: SET_LOGIN_INFO,
payload: { nickName, avatar, userId: _id },
})
// 提示登录成功
Taro.atMessage({ type: 'success', message: '恭喜您!登录成功!' })
} catch (err) {
console.log('login ERR: ', err)
// 登录失败,发起失败的 action
yield put({ type: LOGIN_ERROR })
// 提示登录失败
Taro.atMessage({ type: 'error', message: '很遗憾!登录失败!' })
}
}
function* watchLogin() {
while (true) {
const { payload } = yield take(LOGIN)
console.log('payload', payload)
yield fork(login, payload.userInfo)
}
}
/***************************** 登录逻辑结束 ************************************/
export { watchLogin }
Как видите, вышеперечисленные изменения в основном предназначены для созданияwatcherSaga
а такжеhandlerSaga
.
СоздайтеwatcherSaga
- Мы создали логин
watcherSaga
:watchLogin
, который используется для наблюденияaction.type
дляLOGIN
действия, а при прослушиванииLOGIN
После действия получить необходимое от этого действияuserInfo
массив, затем активируйтеhandlerSaga
:login
Для обработки соответствующей логики входа. - здесь
watcherSaga
:watchLogin
является генераторной функцией, которая внутренне являетсяwhile
Бесконечный цикл, что означает непрерывное внутреннее прослушиваниеLOGIN
действие. - Внутри цикла мы использовали
redux-saga
который предоставилeffects helper
функция:take
, который используется для наблюденияLOGIN
Действие, получите данные, переданные в действии. - Тогда мы используем другой
effects helper
функция:fork
, что означает неблокирующее выполнениеhandlerSaga
:login
, и воляpayload.userInfo
передается как параметр вlogin
.
СоздайтеhandlerSaga
- Мы создали логин
handlerSaga
:login
, который обрабатывает логику входа. -
login
также является генераторной функцией, а внутри нее находитсяtry/catch
Заявление для обработки возможных условий ошибки для запросов на вход. - существует
try
оператор, первый должен использоватьredux-saga
предоставил намeffects helper
функция:call
для вызова API входа:userApi.login
, и положиuserInfo
Передано в качестве параметра этому API.- Затем, если вход в систему прошел успешно, мы войдем успешно
user
кешировать вstorage
в. - Далее мы используем
redux-saga
который предоставилeffects helpers
функция:put
,put
похож на предыдущийview
серединаdispatch
действуй, приходиdispatch
три действия:LOGIN_SUCCESS
,SET_IS_OPENED
,SET_LOGIN_INFO
, который представляет статус успешного входа в систему, закройте окно входа и установите информацию для входа в Redux Store. - Наконец, мы использовали окно сообщения, предоставленное нам пользовательским интерфейсом Taro, для отображения
success
Информация.
- Затем, если вход в систему прошел успешно, мы войдем успешно
- Если авторизация не удалась, мы используем
put
инициироватьLOGIN_ERROR
действие, чтобы обновить сообщение об ошибке входа в Redux Store, а затем использовать окно сообщения, предоставленное нам пользовательским интерфейсом Taro, для отображенияerror
Информация.
Уведомление
Учащиеся, не понимающие функции генератора, могут ознакомиться с этим документом:итераторы и генераторы.
дополнительная работа
чтобы создатьwatcherSaga
а такжеhandlerSaga
, мы также импортировалиuserApi
, мы создадим этот API позже.
В дополнение к этому мы также импортируем константы действия, которые нам нужно использовать:
-
SET_LOGIN_INFO
: установить данные для входа -
LOGIN_SUCCESS
: обновить информацию об успешном входе в систему. -
LOGIN
: прослушивание действий входа -
LOGIN_ERROR
: обновить информацию об ошибке входа в систему. -
SET_IS_OPENED
: включение/выключение информации в окне входа в систему.
мы тоже изredux-saga/effects
В пакете импортированы необходимые функции:
-
call
:существуетsaga
Вызовите другие асинхронные/синхронные функции в функции, чтобы получить результат -
put
:аналогичныйdispatch
Дляsaga
Инициировать действие в функции -
take
:существуетsaga
Слушайте действие в функции и получайте данные, переносимые соответствующим действием. -
fork
:существуетsaga
Неблокирующие вызовы в функцияхhandlerSaga
, то есть после вызова последующая логика выполнения не будет заблокирована.
Наконец, мы получаемwatchLogin
.
Создайтеsaga
центральный файл расписания
Мы экспортировали на предыдущем шагеwatchLogin
, что похоже наreducers
один внутриreducer
функция, нам также нужно иметь что-то вродеcombineReducers
комбинацияreducer
объединить одно и то жеwatcherSaga
.
существуетsrc/sagas
создать папкуindex.js
файл и пишем в нем следующее:
import { fork, all } from 'redux-saga/effects'
import { watchLogin } from './user'
export default function* rootSaga() {
yield all([
fork(watchLogin)
])
}
Как видите, в приведенном выше файле есть три основных изменения:
- мы начинаем с
redux-saga/effects
экспортируетсяeffects helper
функцияfork
а такжеall
. - Затем мы начинаем с
user.js
импортировано в сагеwatchLogin
. - Наконец, мы экспортировали один
rootSaga
, Это центр всех функций сага, черезall
Массив передается в функцию, иfork
неблокирующее выполнениеwatchLogin
, а затем начните слушать и отправлять асинхронные действия,LOGIN
действие, активироватьwatchLogin
Логика обработки внутри.
Уведомление
В настоящее время
all
Массив, полученный функцией,fork(watchLogin)
, дождитесь последующих соединенийpost
Когда используется асинхронная логика, она также добавит несколькоfork(watcherSaga)
.
добавить константу действия
потому что на предыдущем шагеuser
В файле саги мы используем некоторые константы, которые еще не определены, поэтому давайте сразу их определим, открываемsrc/constants/user.js
и добавьте соответствующие константы следующим образом:
export const SET_IS_OPENED = 'MODIFY_IS_OPENED'
export const SET_LOGIN_INFO = 'SET_LOGIN_INFO'
export const LOGIN = 'LOGIN'
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'
export const LOGIN_ERROR = 'LOGIN_ERROR'
export const LOGIN_NORMAL = 'LOGIN_NORMAL'
Как видите, помимо констант, которые мы использовали в «саге об обработке асинхронных запросов», есть еще однаLOGIN_NORMAL
Константы, которые в основном используются для установки состояния входа по умолчанию.
Реализовать API запроса входа в систему
в предыдущемuser
В файле саги мы используемuserApi
, который инкапсулирует логику инициирования запросов к бэкенду (здесь мы облако апплета), давайте сразу его реализуем.
Мы храним все файлы API в одном местеapi
В папке это удобно для нашей будущей работы по обслуживанию кода, вsrc
создать папкуapi
папка, добавитьuser.js
файл и пропишите в файле следующее:
import Taro from '@tarojs/taro'
async function login(userInfo) {
const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY
// 针对微信小程序使用小程序云函数,其他使用小程序 RESTful API
try {
if (isWeapp) {
const { result } = await Taro.cloud.callFunction({
name: 'login',
data: {
userInfo,
},
})
return result.user
}
} catch (err) {
console.error('login ERR: ', err)
}
}
const userApi = {
login,
}
export default userApi
В приведенном выше коде мы определяемlogin
функция, этоasync
функция, используемая для обработки асинхронной логики, вlogin
В функции мы судим о текущей среде, и только в апплете WeChat, т.е.isWeapp
Выполните операцию входа в систему в условиях апплета Alipay и H5, мы будем использовать бессерверную версию LeanCloud для ее решения в следующем разделе.
Логика входа в системуtry/catch
оператор, используемый для обнаружения возможных ошибок запроса, вtry
В блоке кода мы использовалиTaro
Предоставляет нам API облачных функций WeChat Mini Program Cloud.Taro.cloud.callFunction
Чтобы было удобно запускать запрос на вызов облачной функции, его тело вызова представляет собой объект, подобный следующей структуре:
{
name: '', // 需要调用的云函数名
data: {} // 需要传递给云函数的数据
}
Здесь мы называемlogin
облачная функция, и будетuserInfo
Он передается в качестве параметра облачной функции, чтобы использовать информацию о пользователе в облачной функции для регистрации пользователя и сохранения ее в базе данных.Мы реализуем эту облачную функцию в следующем разделе.
намекать
Чтобы узнать больше об облачных функциях апплета WeChat, вы можете обратиться к документации по облачным функциям апплета WeChat:адрес документа
Если вызов успешно, мы можем получить возвращаемое значение для возврата данных с бэкэнда. Здесь мы используем метод разрушимости, чтобы получить его от возврата телаresult
объект, а затем извлекитеuser
объект и какlogin
Возвращаемое значение функции API.
Если вызов не удался, вывести ошибку.
Наконец, мы определяемuserApi
Объект, используемый для хранения всех функций, связанных с пользовательской логикой, и добавленияlogin
Недвижимость API, а затем экспортировать это так, чтобы вuser
Вы можете импортировать его в функцию сагиuserApi
затем пройтиuserApi.login
способ позвонитьlogin
API обрабатывает логику входа.
Создать файл экспорта API по умолчанию
мы создалиsrc/api/user.js
файл, нам нужно создать единый файл по умолчанию для экспорта всех файлов API, чтобы облегчить единое распространение всех API.src/api
создать папкуindex.js
файл и пишем в нем следующее:
import userApi from './user'
export { userApi }
Видно, что мы начинаем сuser.js
Он экспортируется по умолчаниюuserApi
, и добавьте его вexport
Свойства экспортируемого объекта.
Настройка среды разработки облачных функций
Мы использовали API облачных функций, предоставленный Taro в предыдущем разделе, для вызоваlogin
Облачная функция, теперь мы будем реализовывать эту облачную функцию немедленно.
Документация апплета WeChat требует, чтобы мы создали папку для хранения облачных функций в корневом каталоге проекта, а затемproject.config.json
изcloudfunctionRoot
Значение поля указывается как этот каталог, чтобы инструмент разработчика апплета мог идентифицировать этот каталог как каталог для хранения облачных функций и выполнять специальную обработку флагов.
Мы создалиfunctions
папка, аналогичнаяsrc
Папки являются братьями и сестрами:
.
├── LICENSE
├── README.md
├── config
├── dist
├── functions
├── node_modules
├── package.json
├── project.config.json
├── src
├── tuture-assets
├── tuture-build
├── tuture.yml
└── yarn.lock
Затем мы находимся в корневом каталогеproject.config.json
файл добавленcloudfunctionRoot
поле и установить его на'functions/'
следующим образом:
{
"miniprogramRoot": "dist/",
"projectname": "ultra-club",
"description": "",
"appid": "",
"cloudfunctionRoot": "functions/",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"cloudfunctionTemplateRoot": "cloudfunctionTemplate",
"condition": {}
}
Как видите, когда мы создали указанную выше папку и установилиproject.config.json
После этого наш инструмент разработчика апплета будет выглядеть так:
тот, который мы создалиfunctions
Папки имеют дополнительный значок облака и называются отfunctions
сталfunctions | ultra-club
, правая сторона вертикальной полосы — это наша текущая среда апплета.
И когда мы щелкаем правой кнопкой мыши в инструментах разработчика апплетаfunctions
Когда папка открыта, появится всплывающее окно меню, позволяющее нам выполнять операции, связанные с облачными функциями:
Мы видим, что операций много, здесь мы в основном используем следующие операции:
- Новая облачная функция Node.js
- Включить локальную отладку облачных функций
Уведомление
Другие операции будут встречаться, когда вам нужно написать более сложную бизнес-логику после того, как вы прошли весь процесс разработки Mini Program Cloud.Подробности см. в документации Mini Program Cloud:адрес документа.
Уведомление
Прежде чем использовать облачные функции, необходимо активировать облачную среду разработки Mini Program. Для конкретных шагов, пожалуйста, обратитесь к нашему объяснению в разделе «Активация Mini Program Cloud».
Создайте облачную функцию входа в систему
Объяснили настройку облачных функций апплета WeChat и, наконец, дошли до этапа создания облачных функций.Щелкаем правой кнопкой мыши в инструментах разработчика апплетаfunctions
папку, затем выберите «Новая облачная функция Node.js», введитеlogin
, а затем нажмите Enter для создания, вы увидите, что инструмент разработчика апплета автоматически создал для нас следующий файл кода:
Как видите, облачная функция — это автономный модуль Node.js, который обрабатывает класс логики.
Давайте сначала посмотримpackage.json
Файлы следующие:
{
"name": "login",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"wx-server-sdk": "latest"
}
}
Видно, что при добавлении облачных функций инструмент разработчика апплета добавил нам пункт по умолчанию.wx-server-sdk
Зависимость, нам нужно использовать его встроенные связанные API в облачной функции для работы с облаком апплетов.
Чтобы запустить эту облачную функцию/проект Node.js, нам нужно установить зависимости, введитеfunctions/login
каталог, запустить в каталогеnpm install
команда для установки зависимостей.
Общие сведения об облачных функциях, созданных по умолчанию
Когда облачная функция будет создана и зависимости установлены, мы сразу разгадаем тайну облачной функции и откроемfunctions/login/index.js
, вы можете увидеть следующий код:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
const wxContext = cloud.getWXContext()
return {
event,
openid: wxContext.OPENID,
appid: wxContext.APPID,
unionid: wxContext.UNIONID,
}
}
Видно, что созданный по умолчанию код в основном сделал следующую работу:
- импорт
wx-server-sdk
пакет и названный какcloud
, все методы, необходимые для работы с облаком апплетов, связаны вcloud
на объекте. - Тогда позвони
cloud.init()
Для инициализации облачной среды разработки облачных функций мы реализуем ее позжеlogin
Установите среду, когда это логично. - Наконец, функция входа в облачную функцию, которая по умолчанию
main
функция как экспортируемая функция, являетсяasync
функции, мы можем обрабатывать асинхронную логику синхронным образом внутри функции.Как видите, эта функция получает два параметра:event
а такжеcontext
,event
Относится к событию, которое запускает облачную функцию. Когда сторона апплета вызывает облачную функцию, событие представляет собой параметр, передаваемый, когда сторона апплета вызывает облачную функцию, плюс серверная часть, автоматически внедряемая пользователем апплета.openid
И апплетыappid
.context
Объект содержит информацию о вызове и статус выполнения вызова, который можно использовать для понимания текущего статуса службы. Внутренний код функции, сгенерированный по умолчанию, в это время в основном получает контекстную информацию WeChat, а затем связывается сevent
Объекты возвращаются вместе, так что когда мы используем сторону апплета сTaro.cloud.callFunction
Возвращаемое значение, полученное при вызове этой функции, содержит контекстную информацию WeChat иevent
Объект.
Напишите облачную функцию входа в систему
Поняв конкретную логику облачной функции, мы немедленно реализуем нашу конкретную логику входа в облачную функцию, открываемfunctions/login/index.js
, и внесите соответствующие изменения в код следующим образом:
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
const db = cloud.database()
// 云函数入口函数
exports.main = async (event, context) => {
const { userInfo } = event
console.log('event', event)
try {
const { data } = await db
.collection('user')
.where({
nickName: userInfo.nickName,
})
.get()
if (data.length > 0) {
return {
user: data[0],
}
} else {
const { _id } = await db.collection('user').add({
data: {
...userInfo,
createdAt: db.serverDate(),
updatedAt: db.serverDate(),
},
})
const user = await db.collection('user').doc(_id)
return {
user,
}
}
} catch (err) {
console.error(`login ERR: ${err}`)
}
}
Видно, что приведенные выше изменения кода в основном включают следующие шесть:
- Сначала мы даем
cloud.init()
Передаются параметры окружения, мы используем встроенныйcloud.DYNAMIC_CURRENT_ENV
, указывающий, что он автоматически устанавливается в текущую облачную среду, то есть щелчок правой кнопкой мыши в инструменте разработчика апплетаfunctions
Среда, выбранная при использовании папки. - Далее проходим
cloud.database()
сгенерированный экземпляр данныхdb
, который позже используется для удобной работы с облачной базой данных в теле функции. - Тогда есть
main
Тело функции, мы сначала начнем сevent
Вызов в апплете получается из объектаTaro.cloud.callFunction
Прошло болееuserInfo
данные. - Затем следует выборка данных
try/catch
Заявление Блок, используемый для ловли ошибок, вtry
В блоке операторов мы используемdb
операция запроса:db.collection('user').where().get()
, представляющий запросwhere
условныйuser
Табличные данные, это должен быть массив, когда он узнает, если он не существует, чтобы удовлетворитьwhere
условный, то пустой массив, если существуетwhere
условно, затем вернутьuser
множество. - Далее мы оцениваем, пуст ли запрошенный массив пользователей, если он пуст, значит, пользователь еще не зарегистрирован, и создаем нового пользователя, если он не пуст, то возвращаем первый запрошенный элемент.
- Здесь мы используем
db.collection('user').add()
, чтобы добавитьuser
данные, а затемadd
прошел в методеdata
Поле, которое означает установить начальное значение этого пользователя, здесь мы дополнительно используемdb.serverDate()
Используется для записи времени создания пользователя и времени обновления пользователя, что удобно для последующего условного запроса, так как после добавления записи в БД будут возвращены записи только этой записи._id
, поэтому нам нужна дополнительная операцияdb.collection('user').doc()
чтобы получить эту запись, этоdoc
Используется для получения указанной ссылки на запись, эти данные возвращаются вместо массива.
Уведомление
Для связанных операций с облачной базой данных здесь вы можете обратиться к облачной документации апплета WeChat, которая содержит подробные примеры:документация по базе данных.
Редьюсер для асинхронных действий
Когда мы ранее обрабатывали вход в систему, внутри компонентаdispatch
охватыватьLOGIN
действие, в функции саги, которая обрабатывает асинхронные действия, используйтеput
Инициируется ряд действий для обновления статуса входа в магазин, и теперь мы реализуем действия, которые реагируют на эти действия.reducers
,Открытьsrc/reducers/user.js
, и внесите соответствующие изменения в код следующим образом:
import {
SET_LOGIN_INFO,
SET_IS_OPENED,
LOGIN_SUCCESS,
LOGIN,
LOGIN_ERROR,
LOGIN_NORMAL,
} from '../constants/'
const INITIAL_STATE = {
userId: '',
avatar: '',
nickName: '',
isOpened: false,
isLogin: false,
loginStatus: LOGIN_NORMAL,
}
export default function user(state = INITIAL_STATE, action) {
switch (action.type) {
case SET_IS_OPENED: {
const { isOpened } = action.payload
return { ...state, isOpened }
}
case SET_LOGIN_INFO: {
const { avatar, nickName, userId } = action.payload
return { ...state, nickName, avatar, userId }
}
case LOGIN: {
return { ...state, loginStatus: LOGIN, isLogin: true }
}
case LOGIN_SUCCESS: {
return { ...state, loginStatus: LOGIN_SUCCESS, isLogin: false }
}
case LOGIN_ERROR: {
return { ...state, loginStatus: LOGIN_ERROR, isLogin: false }
}
default:
return state
}
}
Взгляните и убедитесь, что приведенный выше код имеет три основных изменения:
- Сначала мы импортируем необходимые константы действия
- Затем мы даем
INITIAL_STATE
Добавлено несколько полей:-
userId
: используется для последующего получения пользовательских данных и для обозначения статуса входа пользователя в систему. -
isLogin
: используется для обозначения того, выполняется ли логика входа в систему во время процесса входа в систему,true
Указывает, что выполняется вход в систему,false
Указывает, что логика входа завершена -
loginStatus
: используется для отметки состояния во время входа в систему: начать вход (LOGIN
),Авторизация успешна(LOGIN_SUCCESS
),Авторизация не удалась(LOGIN_ERROR
)
-
- Ну наконец то
switch
Оператор отвечает на действие и обновляет соответствующее состояние.
Завершите остальную часть асинхронной логики пользователя.
Вход в WeChat
В предыдущем разделе «Реализация асинхронной логики Redux» мы сосредоточились на реализации асинхронной логики обычной кнопки входа, теперь давайте завершим логику использования входа в WeChat. Открытьsrc/components/WeappLoginButton/index.js
файл и внесите соответствующие изменения в его содержимое следующим образом:
import Taro, { useState } from '@tarojs/taro'
import { Button } from '@tarojs/components'
import { useDispatch } from '@tarojs/redux'
import './index.scss'
import { LOGIN } from '../../constants'
export default function WeappLoginButton(props) {
const [isLogin, setIsLogin] = useState(false)
const dispatch = useDispatch()
async function onGetUserInfo(e) {
setIsLogin(true)
const { avatarUrl, nickName } = e.detail.userInfo
const userInfo = { avatar: avatarUrl, nickName }
dispatch({
type: LOGIN,
payload: {
userInfo: userInfo,
},
})
setIsLogin(false)
}
return (
<Button
openType="getUserInfo"
onGetUserInfo={onGetUserInfo}
type="primary"
className="login-button"
loading={isLogin}
>
微信登录
</Button>
)
}
Как видите, приведенный выше код имеет три основных изменения:
- Мы удалили ранее установленную информацию для входа напрямую
SET_LOGIN_INFO
постоянная, вместоLOGIN
постоянный. - Затем мы удалили прямую настройку
storage
Логика кэшированного кода - Наконец, мы начнем
SET_LOGIN_INFO
Логика действия изменена на инициациюLOGIN
Асинхронное действие для обработки входа и сборкиuserInfo
объект какpayload
свойства объекта.
Потому что мы уже разобрались с этим в предыдущем разделе «Реализация асинхронной логики Redux».LOGIN
Вся логика асинхронного потока данныхdispatch
соответствующийLOGIN
action может обрабатывать асинхронную логику входа в WeChat.
оптимизацияuser
логический компонент верхнего уровня
Наконец, давайте завершимuser
компонент верхнего уровня логики,mine
страница, открытьsrc/pages/mine/mine.jsx
, и внесите соответствующие изменения в его содержимое следующим образом:
import Taro, { useEffect } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { useDispatch, useSelector } from '@tarojs/redux'
import { Header, Footer } from '../../components'
import './mine.scss'
import { SET_LOGIN_INFO } from '../../constants'
export default function Mine() {
const dispatch = useDispatch()
const nickName = useSelector(state => state.user.nickName)
const isLogged = !!nickName
useEffect(() => {
async function getStorage() {
try {
const { data } = await Taro.getStorage({ key: 'userInfo' })
const { nickName, avatar, _id } = data
// 更新 Redux Store 数据
dispatch({
type: SET_LOGIN_INFO,
payload: { nickName, avatar, userId: _id },
})
} catch (err) {
console.log('getStorage ERR: ', err)
}
}
if (!isLogged) {
getStorage()
}
})
return (
<View className="mine">
<Header />
<Footer />
</View>
)
}
Mine.config = {
navigationBarTitleText: '我的',
}
Как видите, мы внесли три изменения в приведенный выше код следующим образом:
- Сначала мы экспортировали
useSelector
Крючки, полученные из Redux StorenickName
. - Далее, поскольку мы были в разделе «Реализация асинхронной логики Redux», мы сохранили
userId
в магазин Редуксuser
логическая часть, так что здесь мы начинаем сstorage
понятно_id
, затем дайте предыдущийSET_LOGIN_INFO
изpayload
принесuserId
Атрибуты. - Наконец, мы судим
getStorage
Логика, только когда данных в Redux Store в это время нет, идем получать данные в хранилище для обновления Redux Store.
Расширьте диапазон чистых данных Logout
потому что в магазине Reduxuser
еще один атрибутuserId
свойства, поэтому мыLogout
компонентdispatch
действие, очиститьuserId
следующим образом:
import Taro, { useState } from '@tarojs/taro'
import { AtButton } from 'taro-ui'
import { useDispatch } from '@tarojs/redux'
import { SET_LOGIN_INFO } from '../../constants'
export default function LoginButton(props) {
const [isLogout, setIsLogout] = useState(false)
const dispatch = useDispatch()
async function handleLogout() {
setIsLogout(true)
try {
await Taro.removeStorage({ key: 'userInfo' })
dispatch({
type: SET_LOGIN_INFO,
payload: {
avatar: '',
nickName: '',
userId: '',
},
})
} catch (err) {
console.log('removeStorage ERR: ', err)
}
setIsLogout(false)
}
return (
<AtButton type="secondary" full loading={isLogout} onClick={handleLogout}>
退出登录
</AtButton>
)
}
резюме
Готово! здесь мы ставимuser
Логика подключена к облаку апплета и может успешно реализовать вход в облако апплета на апплете WeChat Давайте попробуем предварительно просмотреть изображение предварительного просмотра эффекта локальной отладки:
Как видите, мы отлаживаем облачную функцию локально, и шаги для доступа к облачной функции из апплета следующие:
- Мы сначала щелкаем правой кнопкой мыши
functions
папку и включите «Локальная отладка облачных функций». - Тогда выберите наш
login
Облачная функция, а затем нажмите, чтобы начать локальную отладку, чтобы мы могли отлаживать облачную функцию локально. - Затем мы нажимаем «Войти в WeChat» на стороне апплета, и тогда мы видим, что и консоль инструментов разработчика апплета, и консоль отладки облачных функций разрешают работу облачной функции в это время.
- Наконец, мы успешно вошли в систему, успешно отобразили логин и аватар в апплете и проверили облачную разработку> базу данных> таблицу пользователей, она добавила соответствующий
user
запись, указывающая, что мы успешно соединили терминал апплета и облако апплета.
Как правило, после локальной отладки мы можем загрузить облачную функцию в облако, чтобы мы могли использовать облачную функцию без включения локальной отладки, что необходимо для выпуска небольшой программы в Интернете.Щелкните правой кнопкой мыши в инструментах разработчика.functions
Соответствующую облачную функцию в папке, а затем выберите «Загрузить и развернуть: облачная установка так зависит»:
В этом уроке мы реализовали асинхронный процесс пользовательской логики, а в следующем уроке мы реализуем асинхронный процесс пост-логики, так что следите за обновлениями!
Хотите узнать больше интересных практических технических руководств? ПриходитьСообщество ТукеМагазин вокруг.
Исходный код, задействованный в этой статье, размещен вGithubНа, если вы считаете, что мы написали неплохо, я надеюсь, что вы можете поставить лайк этой статье ❤️ + звезда репозитория Github ❤️ о