Добро пожаловать, чтобы продолжить чтение серии «Крупномасштабное сражение по разработке мини-программы Таро», обзор предыдущей ситуации:
- Знакомый React, знакомые хуки: Мы реализовали очень простой прототип для добавления постов с помощью React и Hooks.
- Многостраничный прыжок и библиотека компонентов Taro UI: мы используем собственную функцию маршрутизации Taro для перехода на несколько страниц и используем библиотеку компонентов пользовательского интерфейса Taro для обновления интерфейса приложения.
В этой статье мы реализуем многотерминальный вход WeChat и Alipay. Если вы хотите начать прямо с этого, выполните следующую команду:
git clone -b third-part https://github.com/tuture-dev/ultra-club.git
cd ultra-club
Исходный код, задействованный в этой статье, размещен вGithubНа, если вы считаете, что мы написали неплохо, я надеюсь, что вы можете поставить лайк этой статье ❤️ + звезда репозитория Github ❤️ о~
Перед официальным стартом мы надеемся, что вы обладаете следующими знаниями:
- Базовые знания фреймворка React см.эта статьяучиться
- Для часто используемых хуков React (
useState
,useEffect
), а сообщество Tuque запустит «Время для чашки чая, начните работу с React Hooks», так что следите за обновлениями!
В дополнение к этому вам также потребуется скачать и установитьИнструменты разработчика Alipayи создайте свой собственный идентификатор мини-программы после входа в систему.
Мультитерминальный логин, группа демонов дико танцует
По сравнению с обычными веб-приложениями, апплет может реализовать вход в один клик на той платформе, где он находится, что очень удобно. На этом этапе мы также внедрим многотерминальный вход в систему (в основном, включая вход в WeChat и вход в Alipay). Причина, по которой название называется «Танцы демонов», не только вдохновлена редакторами «Shocked», но и тем, что способы, которыми различные платформы обрабатывают вход и аутентификацию, сегодня сильно различаются. наступить на многое.«Яма» действительно может реализовать «мультитерминальный вход».
Готов к работе
Планирование проектирования компонентов
Код в этом разделе очень длинный, поэтому перед официальным стартом давайте проверим планирование дизайна компонента, чтобы вы могли иметь четкое представление о работе, которую мы будем делать дальше.
Вы можете видеть, что страница «Моя» разбита в целом наHeader
а такжеFooter
:
-
Header
включатьLoggedMine
(личная информация), и если вы не вошли в системуLoginButton
(обычная кнопка входа),WeappLoginButton
(Кнопка входа в WeChat, которая появляется только в апплете WeChat) иAlipayLoginButton
(кнопка входа в Alipay, которая появляется только в апплете Alipay) -
Footer
Текст, используемый для отображения того, вошли ли вы в систему, он будет отображаться, если вы вошли в систему.Logout
(кнопка выхода)
Настройте плагин Babel
С этого шага мы впервые начнем писать асинхронный код. Этот проект будет использовать популярный async/await для написания асинхронной логики, поэтому давайте настроим соответствующий плагин Babel:
npm install babel-plugin-transform-runtime --save-dev
# yarn add babel-plugin-transform-runtime -D
затем вconfig/index.js
Чжунвэйconfig.babel.plugins
Добавьте соответствующую конфигурацию следующим образом:
const config = {
// ...
babel: {
// ...
plugins: [
// ...
[
'transform-runtime',
{
helpers: false,
polyfill: false,
regenerator: true,
moduleName: 'babel-runtime',
},
],
],
},
// ...
}
// ...
Реализация каждого компонента
Реализовать кнопку входа
Во-первых, давайте реализуем обычную кнопку входа в системуLoginButton
компоненты. Создайтеsrc/components/LoginButton
каталог, в котором создатьindex.js
, код показан ниже:
import Taro from '@tarojs/taro'
import { AtButton } from 'taro-ui'
export default function LoginButton(props) {
return (
<AtButton type="primary" onClick={props.handleClick}>
普通登录
</AtButton>
)
}
Мы использовали Taro UIAtButton
компонент и определяетhandleClick
События, которые будут переданы позже при использовании.
Реализовать WeappLoginButton
Затем мы реализуем кнопку входа в WeChat.WeappLoginButton
. Создайтеsrc/components/WeappLoginButton
каталог, в котором создатьindex.js
а такжеindex.scss
.index.js
код показывает, как показано ниже:
import Taro, { useState } from '@tarojs/taro'
import { Button } from '@tarojs/components'
import './index.scss'
export default function LoginButton(props) {
const [isLogin, setIsLogin] = useState(false)
async function onGetUserInfo(e) {
setIsLogin(true)
const { avatarUrl, nickName } = e.detail.userInfo
await props.setLoginInfo(avatarUrl, nickName)
setIsLogin(false)
}
return (
<Button
openType="getUserInfo"
onGetUserInfo={onGetUserInfo}
type="primary"
className="login-button"
loading={isLogin}
>
微信登录
</Button>
)
}
Видно, что в кнопке входа в WeChat и предыдущей обычной кнопке входа гораздо больше вещей:
- Добавлен
isLogin
Статус, который используется для указания, ожидает ли он входа в систему и следует ли изменить статус.setIsLogin
функция - Достигнуто
onGetUserInfo
Асинхронная функция используется для обработки логики после того, как пользователь нажимает кнопку входа и получает информацию. Среди них мы передаем полученную информацию о пользователеprops
серединаsetLoginInfo
, тем самым изменяя статус входа всего приложения - Добавлен
openType
(возможность открытия WeChat), здесь мы вводимgetUserInfo
(чтобы получить информацию о пользователе), чтобы увидеть все поддерживаемые открытые типы, см.Wechat Open Document Соответствующий раздел - Добавлен
onGetUserInfo
Этот обработчик используется для записи логики обработки после получения информации о пользователе, вот только что реализованный входящийonGetUserInfo
WeappLoginButton
стильindex.scss
код показывает, как показано ниже:
.login-button {
width: 100%;
margin-top: 40px;
margin-bottom: 40px;
}
Реализовать AlipayLoginButton
Давайте реализуем компонент кнопки входа в Alipay. Создайтеsrc/components/AlipayLoginButton
каталог, в котором создатьindex.js
а такжеindex.scss
.index.js
код показывает, как показано ниже:
import Taro, { useState } from '@tarojs/taro'
import { Button } from '@tarojs/components'
import './index.scss'
export default function LoginButton(props) {
const [isLogin, setIsLogin] = useState(false)
async function onGetAuthorize(res) {
setIsLogin(true)
try {
let userInfo = await Taro.getOpenUserInfo()
userInfo = JSON.parse(userInfo.response).response
const { avatar, nickName } = userInfo
await props.setLoginInfo(avatar, nickName)
} catch (err) {
console.log('onGetAuthorize ERR: ', err)
}
setIsLogin(false)
}
return (
<Button
openType="getAuthorize"
scope="userInfo"
onGetAuthorize={onGetAuthorize}
type="primary"
className="login-button"
loading={isLogin}
>
支付宝登录
</Button>
)
}
Как видите, содержимое в основном похоже на предыдущую кнопку входа в WeChat, но со следующими отличиями:
- выполнить
onGetAuthorize
Перезвоните. В отличие от предыдущей функции обратного вызова WeChat, здесь мы должны вызватьTaro.getOpenUserInfo
Вручную получить основную информацию о пользователе (фактически позвоните на открытую платформу Alipaymy.getOpenUserInfo) -
Button
компонентopenType
(возможность открытия Alipay) установлено наgetAuthorize
(Авторизация мини-программы) - При установлении открытой емкости как
getAuthorize
, вам нужно добавитьscope
собственностьuserInfo
, чтобы пользователь мог авторизовать апплет для получения основной информации о членах Alipay (другое допустимое значение —phoneNumber
, используется для получения номера телефона) - входящий
onGetAuthorize
Перезвоните
намекать
Подробную информацию о кнопке входа в апплет Alipay см.официальная документация.
файл стиляindex.scss
Код выглядит следующим образом:
.login-button {
width: 100%;
margin-top: 40px;
}
Внедрить LoggedMine
Затем мы реализуем состояние входа в системуLoggedMine
компоненты. Создайтеsrc/components/LoggedMine
каталог, в котором создатьindex.jsx
а такжеindex.scss
.index.jsx
код показывает, как показано ниже:
import Taro from '@tarojs/taro'
import { View, Image } from '@tarojs/components'
import PropTypes from 'prop-types'
import './index.scss'
import avatar from '../../images/avatar.png'
export default function LoggedMine(props) {
const { userInfo = {} } = props
function onImageClick() {
Taro.previewImage({
urls: [userInfo.avatar],
})
}
return (
<View className="logged-mine">
<Image
src={userInfo.avatar ? userInfo.avatar : avatar}
className="mine-avatar"
onClick={onImageClick}
/>
<View className="mine-nickName">
{userInfo.nickName ? userInfo.nickName : '图雀酱'}
</View>
<View className="mine-username">{userInfo.username}</View>
</View>
)
}
LoggedMine.propTypes = {
avatar: PropTypes.string,
nickName: PropTypes.string,
username: PropTypes.string,
}
Здесь мы добавили функцию нажатия на аватарку для предварительного просмотра, доступ к которой можно получить черезTaro.previewImage
функциявыполнить.
LoggedMine
Файл стиля для компонента выглядит следующим образом:
.logged-mine {
display: flex;
flex-direction: column;
align-items: center;
}
.mine-avatar {
width: 200px;
height: 200px;
border-radius: 50%;
}
.mine-nickName {
font-size: 40;
margin-top: 20px;
}
.mine-username {
font-size: 32px;
margin-top: 16px;
color: #777;
}
Реализовать компонент заголовка
После того, как все «мелкие детали» будут реализованы, мы реализуем весь интерфейс входа в систему.Header
часть. Создайтеsrc/components/Header
каталог, в котором создатьindex.js
а такжеindex.scss
.index.js
код показывает, как показано ниже:
import Taro from '@tarojs/taro'
import { View } from '@tarojs/components'
import { AtMessage } from 'taro-ui'
import LoggedMine from '../LoggedMine'
import LoginButton from '../LoginButton'
import WeappLoginButton from '../WeappLoginButton'
import AlipayLoginButton from '../AlipayLoginButton'
import './index.scss'
export default function Header(props) {
const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY
return (
<View className="user-box">
<AtMessage />
<LoggedMine userInfo={props.userInfo} />
{!props.isLogged && (
<View className="login-button-box">
<LoginButton handleClick={props.handleClick} />
{isWeapp && <WeappLoginButton setLoginInfo={props.setLoginInfo} />}
{isAlipay && <AlipayLoginButton setLoginInfo={props.setLoginInfo} />}
</View>
)}
</View>
)
}
Видно, что мы основывались наTaro.ENV_TYPE
Проверьте текущую платформу (WeChat, Alipay или другие), а затем определите, отображается ли кнопка входа соответствующей платформы.
намекать
Вы, возможно, нашли,
setLoginInfo
Все еще нужно дождаться входящего родительского компонента. Хотя хуки упрощают определение и обновление состояния, они не упрощают логику изменения состояния компонентов. На следующем шаге мы упростим с помощью Redux.
Header
Код стиля для компонента выглядит следующим образом:
.user-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
.login-button-box {
margin-top: 60px;
width: 100%;
}
Реализовать форму входа
Затем реализуем обычный логинLoginForm
компоненты. Поскольку целью этой серии руководств является объяснение Таро, процесс регистрации/входа здесь упрощен.Пользователи могут напрямую ввести свое имя пользователя и загрузить аватар для регистрации/входа без установки паролей и других процессов проверки. Создайтеsrc/components/LoginForm
каталог, в котором создатьindex.jsx
а такжеindex.scss
.index.jsx
код показывает, как показано ниже:
import Taro, { useState } from '@tarojs/taro'
import { View, Form } from '@tarojs/components'
import { AtButton, AtImagePicker } from 'taro-ui'
import './index.scss'
export default function LoginForm(props) {
const [showAddBtn, setShowAddBtn] = useState(true)
function onChange(files) {
if (files.length > 0) {
setShowAddBtn(false)
}
props.handleFilesSelect(files)
}
function onImageClick() {
Taro.previewImage({
urls: [props.files[0].url],
})
}
return (
<View className="post-form">
<Form onSubmit={props.handleSubmit}>
<View className="login-box">
<View className="avatar-selector">
<AtImagePicker
length={1}
mode="scaleToFill"
count={1}
files={props.files}
showAddBtn={showAddBtn}
onImageClick={onImageClick}
onChange={onChange}
/>
</View>
<Input
className="input-nickName"
type="text"
placeholder="点击输入昵称"
value={props.formNickName}
onInput={props.handleNickNameInput}
/>
<AtButton formType="submit" type="primary">
登录
</AtButton>
</View>
</Form>
</View>
)
}
Здесь мы используем Taro UIКомпонент выбора изображений ImagePicker, что позволяет пользователю выбрать изображение для загрузки.AtImagePicker
Наиболее важным атрибутом являетсяonChange
Функция обратного вызова, здесь мы передаем ее через родительский компонентhandleFilesSelect
функция, чтобы сделать это.
LoginForm
Код стиля для компонента выглядит следующим образом:
.post-form {
margin: 0 30px;
padding: 30px;
}
.input-nickName {
border: 1px solid #eee;
padding: 10px;
font-size: medium;
width: 100%;
margin-top: 40px;
margin-bottom: 40px;
}
.avatar-selector {
width: 200px;
margin: 0 auto;
}
Реализовать выход
После входа в систему нам также нужна кнопка для выхода из системы. Создайтеsrc/components/Logout/index.js
файл, код такой:
import Taro from '@tarojs/taro'
import { AtButton } from 'taro-ui'
export default function LoginButton(props) {
return (
<AtButton
type="secondary"
full
loading={props.loading}
onClick={props.handleLogout}
>
退出登录
</AtButton>
)
}
Реализовать нижний колонтитул
После того, как все подкомпоненты реализованы, давайте реализуемFooter
компоненты. Создайтеsrc/components/Footer
каталог, в котором создатьindex.jsx
а такжеindex.scss
.index.jsx
код показывает, как показано ниже:
import Taro, { useState } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { AtFloatLayout } from 'taro-ui'
import Logout from '../Logout'
import LoginForm from '../LoginForm'
import './index.scss'
export default function Footer(props) {
// Login Form 登录数据
const [formNickName, setFormNickName] = useState('')
const [files, setFiles] = useState([])
async function handleSubmit(e) {
e.preventDefault()
// 鉴权数据
if (!formNickName || !files.length) {
Taro.atMessage({
type: 'error',
message: '您还有内容没有填写!',
})
return
}
// 提示登录成功
Taro.atMessage({
type: 'success',
message: '恭喜您,登录成功!',
})
// 缓存在 storage 里面
const userInfo = { avatar: files[0].url, nickName: formNickName }
await props.handleSubmit(userInfo)
// 清空表单状态
setFiles([])
setFormNickName('')
}
return (
<View className="mine-footer">
{props.isLogged && (
<Logout loading={props.isLogout} handleLogout={props.handleLogout} />
)}
<View className="tuture-motto">
{props.isLogged ? 'From 图雀社区 with Love ❤' : '您还未登录'}
</View>
<AtFloatLayout
isOpened={props.isOpened}
title="登录"
onClose={() => props.handleSetIsOpened(false)}
>
<LoginForm
formNickName={formNickName}
files={files}
handleSubmit={e => handleSubmit(e)}
handleNickNameInput={e => setFormNickName(e.target.value)}
handleFilesSelect={files => setFiles(files)}
/>
</AtFloatLayout>
</View>
)
}
Footer
Код файла стиля компонента выглядит следующим образом:
.mine-footer {
font-size: 28px;
color: #777;
margin-bottom: 20px;
}
.tuture-motto {
margin-top: 40px;
text-align: center;
}
После того, как все виджеты сделаны, мы находимся вsrc/components
нужно только выставитьHeader
а такжеFooter
. Исправлятьsrc/components/index.jsx
, код показан ниже:
import PostCard from './PostCard'
import PostForm from './PostForm'
import Footer from './Footer'
import Header from './Header'
export { PostCard, PostForm, Footer, Header }
Обновите «Мою» страницу
Пришло время использовать написанноеHeader
а такжеFooter
компонентов, но перед этим поговорим о том, что нам нужно использоватьuseEffect
Крючки.
useEffect Hooks
useEffect
Хуки используются для замены функции хука жизненного цикла оригинального React.Мы можем инициировать в нем некоторые операции «побочного эффекта», такие как асинхронное получение внутренних данных, установка таймеров или выполнение операций DOM:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 和 componentDidMount 以及 componentDidUpdate 类似:
useEffect(() => {
// 使用浏览器 API 更新 document 的标题
document.title = `你点击了 ${count} 次`;
});
return (
<div>
<p>你点击了 {count} 次</p>
<button onClick={() => setCount(count + 1)}>
点我
</button>
</div>
);
}
вышеуказанная параdocument
Модификация заголовка — операция с побочными эффектами, в предыдущем React-приложении мы обычно писали:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
componentDidMount() {
document.title = `你点击了 ${this.state.count} 次`;
}
componentDidUpdate() {
document.title = `你点击了 ${this.state.count} 次`;
}
render() {
return (
<div>
<p>你点击了 {this.state.count} 次</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
点我
</button>
</div>
);
}
}
Если вы хотите знатьuseEffect
Для получения более подробной информации вы можете проверить Reactофициальная документация.
отличная работа! понялuseEffect
После концепции хуков, давайте сразу обновим компонент «Моя страница»src/pages/mine/mine.jsx
, код показан ниже:
import Taro, { useState, useEffect } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { Header, Footer } from '../../components'
import './mine.scss'
export default function Mine() {
const [nickName, setNickName] = useState('')
const [avatar, setAvatar] = useState('')
const [isOpened, setIsOpened] = useState(false)
const [isLogout, setIsLogout] = useState(false)
// 双取反来构造字符串对应的布尔值,用于标志此时是否用户已经登录
const isLogged = !!nickName
useEffect(() => {
async function getStorage() {
try {
const { data } = await Taro.getStorage({ key: 'userInfo' })
const { nickName, avatar } = data
setAvatar(avatar)
setNickName(nickName)
} catch (err) {
console.log('getStorage ERR: ', err)
}
}
getStorage()
})
async function setLoginInfo(avatar, nickName) {
setAvatar(avatar)
setNickName(nickName)
try {
await Taro.setStorage({
key: 'userInfo',
data: { avatar, nickName },
})
} catch (err) {
console.log('setStorage ERR: ', err)
}
}
async function handleLogout() {
setIsLogout(true)
try {
await Taro.removeStorage({ key: 'userInfo' })
setAvatar('')
setNickName('')
} catch (err) {
console.log('removeStorage ERR: ', err)
}
setIsLogout(false)
}
function handleSetIsOpened(isOpened) {
setIsOpened(isOpened)
}
function handleClick() {
handleSetIsOpened(true)
}
async function handleSubmit(userInfo) {
// 缓存在 storage 里面
await Taro.setStorage({ key: 'userInfo', data: userInfo })
// 设置本地信息
setAvatar(userInfo.avatar)
setNickName(userInfo.nickName)
// 关闭弹出层
setIsOpened(false)
}
return (
<View className="mine">
<Header
isLogged={isLogged}
userInfo={{ avatar, nickName }}
handleClick={handleClick}
setLoginInfo={setLoginInfo}
/>
<Footer
isLogged={isLogged}
isOpened={isOpened}
isLogout={isLogout}
handleLogout={handleLogout}
handleSetIsOpened={handleSetIsOpened}
handleSubmit={handleSubmit}
/>
</View>
)
}
Mine.config = {
navigationBarTitleText: '我的',
}
Как видите, мы сделали следующее:
- использовать
useState
Создаются четыре состояния: Информация о пользователе (nickName
а такжеavatar
), открыт ли всплывающий слой входа в систему (isOpened
), успешно ли выполнен вход (isLogged
) и соответствующую функцию обновления - пройти через
useEffect
Хук пытается получить информацию о пользователе из локального кеша (Taro.getStorage) и используется для обновленияnickName
а такжеavatar
условие - осознал давно потерянное
setLoginInfo
функцию, где мы не только обновилиnickName
а такжеavatar
состояние, а также хранить пользовательские данные в локальном кеше (Taro.getStorage), убедитесь, что вы остаетесь в системе в следующий раз, когда откроете его. - добился того же давно потерянного
handleLogout
функция, которая не только обновляет актуальное состояние, но и удаляет данные в локальном кеше (Taro.removeStorage) - Реализовано для обработки обычных входов в систему
handleSubmit
функция, содержание в основном такое же, какsetLoginInfo
последовательный - Рендеринг при возврате к коду JSX
Header
а такжеFooter
Компонент, передача в соответствующее состояние и функция обратного вызова
КорректированиеMine
стиль компонентаsrc/pages/mine/mine.scss
код показывает, как показано ниже:
.mine {
margin: 30px;
height: 90vh;
padding: 40px 40px 0;
display: flex;
flex-direction: column;
justify-content: space-between;
}
Наконец вsrc/app.scss
Введите стили соответствующих компонентов пользовательского интерфейса Таро в:
@import './custom-theme.scss';
@import '~taro-ui/dist/style/components/button.scss';
@import '~taro-ui/dist/style/components/fab.scss';
@import '~taro-ui/dist/style/components/icon.scss';
@import '~taro-ui/dist/style/components/float-layout.scss';
@import '~taro-ui/dist/style/components/textarea.scss';
@import '~taro-ui/dist/style/components/message.scss';
@import '~taro-ui/dist/style/components/avatar.scss';
@import '~taro-ui/dist/style/components/image-picker.scss';
@import '~taro-ui/dist/style/components/icon.scss';
Посмотреть эффект
Наконец-то добрались до священной ссылки принятия. Первый — обычный логин:
Для входа в WeChat и Alipay после нажатия вы войдете в систему напрямую с учетной записью, используемой для входа в инструменты разработчика. Ниже показан интерфейс после входа в WeChat и Alipay:
После входа в систему нажмите кнопку «Выйти» ниже, чтобы выйти из текущей учетной записи.
На этом третья часть «Масштабной практики разработки мультитерминальной мини-программы Таро» завершена. В следующихГлава 4В , мы будем постепенно использовать Redux для реконструкции потока бизнес-данных, чтобы наше теперь немного раздутое управление состоянием стало понятным и контролируемым.
Хотите узнать больше интересных практических технических руководств? ПриходитьСообщество ТукеМагазин вокруг.
Исходный код, задействованный в этой статье, размещен вGithubНа, если вы считаете, что мы написали неплохо, я надеюсь, что вы можете поставить лайк этой статье ❤️ + звезда репозитория Github ❤️ о~