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

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

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

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

如果你不熟悉 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 逻辑的异步流程,让我们再来复习一下我们开头提到的 “六脉神剑”:

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

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

    相关推荐


    概述 消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。 订阅消息推送位置:服务通知 订阅消息下发条件:用户自主订阅 订阅消息卡片跳转能力:点击查看详情可提爱转至该小程序的页面 消息类型 一次性订阅消息 一次性订阅消息用于解决用户使用小程序后,后续服务
    判断H5页面环境在微信中还是小程序中 用小程序提供的wx.miniProgram.getEnv可以获取环境参数 &lt;script type=&quot;text/javascript&quot; src=&quot;https://res.wx.qq.com/open/js/jweixin-1.
    wx.reLaunch和wx.navigateTo,wx.navigateTo的区别 2019-03-23 11:18:05 wx.navigateTo 用于保留当前页面、跳转到应用内的某个页面,使用 wx.navigateBack可以返回到原页面。对于页面不是特别多的小程序,通常推荐使用 wx.n
    微信小程序如何从数组里取值_微信小程序 传值取值的几种方法总结 小程序里常见的取值有以下几种,一个完整的项目写下来,用到的概率几乎是100%。 列表index下标取值 页面传值 form表单取值 1. 列表index下标取值 实现方式是:data-index=&quot;{{index}}&quot
    H5项目接入微信授权登录,通过 UA 区分微信还是普通浏览器: let&#160;ua&#160;=&#160;navigator.userAgent.toLowerCase(); let&#160;isWeixin&#160;=&#160;ua.indexOf(&#39;micromessenge
    微信小程序获取data-xx=&quot;&quot;属性的值,自定义属性设置和获取(data-) 微信小程序&lt;view class=&quot;details-btn&quot; data-taskId=&quot;111&quot; bindtap=&#39;taskdetails&#39
    小程序报错:TypeError: Cannot read property ‘addEventListener‘ of undefined 解决办法 将调试基础库由2.16.0(或者当前的) -&gt; 2.14.1 解决问题
    H5跳转微信小程序-成功案例(VUE)(踩坑无数) TuoMei 已于 2022-07-29 09:52:22 修改 准备工作 根据官方提供的资料需准备以下几点: 1、已认证的服务号 2、绑定JS接口安全域名 (在微信公众平台设置) 3、IP白名单 (在微信公众平台设置) 4、将小程序和H5公众号进
    微信小程序 页面跳转和数据传递实例详解 这篇文章主要介绍了微信小程序 页面跳转和数据传递实例详解的相关资料,这里附有实例代码帮助到家学习理解,需要的朋友可以参考下 微信小程序 页面跳转和数据传递 1.先导 在Android中,我们Activity和Fragment都有栈的概念在里面,微信小程序页面也
    情景1.拉取公司代码演示: 因为github有墙,这里我们以gitee(码云)为例作为演示 (其实就是国产github,也非常好用~) 步骤一:打开Git界面 先在一个空文件夹右击Git Bash Here,打开git界面 步骤二:输入克隆远程仓库指令 别人复制的链接在这里获取 拿到别人赋值的链接自
    如何开发微信小程序? 作为一名10多年一直从事互联网平台开发的从业者,我来回答下这个问题吧。 微信小程序开发流程总体可以归纳为4个步骤, 老张带您捋一捋整个环节,小白用户可以收藏了。 好了废话不多说,开始! 一、开发前小程序需要准备的资料 我们在开发微信小程序前,需要准备下相关资料。这个资料主要是后
    原生小程序开发优化方案 为了更好的制定优化方案,我们 有必要先了解下小程序的底层架构、以及与普通网页开发的差异 小程序最终渲染载体与当下一些热门的技术 Flutter、React Native等不同,依然是浏览器内核,而不是原生客户端。 而对于传统的网页来说,UI 渲染和 JS 脚本是在同一个线程中
    1,不要下两倍尺寸的图片, 小程序本身自己就会对元素缩小两倍,设计图片的一杯就已经很清晰了。 2,图片压缩,(主要是压缩静态资源,ps 可以压缩,然后有一些在线压缩工具,保持600-800kb 的静态) 3,通用的代码组件化 4,是在工程量太大可以分包,分包现在最大支持20m(一般都不会去分包的)
    文章浏览阅读189次。人工智能研究实验室OpenAI在2022年11月30日发布了自然语言生成模型ChatGPT,上线两个月就已经超过一亿用户,成为了人工智能界当之无愧的超级大网红。ChatGPT凭借着自身强大的拟人化及时应答能力迅速破圈,引起了各行各业的热烈讨论。简单来说ChatGPT就是可以基于用户文本输入自动生成回答的人工智能聊天机器人。那肯定会有人说这不就是Siri嘛,虽然都是交互机器人但是两者的差别可老大了。那么ChatGPT在人机交互时为什么会有这么出色的表现?它到底会不会取代搜索引擎?90%的人真的会因为ChatG
    文章浏览阅读193次。8. 导航和路由管理:掌握小程序的导航方式,如使用wx.navigateTo跳转页面、使用wx.redirect重定向页面等,学会实现页面之间的跳转和传参。1. 小程序的基本概念和架构:了解小程序的定义、特点以及与传统APP的区别,掌握小程序的运行环境、组件和API等基本概念。10. 支付功能:学习小程序的支付方式,如微信支付、支付宝支付等,了解支付流程和注意事项,学会实现小程序的支付功能。9. 用户授权和登录:了解小程序的用户授权机制,如获取用户信息、调用微信API等,学会实现用户的登录和注册功能。_微信小程序开发知识点总结
    文章浏览阅读4.8k次,点赞7次,收藏18次。一、准备工作1. 安装微信开发者工具,并登录微信小程序账号;2. 准备斗地主游戏的图片资源;3. 准备斗地主游戏的音效资源;二、创建小程序1. 打开微信开发者工具,点击“新建小程序”,输入小程序名称,选择小程序的项目目录,点击“创建”;2. 在小程序的项目目录中,新建文件夹“images”,将准备好的斗地主游戏的图片资源放入“images”文件夹中;3. 在小程序的项目目录中,新建文件夹“sounds”,将准备好的斗地主游戏的音效资源放入“sounds”文件夹中;三、编写代码1. 在小程_扑克牌微信小程序代码
    文章浏览阅读3.9k次,点赞3次,收藏7次。一、准备工作:1. 安装微信开发者工具,创建小程序项目;2. 准备游戏角色图片;3. 准备游戏背景音乐;二、实现步骤:1. 创建游戏页面,添加游戏角色图片,添加游戏背景音乐;2. 创建游戏角色类,定义游戏角色属性,如角色名称、角色图片、角色能力等;3. 创建游戏类,定义游戏属性,如游戏人数、游戏角色、游戏规则等;4. 创建游戏控制类,定义游戏流程,如游戏开始、游戏结束、游戏角色分配等;5. 创建游戏界面,实现游戏流程,如游戏开始、游戏结束、游戏角色分配等;6. 创建游戏结果页面,显示游戏_微信小程序游戏代码
    文章浏览阅读1.7k次。1. 创建小程序项目:使用微信开发者工具创建一个小程序项目,并在项目中添加一个页面,用于模拟聊天。 2. 定义数据结构:定义一个数据结构,用于存储聊天记录,包括发送者、接收者、消息内容等信息。 3. 实现聊天功能:实现聊天功能,包括发送消息、接收消息、显示消息等功能。 4. 实现界面:使用微信小程序的界面框架,实现聊天界面,包括聊天记录列表、输入框等。代码示例:// 定义数据结构var chatData = { sender: '', receiver: '', message: '' };_制作聊天对话小程序代码
    文章浏览阅读2.1k次。1、创建小程序项目:使用微信开发者工具,新建一个小程序项目,输入项目名称,选择项目目录,点击“创建”按钮,即可创建小程序项目。2、添加页面:在小程序项目中,可以添加多个页面,每个页面都有自己的页面文件,比如首页、分类页、购物车页、我的页面等。3、添加组件:在小程序项目中,可以添加多个组件,比如商品列表组件、购物车组件、订单组件等,用于在页面中显示商品信息、购物车信息、订单信息等。4、添加接口:在小程序项目中,可以添加多个接口,用于获取商品信息、购物车信息、订单信息等,以便在页面中显示。5、_微信开发者工具做一个我的商城