加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 资源网站 > 资源 > 正文

Taro 小程序开发大型实战:尝鲜微信小程序云

发布时间:2020-12-14 19:02:49 所属栏目:资源 来源:网络整理
导读:欢迎继续阅读《Taro 小程序开发大型实战》系列,前情回顾: 熟悉的 React,熟悉的 Hooks?:我们用 React 和 Hooks 实现了一个非常简单的添加帖子的原型 多页面跳转和 Taro UI 组件库?:我们用 Taro 自带的路由功能实现了多页面跳转,并用 Taro UI 组件库升级

欢迎继续阅读《Taro 小程序开发大型实战》系列,前情回顾:

  • 熟悉的 React,熟悉的 Hooks?:我们用 React 和 Hooks 实现了一个非常简单的添加帖子的原型
  • 多页面跳转和 Taro UI 组件库?:我们用 Taro 自带的路由功能实现了多页面跳转,并用 Taro UI 组件库升级了应用界面
  • 实现微信和支付宝多端登录?:实现了微信、支付宝以及普通登录和退出登录
  • Hooks + Redux 双剑合璧?:使用了 Hooks 版的 Redux 来重构应用的状态管理
  • 使用 Hooks 版的 Redux 实现大型应用状态管理(上篇)?:使用 Hooks 版的 Redux 实现了?user?逻辑的状态管理重构
  • 使用 Hooks 版的 Redux 实现大型应用状态管理(下篇)?:使用 Hooks 版的 Redux 实现了?post?逻辑的状态管理重构
  • Taro 小程序开发大型实战(六):尝鲜微信小程序云(上篇)?:?user?逻辑接入微信小程序云

在上一篇文章中,我们将我们两大逻辑之一 User 部分接入了 Redux 异步处理流程,接着接入了微信小程序云,使得 User 逻辑可以在云端永久保存,好不自在:),两兄弟一个得了好处,另外一个不能干瞪眼对吧?在这一篇教程中,我们想办法把 User 另外一个兄弟 Post 捞上来,也把 Redux 异步流程和微信小程序给它整上,这样就齐活了:laughing:。

我们首先来看一看最终的完成效果:

如果你不熟悉 Redux,推荐阅读我们的《Redux 包教包会》系列教程:

  • Redux 包教包会(一):解救 React 状态危机
  • Redux 包教包会(二):趁热打铁,完全重构
  • Redux 包教包会(三):各司其职,重拾初心

如果你希望直接从这一步开始,请运行以下命令:

git clone -b miniprogram-start https://github.com/tuture-dev/ultra-club.git
cd ultra-club
复制代码

本文所涉及的源代码都放在了?Github?上,如果您觉得我们写得还不错,希望您能给?:heart:这篇文章点赞+Github仓库加星???哦~

此教程属于?React 前端工程师学习路线?的一部分,欢迎来 Star 一波,鼓励我们继续创作出更好的教程,持续更新中~

“六脉神剑” 搞定 createPost 异步逻辑

不知道看到这里的读者有没有发现上篇文章其实打造了一套讲解模式,即按照如下的 “六步流程” 来讲解,我们也称为 “六脉神剑” 讲解法:

sagas
sagas
sagas
reducers

可以看到我们上面的讲解顺序实际上是按照前端数据流的流动来进行的,我们对标上面的讲解逻辑来看一下前端数据流是如何流动的:

  • 从组件中通过对应的常量发起异步请求
  • sagas?监听到对应的异步请求,开始处理流程
  • 在?sagas?调用对应的前端 API 文件向微信小程序云发起请求
  • 微信小程序云函数处理对应的 API 请求,返回数据
  • sagas?中获取到对应的数据,?dispatch?action 到对应的?reducers?处理逻辑
  • reducers?接收数据,开始更新本地 Redux Store 中的?state
  • 组件中重新渲染

好的,了解了讲解逻辑和对应前端数据流动逻辑之后,我们马上来实践这套逻辑,把 User 逻辑的好兄弟 Post 逻辑搞定。

第一剑:?PostForm?组件中发起异步请求

首先从创建帖子逻辑动刀子,我们将创建帖子接入异步逻辑并接通小程序云,让文章上云。打开?src/components/PostForm/index.jsx?,对其中的内容作出对应的修改如下:

import { useDispatch,useSelector } from '@tarojs/redux'

import './index.scss'
import { CREATE_POST } '../../constants'

export default function PostForm() {
  const [formTitle,setFormTitle] = useState('')
  const [formContent,setFormContent] = useState('')

  const userId = useSelector(state => state.user.userId)

  const dispatch = useDispatch()
...    }

    dispatch({
      type: CREATE_POST,payload: {
        postData: {
          title: formTitle,content: formContent,},userId,})

    setFormTitle('')
    setFormContent('')
  }

  return (
复制代码

可以看到,上面的内容做了如下四处修改:

  • 首先我们现在是接收用户的文章输入数据然后向小程序云发起创建文章的请求,所以我们将之前的?dispatch?SET_POSTS Action 改为 CREATE_POST Action,并且将之前的 action payload 简化为?postData?和?userId?,因为我们可以通过小程序云数据库查询?userId?得到创建文章的用户信息,所以不需要再携带用户的数据。
  • 接着,因为我们不再需要用户的?avatar?和?nickName?数据,所以我们删掉了对应的?useSelector?语句。
  • 接着,因为请求是异步的,所以需要等待请求完成之后再设置对应的发表文章的状态以及发表文章弹出层状态,所以我们删掉了对应的?dispatch?SET_POST_FORM_IS_OPENED Action 逻辑以及?Taro.atMessage?逻辑。
  • 最后我们删掉不需要的常量?SET_POSTS?和?SET_POST_FORM_IS_OPENED?,然后导入异步创建文章的常量?CREATE_POST?。

增加 Action 常量

我们在上一步中使用到了?CREATE_POST?常量,打开?src/constants/post.js?,在其中增加?CREATE_POST?常量:

复制代码

到这里,我们的 “六步流程” 讲解法就走完了第一步,即从组件中发起对应的异步请求,这里我们是发出的?action.type?为?CREATE_POST?的异步请求。

第二剑: 声明和补充对应需要的异步?sagas?文件

在 “第一剑” 中,我们从组件中 dispatch 了 action.type 为?CREATE_POST?的异步 Action,接下来我们要做的就是在对应的?sagas?文件中补齐响应这个异步 action 的 sagas。

在?src/sagas/?文件夹下面创建?post.js?文件,并在其中编写如下创建文章的逻辑:

import { call,put,take,fork } 'redux-saga/effects'

import { postApi } '../api'
import {
  CREATE_POST,POST_SUCCESS,POST_ERROR,SET_POSTS,SET_POST_FORM_IS_OPENED,} '../constants'

function* createPost(postData,userId) {
  try {
    const post = yield call(postApi.createPost,postData,userId)

    // 其实以下三步可以合成一步,但是这里为了讲解清晰,将它们拆分成独立的单元

    // 发起发帖成功的 action
    yield put({ type: POST_SUCCESS })

    // 关闭发帖框弹出层
    yield put({ type: SET_POST_FORM_IS_OPENED,payload: { isOpened: false } })

    // 更新 Redux store 数据
    yield put({
      type: SET_POSTS,payload: {
        posts: [post],})

    // 提示发帖成功
    Taro.atMessage({
      message: '发表文章成功',type: 'success',})
  } catch (err) {
    console.log('createPost ERR: ',err)

    // 发帖失败,发起失败的 action
    yield put({ type: POST_ERROR })

    // 提示发帖失败
    Taro.atMessage({
      message: '发表文章失败',0);">'error',})
  }
}

watchCreatePost() {
  while (true) {
    const { payload } = yield take(CREATE_POST)

    'payload',payload)

    yield fork(createPost,payload.postData,payload.userId)
  }
}

export { watchCreatePost }
复制代码

可以看到,上面的改动主要是创建?watcherSaga?和?handlerSaga?。

创建?watcherSaga

  • 我们创建了登录的?watcherSaga?:?watchCreatePost?,它用来监听?CREATE_POST?的 action,并且当监听到?CREATE_POST?action 之后,从这个 action 中获取必要的?userId?数据,然后激活?handlerSaga?:?createPost?去处理对应的创建帖子的逻辑。
  • 这里的?watchCreatePost?是一个生成器函数,它内部是一个?while?无限循环,表示在内部持续监听?CREATE_POST?action。
  • 在循环内部,我们使用了?redux-saga?提供的?effects helper?函数:?take?,它用于监听?CREATE_POST?action,获取 action 中携带的数据。
  • 接着我们使用了另外一个?fork?,它表示非阻塞的执行?handlerSaga:?createPost?,并将?payload.postData?和?payload.userId?作为参数传给?createPost?。

handlerSaga

  • 我们创建了创建帖子的?createPost?,它用来处理创建逻辑。
  • createPost?也是一个生成器函数,在它内部是一个?try/catch?语句,用于处理创建帖子请求可能存在的错误情况。
  • 在?try?语句中,首先是使用了?redux-saga?提供给我们的?call?来调用登录的 API:?postApi.createPost?,并把?userId?作为参数传给这个 API。
    • 如果创建帖子成功,我们使用?effects helpers?函数:?put?,?put类似之前在?view?中的?dispatch?操作,,来?dispatch?了三个 action:?POST_SUCCESS?,?SET_POST_FORM_IS_OPENED?,?SET_POSTS?,代表更新创建帖子成功的状态,关闭发帖框,设置最新创建的帖子信息到 Redux Store 中。
    • 最后我们使用了 Taro UI 提供给我们的消息框,来显示一个?success?消息。
  • 如果发帖失败,我们则使用?put?发起一个?POST_ERROR?的 action 来更新创建帖子失败的信息到 Redux Store,接着使用了 Taro UI 提供给我们的消息框,来显示一个?error?消息。

一些额外的工作

为了创建?handlerSaga?,我们还导入了?postApi?,我们将在后面来创建这个 API。

除此之外我们还导入了需要使用的 action 常量:

这里的?POST_SUCCESS?和?POST_ERROR?我们还没有创建,我们将马上在 “下一剑” 中创建它。

以及一些?redux-saga/effects?相关的 helper 函数,我们已经在之前的内容中详细讲过了,这里就不再赘述了。

加入 saga 中心调度文件

我们像之前将?watchLogin?等加入到?sagas?中心调度文件一样,将我们创建好的?watchCreatePost?也加入进去:

// ...之前的逻辑
import { watchCreatePost } './post'
rootSaga() {
  yield all([
   // ... 之前的逻辑
    fork(watchCreatePost)
  ])
}
复制代码

第三剑:定义?sagas?需要的常量文件

打开?src/constants/post.js?文件,定义我们之前创建的常量文件如下:

const POST_ERROR = 'POST_ERROR'
复制代码

第四剑:定义?sagas?涉及到的前端 API 文件

在之前的?post?saga 文件里面,我们使用到了?postApi?,它里面封装了用于向后端(这里我们是小程序云)发起和帖子有关请求的逻辑,让我们马上来实现它吧。

src/api/?文件夹下添加?post.js?文件,并在文件中编写内容如下:


async const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
  const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY

  'postData',userId)

  // 针对微信小程序使用小程序云函数,其他使用小程序 RESTful API
  if (isWeapp) {
      const { result } = await Taro.cloud.callFunction({
        name: 'createPost',data: {
          postData,})

      return result.post
    }
  } console.error(const postApi = {
  createPost,}
default postApi;
复制代码

在上面的代码中,我们定义了?createPost?函数,它是一个?async?函数,用来处理异步逻辑,在?createPost?函数中,我们对当前的环境进行了判断,且只在微信小程序,即?isWeapp?的条件下执行创建帖子的操作,对于支付宝小程序和 H5,我们则放在下一节使用 LeanCloud 的 Serverless 来解决。

创建帖子逻辑是一个?try/catch?语句,用于捕捉可能存在的请求错误,在?try?代码块中,我们使用了?Taro?为我们提供的微信小程序云的云函数 API?Taro.cloud.callFunction?来便捷的向小程序云发起云函数调用请求。

这里我们调用了一个?createPost?云函数,并将?userId?作为参数传给云函数,用于在云函数中使用用户 Id 和帖子数据来创建一个属于此用户的帖子并保存到数据库,我们将在下一节中实现这个云函数。

如果调用成功,我们可以接收返回值,用于从后端返回数据,这里我们返回了?result.post?数据。

如果调用失败,则打印错误。

最后我们定义了一个?postApi?对象,用于存放所有和用户逻辑有个的函数,并添加?createPost?API 属性然后将其导出,这样在?post?saga 函数里面就可以导入?postApi?然后通过?postApi.createPost?的方式来调用?createPost?API 处理创建帖子的逻辑了。

在 API 默认文件统一导出

src/api/index.js?文件中导入上面创建的?postApi?并进行统一导出如下:

第五剑:创建对应的微信小程序云函数

创建 createPost 云函数

按照和之前创建?login?云函数类似,我们创建?createPost?云函数。

创建成功之后,我们可以得到两个文件,一个是?functions/createPost/package.json?文件,它和之前的类似。

{
  "name": "createPost","version": "1.0.0",0);">"description": "",0);">"main": "index.js",0);">"scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },0);">"author": "license": "ISC",0);">"dependencies": {
    "wx-server-sdk": "latest"
  }
}
复制代码

第二个文件就是我们需要编写创建帖子逻辑的?functions/createPost/index.js?文件,微信小程序开发者工具会默认为我们生成一段样板代码。

我们在?function/createPost?文件夹下同样运行?npm install?安装对应的云函数依赖,这样我们才能运行它。

编写 createPost 云函数

functions/createPost/index.js?文件,对其中的内容作出对应的修改如下:

)

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV,})

const db = cloud.database()

// 云函数入口函数
exports.main = async (event,context) => {
  const { postData,userId } = event

  console.log('event',event)

  const user = await db
      .collection('user')
      .doc(userId)
      .get()
    const { _id } = await db.collection('post').add({
      data: {
        ...postData,user: user.data,createdAt: db.serverDate(),updatedAt: db.serverDate(),})

    const newPost = 'post')
      .doc(_id)
      .get()

    return {
      post: { ...newPost.data },}
  } catch (err) {
    console.error(`createUser ERR: ${err}`)
  }
}
复制代码

可以看到上面的代码改动主要有以下七处:

  • 首先我们给?cloud.init()?传入了环境参数,我们使用了内置的?cloud.DYNAMIC_CURRENT_ENV,表示自动设置为当前的云环境,即在右键点击小程序开发者工具里?functions?文件夹时选择的环境。
  • 接着,我们通过?cloud.database()?生成了数据实例?db?,用于之后在函数体中便捷的操作云数据库。
  • 接着就是?main?函数体,我们首先从?event?对象中取到了在小程序的调用?Taro.cloud.callFunction?传过来的?userId?数据。
  • 然后,跟着取数据的是一个?try/catch?语句块,用于捕获错误,在?try?语句块中,我们使用?db?的查询操作:?db.collection('user').doc(userId).get()?,表示查询?id?为?userId?的?user?表数据,它查出来应该是个唯一值,如果不存在满足?where?条件的,那么是一个?null值,如果存在满足 条件的,那么返回一个?user?对象。
  • 接着,我们使用的?db.collection('post').add()?添加一个?post?数据,然后在?add?方法中传入?data?字段,这里我们不仅传入了?postData?,还将?user?也一同传入了,原因我们将在之后来讲解。除此之外,这里我们额外使用了?db.serverDate()?用于记录创建此帖子的时间和更新此帖子的时间,方便之后做条件查询。
  • 接着,因为向数据库添加一个记录之后只会返回此记录的?_id?,所以我们需要一个额外的操作?db.collection('post').doc()?来获取此条记录,这个?doc?用于获取指定的记录引用,返回的是这条数据,而不是一个数组。
  • 最后我们返回新创建的?post?。

提示

我们在上面创建?post?的时候,将?user?对象也添加到了?post?数据中,这里是因为小程序云数据库是 JSON 数据库,所以没有关系数据库的外键概念,导致建关系困难,所以为了之后查询?post?的时候方便展示?user?数据,我们才这样保存的. 当然更加科学的做法是在?post?里面保存?userId?,这样能减少数据冗余,但是因为做教学用,所以这些我们偷了一点懒。

所以我们这里强烈建议,在正规的环境下,关系型数据库应该建外键,JSON 数据库也至少应该保存?userId?。:

第六剑: 定义对应的?reducers?文件

我们在前面处理创建帖子时,在组件内部?dispatch?了?CREATE_POST?action,在处理异步 action 的 saga 函数中,使用?put?发起了一系列更新 store 中帖子状态的 action,现在我们马上来实现响应这些 action 的?reducers?,打开?src/reducers/post.js?,对其中的代码做出对应的修改如下:


import avatar from '../images/avatar.png'

const INITIAL_STATE = {
  posts: [],  post: {},96);">  isOpened: false,96);">  isPost:   postStatus: POST_NORMAL,}

export default function post(state = INITIAL_STATE,action) {
  switch (action.type) {
    case SET_POST: {
      const { post } = action.payload
      return { ...state,post }
    }

    SET_POSTS: {
      const { posts } = action.payload
      posts: state.posts.concat(...posts) }
    }

    SET_POST_FORM_IS_OPENED: {...      CREATE_POST: {
      postStatus: CREATE_POST,0);">isPost: true }
    }

    POST_SUCCESS: {
      postStatus: POST_SUCCESS,96);">false }
    }

    POST_ERROR: {
      postStatus: POST_ERROR,96);">false }
    }

    default:
      return state
  }
复制代码

看一看到上面的代码主要有三处改动:

  • 首先我们导入了必要的 action 常量
  • 接着我们给?INITIAL_STATE?增加了几个字段:
    isPost?:用于标志帖子逻辑过程中是否在执行创帖逻辑,?true?表示正在执行创帖中,?false?表示登录逻辑执行完毕
    • postStatus?:用于标志创帖过程中的状态:开始创帖(?CREATE_POST?)、创帖成功(?POST_SUCCESS?)、登录失败(?POST_ERROR?)
  • 最后就是?switch?语句中响应 action,更新相应的状态。

“六脉神剑” 搞定 getPosts 异步逻辑

在上一 “大” 节中,我们使用了图雀社区不传之术:“六脉神剑” 搞定了?createPost?的异步逻辑,现在我们马上趁热打铁来巩固我们的武功,搞定 getPosts 异步逻辑,它对应着我们小程序底部两个 tab 栏的第一个,也就是我们打开小程序的首屏渲染逻辑,也就是一个帖子列表。

index?组件中发起异步请求

src/pages/index/index.jsx?文件,对其中的内容作出对应的修改如下:

import {
  SET_POST_FORM_IS_OPENED,SET_LOGIN_INFO,GET_POSTS,0); font-weight: bold;">Index() {
  const posts = useSelector(state => state.post.posts) || []...  const dispatch = useDispatch()

  useEffect(() => {
    const WeappEnv = Taro.getEnv() === Taro.ENV_TYPE.WEAPP

    if (WeappEnv) {
      Taro.cloud.init()
    }

    getStorage() {
      try {
        const { data } = await Taro.getStorage({ key: 'userInfo' })

        const { nickName,avatar,_id } = data

        // 更新 Redux Store 数据
        dispatch({
          type: SET_LOGIN_INFO,payload: { nickName,userId: _id },})
      } catch (err) {
        'getStorage ERR: ',err)
      }
    }

    if (!isLogged) {
      getStorage()
    }

    getPosts() {
      try {
        // 更新 Redux Store 数据
        dispatch({
          type: GET_POSTS,0);">'getPosts ERR: ',51); font-weight: 700;">if (!posts.length) {
      getPosts()
    }
  },[])

  setIsOpened(isOpened) {
    dispatch({ type: SET_POST_FORM_IS_OPENED,payload: { isOpened } })...  return (
    <View className="index">
      <AtMessage />
      {posts.map(post => (
        <PostCard key={post._id} postId={post._id} post={post} isList />
      ))}
      <AtFloatLayout
        isOpened={isOpened}
复制代码

  • 首先我们对当前的开发环境做了判断,如果是微信小程序环境,我们就使用?Taro.cloud.init()?进行小程序环境的初始化。
  • 接着,我们在?useEffects?Hooks 里面定义了?getPosts?函数,它是一个异步函数,用于?dispatch?GET_POSTS 的异步请求,并且我们进行了判断,当此时 Redux Store 内部没有文章时,才进行数据的获取。
  • 接着,我们改进了?getStorage?获取缓存的函数,将其移动到?useEffects?Hooks 里面,并额外增加了?_id?属性,它被赋值给?userId?一起设置 Redux Store 中关于用户的属性,这样做的目的主要是为了之后发帖标志用户,或者获取用户的个人信息用。并且,加了一层?if?判断,只有当没有登录时,即?isLogged?为 false 的时候,才进行获取缓存操作。
  • 最后我们导入了必要的?GET_POSTS?常量,并且将?return?语句里的?PostCard?接收的?key?和?postId?属性变成了真实的帖子?_id?。这样我们在帖子详情时可以直接拿?postId?向小程序云发起异步请求。
  • 注意

    在上一篇教程中,有同学提到没有使用?Taro.cloud.init()?初始化的问题,是因为分成了两篇文章,在这篇文章才初始化。要使用小程序云,初始化环境是必要的。

    第二剑:声明和补充对应需要的异步?GET_POSTS?的异步 Action,接下来我们要做的就是在对应的?src/sagas/post.js?文件,在其中定义?getPosts?sagas 逻辑如下:

    watchGetPosts?,它用来监听?GET_POSTS的 action,并且当监听到?GET_POSTS?action 之后,然后激活?getPosts?去处理对应的获取帖子列表的逻辑。
    
  • 这里的?watchGetPosts?是一个生成器函数,它内部是一个?GET_POSTS?action。
  • 在循环内部,我们使用了?GET_POSTS?action,获取 action 中携带的数据。
  • 接着我们使用了另外一个?getPosts?,因为这里获取帖子列表不需要传数据,所以这里没有额外的数据传递逻辑。
  • getPosts?,它用来处理创建逻辑。
  • getPosts?也是一个生成器函数,在它内部是一个?try/catch?语句,用于处理获取帖子列表请求可能存在的错误情况。
  • 在?postApi. getPosts?。
    • 如果获取帖子列表成功,我们使用?put?类似之前在?dispatch?了两个 action:?SET_POSTS?,代表更新获取帖子列表成功的状态,设置最新获取的帖子列表到 Redux Store 中。
  • 如果获取帖子列表失败,我们则使用?POST_ERROR?的 action 来更新获取帖子列表失败的信息到 Redux Store
  • postApi. getPosts?,我们将在后面来创建这个 API。

    除此之外我们还导入了需要使用的 action 常量:

    • GET_POSTS?:响应获取帖子列表的 ACTION 常量,我们将在 “第三剑” 中创建它。

    watchCreatePost?等加入到?watchGetPosts?也加入进去:

    const GET_POSTS = 'GET_POSTS'
    复制代码

    postApi.getPosts?,它里面封装了用于向后端(这里我们是小程序云)发起和获取帖子列表有关请求的逻辑,让我们马上来实现它吧。

    src/api/post.js?文件,并在其中编写内容如下:

    ,51); font-weight: 700;">return result.posts
        }
      } const postApi = {
      // ... 之前的 API
      getPosts,}
    
    // ... 其余逻辑一样
    复制代码

    getPosts?函数,它是一个?getPosts?函数中,我们对当前的环境进行了判断,且只在微信小程序,即?isWeapp?的条件下执行获取帖子列表的操作,对于支付宝小程序和 H5,我们则放在下一节使用 LeanCloud 的 Serverless 来解决。

    getPosts?云函数,我们将在下一节中实现这个云函数。

    result.posts数据,即从小程序云返回的帖子列表。

    最后我们在已经定义好的?postApi?对象里,添加?getPosts?API 属性然后将其导出,这样在?postApi. getPosts?的方式来调用?getPosts?API 处理获取帖子列表的逻辑了。

    创建 getPosts 云函数

    createPost?云函数类似,我们创建?getPosts?云函数。

    functions/getPosts/package.json?文件,它和之前的类似。

    functions/getPosts/index.js?文件,微信小程序开发者工具会默认为我们生成一段样板代码。

    function/getPosts?文件夹下同样运行?编写 getPosts 云函数

    functions/getPosts/index.js?文件,对其中的内容作出对应的修改如下:

    )
    
    cloud.init({
      env: cloud.DYNAMIC_CURRENT_ENV,51); font-weight: 700;">const db = cloud.database()
    const _ = db.command
    
    async (event,0);">'post').get()
    
        return {
          posts: data,51); font-weight: 700;">catch (e) {
        `getPosts ERR: ${e}`)
      }
    }
    复制代码

    可以看到上面的代码改动主要有以下处:

      main?函数体,里面是一个?db.collection('post').get()?,表示查询所有的?post?数据。
    • 最后我们返回查询到的?posts?数据。

    因为这里?SET_POSTS?的 Action 我们在上一 “大” 节中创建帖子时已经定义了,所有在 “这一剑” 中我们无需添加额外的代码,复用之前的逻辑就好。

    “六脉神剑” 搞定 getPost 异步逻辑

    在上面两 “大” 节中,我们连续用了两次 “六脉神剑”,相信跟到这里的同学应该对我们接下来要做的事情已经轻车熟路了吧:grin:。

    接下来,我们将收尾 Post 逻辑的最后一公里,即帖子详情的异步逻辑 “getPost” 接入,话不多说就是干!

    post?组件中发起异步请求

    src/pages/post/post.jsx?文件,对其中的内容作出对应的修改如下:

    import { PostCard } './post.scss'
    import { GET_POST,SET_POST } Post() {
      const router = useRouter()
      const { postId } = router.params
    
      const dispatch = useDispatch()
      const post = useSelector(state => state.post.post)
    
      useEffect(() => {
        dispatch({
          type: GET_POST,payload: {
            postId,51); font-weight: 700;">return () => {
          dispatch({ type: SET_POST,payload: { post: {} } })
        }
      },[])
    
      return (
        <View className="post">
    复制代码

  • 首先我们使用?useDispatch?Hooks 获取到了?dispatch?函数。
  • 接着,在?useEffects?Hooks 里面定义了 dispatch 了 action.type 为 GET_POST 的 action,它是一个异步 Action,并且我们在 Hooks 最后返回了一个函数,其中的内容为将?post?设置为空对象,这里用到的?SET_POST?常量我们将在后面定义它。这个返回函数主要用于?post?组件卸载之后,Redux Store 数据的重置,避免下次打开帖子详情还会渲染之前获取到的帖子数据。
  • 接着,我们使用?useSelector?Hooks 来获取异步请求到的?post?数据,并用于?return?语句中的数据渲染。
  • 最后我们删除了不必要的获取?posts?数据的?useSelector?Hooks,以及删掉了不必要的调试?console.log?语句。
  • GET_POST?的异步 Action,接下来我们要做的就是在对应的?; // ... 和之前的逻辑一样 getPost(postId) { yield call(postApi.getPost,postId) yield put({ type: SET_POST,payload: { post,0);">'getPost ERR: ',0); font-weight: bold;">watchGetPost() { yield take(GET_POST) yield fork(getPost,payload.postId) } } export { watchGetPost } 复制代码

    watchGetPost?,它用来监听?GET_POST的 action,并且当监听到?GET_POST?action 之后,然后激活?getPost?去处理对应的获取单个帖子的逻辑。

  • 这里的?watchGetPost?是一个生成器函数,它内部是一个?GET_POST?action。
  • 在循环内部,我们使用了?GET_POST?action,获取 action 中携带的数据,这里我们拿到了传过来的?payload?数据。
  • 接着我们使用了另外一个?getPost?,并传入了获取到?payload.postId?参数。
  • 我们创建了获取单个帖子的?getPost?,它用来处理获取帖子逻辑。
  • getPost?也是一个生成器函数,在它内部是一个?try/catch?语句,用于处理获取单个帖子请求可能存在的错误情况。
  • 在?postApi. getPost?。
    • 如果获取单个帖子成功,我们使用?SET_POSTS?,代表更新获取单个帖子成功的状态,设置最新获取的帖子到 Redux Store 中。
  • 如果获取单个帖子失败,我们则使用?POST_ERROR?的 action 来更新获取单个帖子失败的信息到 Redux Store
  • postApi.getPost?,我们将在后面来创建这个 API。

    SET_POST?:响应获取帖子列表的 ACTION 常量,我们将在 “第三剑” 中创建它

    watchGetPosts?等加入到?watchGetPost?也加入进去:

    src/sagas/index.js?文件,对其中的内容作出如下的修改:

    import { watchCreatePost,watchGetPosts,watchGetPost } './post'
    
    yield all([
        fork(watchLogin),fork(watchCreatePost),fork(watchGetPosts),fork(watchGetPost),])
    }
    复制代码

    src/constants/post.js?文件,定义我们之前创建的常量文件?GET_POST?:

    复制代码

    postApi.getPost?,它里面封装了用于向后端(这里我们是小程序云)发起和获取单个帖子有关请求的逻辑,让我们马上来实现它吧。

    ,data: { postId,51); font-weight: 700;">const postApi = { getPost,51); font-weight: 700;">default postApi 复制代码

    可以看到上面的代码有如下六处改动:

    • 在上面的代码中,我们定义了?getPost?函数,它是一个?getPost?函数中,我们对当前的环境进行了判断,且只在微信小程序,即?isWeapp?的条件下执行获取单个帖子的操作,对于支付宝小程序和 H5,我们则放在下一节使用 LeanCloud 的 Serverless 来解决。

    • 创建帖子逻辑是一个?Taro.cloud.callFunction?来便捷的向小程序云发起云函数调用请求。

    • 这里我们调用了一个?getPost?云函数,并给它传递了对应要获取的帖子的?postId?我们将在下一节中实现这个云函数。

    • 如果调用成功,我们可以接收返回值,用于从后端返回数据,这里我们返回了?result.post数据,即从小程序云返回的单个帖子。

    • 如果调用失败,则打印错误。

    • 最后我们在已经定义好的?getPost?API 属性然后将其导出,这样在?postApi. getPost?的方式来调用?getPostAPI 处理获取单个帖子的逻辑了。

    创建 getPost 云函数

    getPosts?云函数类似,我们创建?getPost?云函数。

    functions/getPost/package.json?文件,它和之前的类似。

    functions/getPost/index.js?文件,微信小程序开发者工具会默认为我们生成一段样板代码。

    function/getPost?文件夹下同样运行?编写 getPost 云函数

    functions/getPost/index.js?文件,对其中的内容作出对应的修改如下:

    )
          .doc(postId)
          .get()
    
        return {
          post: data,0);">`getPost ERR: 最后我们返回查询到的?post?数据。
    
    

    SET_POST?的 Action 我们在上上 “大” 节中创建帖子时已经定义了,所有在 “这一剑” 中我们无需添加额外的代码,复用之前的逻辑就好。

    小结

    在这篇教程中,我们连续使用了三次 “六脉神剑” 讲完了我们的 Post 逻辑的异步流程,让我们再来复习一下我们开头提到的 “六脉神剑”:

    这是一套讲解模式,也是一套写代码的最佳实践方式之一,希望你能受用。

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读