国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Taro 小程序開發(fā)大型實(shí)戰(zhàn)(七):嘗鮮微信小程序云(下篇)

這篇具有很好參考價(jià)值的文章主要介紹了Taro 小程序開發(fā)大型實(shí)戰(zhàn)(七):嘗鮮微信小程序云(下篇)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

歡迎繼續(xù)閱讀《Taro 小程序開發(fā)大型實(shí)戰(zhàn)》系列,前情回顧:

  • 熟悉的 React,熟悉的 Hooks:我們用 React 和 Hooks 實(shí)現(xiàn)了一個(gè)非常簡(jiǎn)單的添加帖子的原型
  • 多頁面跳轉(zhuǎn)和 Taro UI 組件庫(kù):我們用 Taro 自帶的路由功能實(shí)現(xiàn)了多頁面跳轉(zhuǎn),并用 Taro UI 組件庫(kù)升級(jí)了應(yīng)用界面
  • 實(shí)現(xiàn)微信和支付寶多端登錄:實(shí)現(xiàn)了微信、支付寶以及普通登錄和退出登錄
  • 使用 Hooks 版的 Redux 實(shí)現(xiàn)大型應(yīng)用狀態(tài)管理(上篇):使用 Hooks 版的 Redux 實(shí)現(xiàn)了 user 邏輯的狀態(tài)管理重構(gòu)
  • 使用 Hooks 版的 Redux 實(shí)現(xiàn)大型應(yīng)用狀態(tài)管理(下篇):使用 Hooks 版的 Redux 實(shí)現(xiàn)了 post 邏輯的狀態(tài)管理重構(gòu)
  • Taro 小程序開發(fā)大型實(shí)戰(zhàn)(六):嘗鮮微信小程序云(上篇):user 邏輯接入微信小程序云

在上一篇文章中,我們將我們兩大邏輯之一 User 部分接入了 Redux 異步處理流程,接著接入了微信小程序云,使得 User 邏輯可以在云端永久保存,好不自在:),兩兄弟一個(gè)得了好處,另外一個(gè)不能干瞪眼對(duì)吧?在這一篇教程中,我們想辦法把 User 另外一個(gè)兄弟 Post 撈上來,也把 Redux 異步流程和微信小程序給它整上,這樣就齊活了。

我們首先來看一看最終的完成效果:

如果你不熟悉 Redux,推薦閱讀我們的《Redux 包教包會(huì)》系列教程:

  • Redux 包教包會(huì)(一):解救 React 狀態(tài)危機(jī)
  • Redux 包教包會(huì)(二):趁熱打鐵,完全重構(gòu)
  • Redux 包教包會(huì)(三):各司其職,重拾初心

如果你希望直接從這一步開始,請(qǐng)運(yùn)行以下命令:

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

本文所涉及的源代碼都放在了Github 上,如果您覺得我們寫得還不錯(cuò),希望您能給?這篇文章點(diǎn)贊+Github倉(cāng)庫(kù)加星?哦~

此教程屬于 React 前端工程師學(xué)習(xí)路線的一部分,歡迎來 Star 一波,鼓勵(lì)我們繼續(xù)創(chuàng)作出更好的教程,持續(xù)更新中~

“六脈神劍” 搞定 createPost 異步邏輯

不知道看到這里的讀者有沒有發(fā)現(xiàn)上篇文章其實(shí)打造了一套講解模式,即按照如下的 “六步流程” 來講解,我們也稱為 “六脈神劍” 講解法:

  • 將組件中的同步邏輯重構(gòu)到異步邏輯
  • 聲明和補(bǔ)充對(duì)應(yīng)需要的異步 sagas 文件
  • 定義 sagas 需要的常量文件
  • 定義 sagas 涉及到的前端 API 文件
  • 創(chuàng)建對(duì)于的微信小程序云函數(shù),并編寫對(duì)應(yīng)的 Node.js 處理邏輯
  • 定義對(duì)應(yīng)的 reducers 文件
  • 如此往復(fù)

可以看到我們上面的講解順序?qū)嶋H上是按照前端數(shù)據(jù)流的流動(dòng)來進(jìn)行的,我們對(duì)標(biāo)上面的講解邏輯來看一下前端數(shù)據(jù)流是如何流動(dòng)的:

  • 從組件中通過對(duì)應(yīng)的常量發(fā)起異步請(qǐng)求
  • sagas 監(jiān)聽到對(duì)應(yīng)的異步請(qǐng)求,開始處理流程
  • sagas 調(diào)用對(duì)應(yīng)的前端 API 文件向微信小程序云發(fā)起請(qǐng)求
  • 微信小程序云函數(shù)處理對(duì)應(yīng)的 API 請(qǐng)求,返回?cái)?shù)據(jù)
  • sagas 中獲取到對(duì)應(yīng)的數(shù)據(jù),dispatch action 到對(duì)應(yīng)的 reducers 處理邏輯
  • reducers 接收數(shù)據(jù),開始更新本地 Redux Store 中的 state
  • 組件中重新渲染

好的,了解了講解邏輯和對(duì)應(yīng)前端數(shù)據(jù)流動(dòng)邏輯之后,我們馬上來實(shí)踐這套邏輯,把 User 邏輯的好兄弟 Post 邏輯搞定。

第一劍:PostForm 組件中發(fā)起異步請(qǐng)求

首先從創(chuàng)建帖子邏輯動(dòng)刀子,我們將創(chuàng)建帖子接入異步邏輯并接通小程序云,讓文章上云。打開 src/components/PostForm/index.jsx ,對(duì)其中的內(nèi)容作出對(duì)應(yīng)的修改如下:

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

import './index.scss'
import { CREATE_POST } from '../../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 (

可以看到,上面的內(nèi)容做了如下四處修改:

  • 首先我們現(xiàn)在是接收用戶的文章輸入數(shù)據(jù)然后向小程序云發(fā)起創(chuàng)建文章的請(qǐng)求,所以我們將之前的 dispatch SET_POSTS Action 改為 CREATE_POST Action,并且將之前的 action payload 簡(jiǎn)化為 postDatauserId,因?yàn)槲覀兛梢酝ㄟ^小程序云數(shù)據(jù)庫(kù)查詢 userId 得到創(chuàng)建文章的用戶信息,所以不需要再攜帶用戶的數(shù)據(jù)。
  • 接著,因?yàn)槲覀儾辉傩枰脩舻?avatarnickName 數(shù)據(jù),所以我們刪掉了對(duì)應(yīng)的 useSelector 語句。
  • 接著,因?yàn)檎?qǐng)求是異步的,所以需要等待請(qǐng)求完成之后再設(shè)置對(duì)應(yīng)的發(fā)表文章的狀態(tài)以及發(fā)表文章彈出層狀態(tài),所以我們刪掉了對(duì)應(yīng)的 dispatch SET_POST_FORM_IS_OPENED Action 邏輯以及 Taro.atMessage 邏輯。
  • 最后我們刪掉不需要的常量 SET_POSTSSET_POST_FORM_IS_OPENED,然后導(dǎo)入異步創(chuàng)建文章的常量 CREATE_POST。
增加 Action 常量

我們?cè)谏弦徊街惺褂玫搅?CREATE_POST 常量,打開 src/constants/post.js,在其中增加 CREATE_POST 常量:

export const CREATE_POST = 'CREATE_POST'

到這里,我們的 “六步流程” 講解法就走完了第一步,即從組件中發(fā)起對(duì)應(yīng)的異步請(qǐng)求,這里我們是發(fā)出的 action.typeCREATE_POST 的異步請(qǐng)求。

第二劍: 聲明和補(bǔ)充對(duì)應(yīng)需要的異步 sagas 文件

在 “第一劍” 中,我們從組件中 dispatch 了 action.type 為 CREATE_POST 的異步 Action,接下來我們要做的就是在對(duì)應(yīng)的 sagas 文件中補(bǔ)齊響應(yīng)這個(gè)異步 action 的 sagas。

src/sagas/ 文件夾下面創(chuàng)建 post.js 文件,并在其中編寫如下創(chuàng)建文章的邏輯:

import Taro from '@tarojs/taro'
import { call, put, take, fork } from 'redux-saga/effects'

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

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

    // 其實(shí)以下三步可以合成一步,但是這里為了講解清晰,將它們拆分成獨(dú)立的單元

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

    // 關(guān)閉發(fā)帖框彈出層
    yield put({ type: SET_POST_FORM_IS_OPENED, payload: { isOpened: false } })

    // 更新 Redux store 數(shù)據(jù)
    yield put({
      type: SET_POSTS,
      payload: {
        posts: [post],
      },
    })

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

    // 發(fā)帖失敗,發(fā)起失敗的 action
    yield put({ type: POST_ERROR })

    // 提示發(fā)帖失敗
    Taro.atMessage({
      message: '發(fā)表文章失敗',
      type: 'error',
    })
  }
}

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

    console.log('payload', payload)

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

export { watchCreatePost }

可以看到,上面的改動(dòng)主要是創(chuàng)建 watcherSagahandlerSaga 。

創(chuàng)建 watcherSaga

  • 我們創(chuàng)建了登錄的 watcherSagawatchCreatePost ,它用來監(jiān)聽 action.typeCREATE_POST 的 action,并且當(dāng)監(jiān)聽到 CREATE_POST action 之后,從這個(gè) action 中獲取必要的 postData userId 數(shù)據(jù),然后激活 handlerSagacreatePost 去處理對(duì)應(yīng)的創(chuàng)建帖子的邏輯。
  • 這里的 watcherSagawatchCreatePost 是一個(gè)生成器函數(shù),它內(nèi)部是一個(gè) while 無限循環(huán),表示在內(nèi)部持續(xù)監(jiān)聽 CREATE_POST action。
  • 在循環(huán)內(nèi)部,我們使用了 redux-saga 提供的 effects helper 函數(shù):take,它用于監(jiān)聽 CREATE_POST action,獲取 action 中攜帶的數(shù)據(jù)。
  • 接著我們使用了另外一個(gè) effects helper 函數(shù):fork,它表示非阻塞的執(zhí)行 handlerSagacreatePost,并將 payload.postDatapayload.userId 作為參數(shù)傳給 createPost。

創(chuàng)建 handlerSaga

  • 我們創(chuàng)建了創(chuàng)建帖子的 handlerSagacreatePost,它用來處理創(chuàng)建邏輯。
  • createPost 也是一個(gè)生成器函數(shù),在它內(nèi)部是一個(gè) try/catch 語句,用于處理創(chuàng)建帖子請(qǐng)求可能存在的錯(cuò)誤情況。
  • try 語句中,首先是使用了 redux-saga 提供給我們的 effects helper 函數(shù):call 來調(diào)用登錄的 API:postApi.createPost,并把 postDatauserId 作為參數(shù)傳給這個(gè) API。

    • 如果創(chuàng)建帖子成功,我們使用 redux-saga 提供的 effects helpers 函數(shù):put,put 類似之前在 view 中的 dispatch 操作,,來 dispatch 了三個(gè) action:POST_SUCCESS ,SET_POST_FORM_IS_OPENED SET_POSTS ,代表更新創(chuàng)建帖子成功的狀態(tài),關(guān)閉發(fā)帖框,設(shè)置最新創(chuàng)建的帖子信息到 Redux Store 中。
    • 最后我們使用了 Taro UI 提供給我們的消息框,來顯示一個(gè) success 消息。
  • 如果發(fā)帖失敗,我們則使用 put 發(fā)起一個(gè) POST_ERROR 的 action 來更新創(chuàng)建帖子失敗的信息到 Redux Store,接著使用了 Taro UI 提供給我們的消息框,來顯示一個(gè) error 消息。
一些額外的工作

為了創(chuàng)建 watcherSagahandlerSaga,我們還導(dǎo)入了 postApi,我們將在后面來創(chuàng)建這個(gè) API。

除此之外我們還導(dǎo)入了需要使用的 action 常量:

  • POST_SUCCESS :設(shè)置處理帖子邏輯成功信息
  • POST_ERROR :設(shè)置處理帖子邏輯失敗信息
  • SET_POSTS:將新帖子添加到 Redux Store
  • CREATE_POST: 相應(yīng)創(chuàng)建帖子的常量
  • SET_POST_FORM_IS_OPENED:更新發(fā)帖框的開閉邏輯

這里的 POST_SUCCESSPOST_ERROR 我們還沒有創(chuàng)建,我們將馬上在 “下一劍” 中創(chuàng)建它。

以及一些 redux-saga/effects 相關(guān)的 helper 函數(shù),我們已經(jīng)在之前的內(nèi)容中詳細(xì)講過了,這里就不再贅述了。

加入 saga 中心調(diào)度文件

我們像之前將 watchLogin 等加入到 sagas 中心調(diào)度文件一樣,將我們創(chuàng)建好的 watchCreatePost 也加入進(jìn)去:

// ...之前的邏輯
import { watchCreatePost } from './post'
export default function* rootSaga() {
  yield all([
   // ... 之前的邏輯
    fork(watchCreatePost)
  ])
}

第三劍:定義 sagas 需要的常量文件

打開 src/constants/post.js 文件,定義我們之前創(chuàng)建的常量文件如下:

export const POST_SUCCESS = 'POST_SUCCESS'
export const POST_ERROR = 'POST_ERROR'

第四劍:定義 sagas 涉及到的前端 API 文件

在之前的 post saga 文件里面,我們使用到了 postApi,它里面封裝了用于向后端(這里我們是小程序云)發(fā)起和帖子有關(guān)請(qǐng)求的邏輯,讓我們馬上來實(shí)現(xiàn)它吧。

src/api/ 文件夾下添加 post.js 文件,并在文件中編寫內(nèi)容如下:

import Taro from '@tarojs/taro'

async function createPost(postData, userId) {
  const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
  const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY

  console.log('postData', postData, userId)

  // 針對(duì)微信小程序使用小程序云函數(shù),其他使用小程序 RESTful API
  try {
    if (isWeapp) {
      const { result } = await Taro.cloud.callFunction({
        name: 'createPost',
        data: {
          postData,
          userId,
        },
      })

      return result.post
    }
  } catch (err) {
    console.error('createPost ERR: ', err)
  }
}

const postApi = {
  createPost,
}
export default postApi;

在上面的代碼中,我們定義了 createPost 函數(shù),它是一個(gè) async 函數(shù),用來處理異步邏輯,在 createPost 函數(shù)中,我們對(duì)當(dāng)前的環(huán)境進(jìn)行了判斷,且只在微信小程序,即 isWeapp 的條件下執(zhí)行創(chuàng)建帖子的操作,對(duì)于支付寶小程序和 H5,我們則放在下一節(jié)使用 LeanCloud 的 Serverless 來解決。

創(chuàng)建帖子邏輯是一個(gè) try/catch 語句,用于捕捉可能存在的請(qǐng)求錯(cuò)誤,在 try 代碼塊中,我們使用了 Taro 為我們提供的微信小程序云的云函數(shù) API Taro.cloud.callFunction 來便捷的向小程序云發(fā)起云函數(shù)調(diào)用請(qǐng)求。

這里我們調(diào)用了一個(gè) createPost 云函數(shù),并將 postData userId 作為參數(shù)傳給云函數(shù),用于在云函數(shù)中使用用戶 Id 和帖子數(shù)據(jù)來創(chuàng)建一個(gè)屬于此用戶的帖子并保存到數(shù)據(jù)庫(kù),我們將在下一節(jié)中實(shí)現(xiàn)這個(gè)云函數(shù)。

如果調(diào)用成功,我們可以接收返回值,用于從后端返回?cái)?shù)據(jù),這里我們返回了 result.post 數(shù)據(jù)。

如果調(diào)用失敗,則打印錯(cuò)誤。

最后我們定義了一個(gè) postApi 對(duì)象,用于存放所有和用戶邏輯有個(gè)的函數(shù),并添加 createPost API 屬性然后將其導(dǎo)出,這樣在 post saga 函數(shù)里面就可以導(dǎo)入 postApi 然后通過 postApi.createPost 的方式來調(diào)用 createPost API 處理創(chuàng)建帖子的邏輯了。

在 API 默認(rèn)文件統(tǒng)一導(dǎo)出

src/api/index.js 文件中導(dǎo)入上面創(chuàng)建的 postApi 并進(jìn)行統(tǒng)一導(dǎo)出如下:

import postApi from './post'
export { postApi }

第五劍:創(chuàng)建對(duì)應(yīng)的微信小程序云函數(shù)

創(chuàng)建 createPost 云函數(shù)

按照和之前創(chuàng)建 login 云函數(shù)類似,我們創(chuàng)建 createPost 云函數(shù)。

創(chuàng)建成功之后,我們可以得到兩個(gè)文件,一個(gè)是 functions/createPost/package.json 文件,它和之前的類似。

{
  "name": "createPost",
  "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"
  }
}

第二個(gè)文件就是我們需要編寫創(chuàng)建帖子邏輯的 functions/createPost/index.js 文件,微信小程序開發(fā)者工具會(huì)默認(rèn)為我們生成一段樣板代碼。

我們?cè)?function/createPost 文件夾下同樣運(yùn)行 npm install 安裝對(duì)應(yīng)的云函數(shù)依賴,這樣我們才能運(yùn)行它。

編寫 createPost 云函數(shù)

打開 functions/createPost/index.js 文件,對(duì)其中的內(nèi)容作出對(duì)應(yīng)的修改如下:

// 云函數(shù)入口文件
const cloud = require('wx-server-sdk')

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

const db = cloud.database()

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

  console.log('event', event)

  try {
    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 = await db
      .collection('post')
      .doc(_id)
      .get()

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

可以看到上面的代碼改動(dòng)主要有以下七處:

  • 首先我們給 cloud.init() 傳入了環(huán)境參數(shù),我們使用了內(nèi)置的 cloud.DYNAMIC_CURRENT_ENV,表示自動(dòng)設(shè)置為當(dāng)前的云環(huán)境,即在右鍵點(diǎn)擊小程序開發(fā)者工具里 functions 文件夾時(shí)選擇的環(huán)境。
  • 接著,我們通過 cloud.database() 生成了數(shù)據(jù)實(shí)例 db,用于之后在函數(shù)體中便捷的操作云數(shù)據(jù)庫(kù)。
  • 接著就是 main 函數(shù)體,我們首先從 event 對(duì)象中取到了在小程序的調(diào)用 Taro.cloud.callFunction 傳過來的 postData userId 數(shù)據(jù)。
  • 然后,跟著取數(shù)據(jù)的是一個(gè) try/catch 語句塊,用于捕獲錯(cuò)誤,在 try 語句塊中,我們使用 db 的查詢操作:db.collection('user').doc(userId).get(),表示查詢 iduserIduser 表數(shù)據(jù),它查出來應(yīng)該是個(gè)唯一值,如果不存在滿足 where 條件的,那么是一個(gè) null 值,如果存在滿足 條件的,那么返回一個(gè) user 對(duì)象。
  • 接著,我們使用的 db.collection('post').add() 添加一個(gè) post 數(shù)據(jù),然后在 add 方法中傳入 data 字段,這里我們不僅傳入了 postData ,還將 user 也一同傳入了,原因我們將在之后來講解。除此之外,這里我們額外使用了 db.serverDate() 用于記錄創(chuàng)建此帖子的時(shí)間和更新此帖子的時(shí)間,方便之后做條件查詢。
  • 接著,因?yàn)橄驍?shù)據(jù)庫(kù)添加一個(gè)記錄之后只會(huì)返回此記錄的 _id,所以我們需要一個(gè)額外的操作 db.collection('post').doc() 來獲取此條記錄,這個(gè) doc 用于獲取指定的記錄引用,返回的是這條數(shù)據(jù),而不是一個(gè)數(shù)組。
  • 最后我們返回新創(chuàng)建的 post。

提示

我們?cè)谏厦鎰?chuàng)建 post 的時(shí)候,將 user 對(duì)象也添加到了 post 數(shù)據(jù)中,這里是因?yàn)樾〕绦蛟茢?shù)據(jù)庫(kù)是 JSON 數(shù)據(jù)庫(kù),所以沒有關(guān)系數(shù)據(jù)庫(kù)的外鍵概念,導(dǎo)致建關(guān)系困難,所以為了之后查詢 post 的時(shí)候方便展示 user 數(shù)據(jù),我們才這樣保存的. 當(dāng)然更加科學(xué)的做法是在 post 里面保存 userId,這樣能減少數(shù)據(jù)冗余,但是因?yàn)樽鼋虒W(xué)用,所以這些我們偷了一點(diǎn)懶。

所以我們這里強(qiáng)烈建議,在正規(guī)的環(huán)境下,關(guān)系型數(shù)據(jù)庫(kù)應(yīng)該建外鍵,JSON 數(shù)據(jù)庫(kù)也至少應(yīng)該保存 userId。:

第六劍: 定義對(duì)應(yīng)的 reducers 文件

我們?cè)谇懊嫣幚韯?chuàng)建帖子時(shí),在組件內(nèi)部 dispatchCREATE_POST action,在處理異步 action 的 saga 函數(shù)中,使用 put 發(fā)起了一系列更新 store 中帖子狀態(tài)的 action,現(xiàn)在我們馬上來實(shí)現(xiàn)響應(yīng)這些 action 的 reducers,打開 src/reducers/post.js,對(duì)其中的代碼做出對(duì)應(yīng)的修改如下:

import {
  SET_POST,
  SET_POSTS,
  SET_POST_FORM_IS_OPENED,
  POST_ERROR,
  CREATE_POST,
  POST_NORMAL,
  POST_SUCCESS,
} from '../constants/'

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

const INITIAL_STATE = {
  posts: [],
  post: {},
  isOpened: false,
  isPost: false,
  postStatus: POST_NORMAL,
}

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

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

    case SET_POST_FORM_IS_OPENED: {...      return { ...state, isOpened }
    }

    case CREATE_POST: {
      return { ...state, postStatus: CREATE_POST, isPost: true }
    }

    case POST_SUCCESS: {
      return { ...state, postStatus: POST_SUCCESS, isPost: false }
    }

    case POST_ERROR: {
      return { ...state, postStatus: POST_ERROR, isPost: false }
    }

    default:
      return state
  }

看一看到上面的代碼主要有三處改動(dòng):

  • 首先我們導(dǎo)入了必要的 action 常量
  • 接著我們給 INITIAL_STATE 增加了幾個(gè)字段:

    • posts :保存帖子列表的數(shù)據(jù),創(chuàng)建新的帖子也會(huì)保存在這里面。
    • post :保存單個(gè)帖子,我們將在之后講解用于在獲取帖子詳情時(shí)保存數(shù)據(jù)用。
  • isPost :用于標(biāo)志帖子邏輯過程中是否在執(zhí)行創(chuàng)帖邏輯,true 表示正在執(zhí)行創(chuàng)帖中,false 表示登錄邏輯執(zhí)行完畢

    • postStatus :用于標(biāo)志創(chuàng)帖過程中的狀態(tài):開始創(chuàng)帖(CREATE_POST )、創(chuàng)帖成功(POST_SUCCESS )、登錄失敗(POST_ERROR )
  • 最后就是 switch 語句中響應(yīng) action,更新相應(yīng)的狀態(tài)。

“六脈神劍” 搞定 getPosts 異步邏輯

在上一 “大” 節(jié)中,我們使用了圖雀社區(qū)不傳之術(shù):“六脈神劍” 搞定了 createPost 的異步邏輯,現(xiàn)在我們馬上趁熱打鐵來鞏固我們的武功,搞定 getPosts 異步邏輯,它對(duì)應(yīng)著我們小程序底部?jī)蓚€(gè) tab 欄的第一個(gè),也就是我們打開小程序的首屏渲染邏輯,也就是一個(gè)帖子列表。

第一劍:index 組件中發(fā)起異步請(qǐng)求

打開 src/pages/index/index.jsx 文件,對(duì)其中的內(nèi)容作出對(duì)應(yīng)的修改如下:


import { PostCard, PostForm } from '../../components'
import './index.scss'
import {
  SET_POST_FORM_IS_OPENED,
  SET_LOGIN_INFO,
  GET_POSTS,
} from '../../constants'

export default function 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()
    }

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

        const { nickName, avatar, _id } = data

        // 更新 Redux Store 數(shù)據(jù)
        dispatch({
          type: SET_LOGIN_INFO,
          payload: { nickName, avatar, userId: _id },
        })
      } catch (err) {
        console.log('getStorage ERR: ', err)
      }
    }

    if (!isLogged) {
      getStorage()
    }

    async function getPosts() {
      try {
        // 更新 Redux Store 數(shù)據(jù)
        dispatch({
          type: GET_POSTS,
        })
      } catch (err) {
        console.log('getPosts ERR: ', err)
      }
    }

    if (!posts.length) {
      getPosts()
    }
  }, [])

  function 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}

可以看到,上面的內(nèi)容做了如下四處修改:

  • 首先我們對(duì)當(dāng)前的開發(fā)環(huán)境做了判斷,如果是微信小程序環(huán)境,我們就使用 Taro.cloud.init() 進(jìn)行小程序環(huán)境的初始化。
  • 接著,我們?cè)?useEffects Hooks 里面定義了 getPosts 函數(shù),它是一個(gè)異步函數(shù),用于 dispatch GET_POSTS 的異步請(qǐng)求,并且我們進(jìn)行了判斷,當(dāng)此時(shí) Redux Store 內(nèi)部沒有文章時(shí),才進(jìn)行數(shù)據(jù)的獲取。
  • 接著,我們改進(jìn)了 getStorage 獲取緩存的函數(shù),將其移動(dòng)到 useEffects Hooks 里面,并額外增加了 _id 屬性,它被賦值給 userId 一起設(shè)置 Redux Store 中關(guān)于用戶的屬性,這樣做的目的主要是為了之后發(fā)帖標(biāo)志用戶,或者獲取用戶的個(gè)人信息用。并且,加了一層 if 判斷,只有當(dāng)沒有登錄時(shí),即 isLogged 為 false 的時(shí)候,才進(jìn)行獲取緩存操作。
  • 最后我們導(dǎo)入了必要的 GET_POSTS 常量,并且將 return 語句里的 PostCard 接收的 keypostId 屬性變成了真實(shí)的帖子 _id。這樣我們?cè)谔釉斍闀r(shí)可以直接拿 postId 向小程序云發(fā)起異步請(qǐng)求。

注意

在上一篇教程中,有同學(xué)提到?jīng)]有使用 Taro.cloud.init() 初始化的問題,是因?yàn)榉殖闪藘善恼拢谶@篇文章才初始化。要使用小程序云,初始化環(huán)境是必要的。

第二劍:聲明和補(bǔ)充對(duì)應(yīng)需要的異步 sagas 文件

在 “第一劍” 中,我們從組件中 dispatch 了 action.type 為 GET_POSTS 的異步 Action,接下來我們要做的就是在對(duì)應(yīng)的 sagas 文件中補(bǔ)齊響應(yīng)這個(gè)異步 action 的 sagas。

打開 src/sagas/post.js 文件,在其中定義 getPosts sagas 邏輯如下:

import {
  GET_POSTS,
} from '../constants'

function* getPosts() {
  try {
    const posts = yield call(postApi.getPosts)

    // 其實(shí)以下三步可以合成一步,但是這里為了講解清晰,將它們拆分成獨(dú)立的單元

    // 發(fā)起獲取帖子成功的 action
    yield put({ type: POST_SUCCESS })

    // 更新 Redux store 數(shù)據(jù)
    yield put({
      type: SET_POSTS,
      payload: {
        posts,
      },
    })
  } catch (err) {
    console.log('getPosts ERR: ', err)

    // 獲取帖子失敗,發(fā)起失敗的 action
    yield put({ type: POST_ERROR })
  }
}
function* watchGetPosts() {
  while (true) {
    yield take(GET_POSTS)

    yield fork(getPosts)
  }
}

export { watchGetPosts }

可以看到,上面的改動(dòng)主要是創(chuàng)建 watcherSagahandlerSaga

創(chuàng)建 watcherSaga

  • 我們創(chuàng)建了登錄的 watcherSagawatchGetPosts ,它用來監(jiān)聽 action.typeGET_POSTS 的 action,并且當(dāng)監(jiān)聽到 GET_POSTS action 之后,然后激活 handlerSagagetPosts 去處理對(duì)應(yīng)的獲取帖子列表的邏輯。
  • 這里的 watcherSagawatchGetPosts 是一個(gè)生成器函數(shù),它內(nèi)部是一個(gè) while 無限循環(huán),表示在內(nèi)部持續(xù)監(jiān)聽 GET_POSTS action。
  • 在循環(huán)內(nèi)部,我們使用了 redux-saga 提供的 effects helper 函數(shù):take,它用于監(jiān)聽 GET_POSTS action,獲取 action 中攜帶的數(shù)據(jù)。
  • 接著我們使用了另外一個(gè) effects helper 函數(shù):fork,它表示非阻塞的執(zhí)行 handlerSagagetPosts ,因?yàn)檫@里獲取帖子列表不需要傳數(shù)據(jù),所以這里沒有額外的數(shù)據(jù)傳遞邏輯。

創(chuàng)建 handlerSaga

  • 我們創(chuàng)建了創(chuàng)建帖子的 handlerSagagetPosts ,它用來處理創(chuàng)建邏輯。
  • getPosts 也是一個(gè)生成器函數(shù),在它內(nèi)部是一個(gè) try/catch 語句,用于處理獲取帖子列表請(qǐng)求可能存在的錯(cuò)誤情況。
  • try 語句中,首先是使用了 redux-saga 提供給我們的 effects helper 函數(shù):call 來調(diào)用登錄的 API:postApi. getPosts 。

    • 如果獲取帖子列表成功,我們使用 redux-saga 提供的 effects helpers 函數(shù):put,put 類似之前在 view 中的 dispatch 操作,,來 dispatch 了兩個(gè) action:POST_SUCCESS ,SET_POSTS ,代表更新獲取帖子列表成功的狀態(tài),設(shè)置最新獲取的帖子列表到 Redux Store 中。
  • 如果獲取帖子列表失敗,我們則使用 put 發(fā)起一個(gè) POST_ERROR 的 action 來更新獲取帖子列表失敗的信息到 Redux Store
一些額外的工作

為了創(chuàng)建 watcherSagahandlerSaga,我們還導(dǎo)入了 postApi. getPosts ,我們將在后面來創(chuàng)建這個(gè) API。

除此之外我們還導(dǎo)入了需要使用的 action 常量:

  • GET_POSTS :響應(yīng)獲取帖子列表的 ACTION 常量,我們將在 “第三劍” 中創(chuàng)建它。
加入 saga 中心調(diào)度文件

我們像之前將 watchCreatePost 等加入到 sagas 中心調(diào)度文件一樣,將我們創(chuàng)建好的 watchGetPosts 也加入進(jìn)去:

// ...之前的邏輯
import { watchGetPosts } from './post'
export default function* rootSaga() {
  yield all([
   // ... 之前的邏輯
    fork(watchGetPosts)
  ])
}

第三劍:定義 sagas 需要的常量文件

打開 src/constants/post.js 文件,定義我們之前創(chuàng)建的常量文件如下:

export const GET_POSTS = 'GET_POSTS'

第四劍:定義 sagas 涉及到的前端 API 文件

在之前的 post saga 文件里面,我們使用到了 postApi.getPosts,它里面封裝了用于向后端(這里我們是小程序云)發(fā)起和獲取帖子列表有關(guān)請(qǐng)求的邏輯,讓我們馬上來實(shí)現(xiàn)它吧。

打開 src/api/post.js 文件,并在其中編寫內(nèi)容如下:

// ... 其余邏輯一樣
async function getPosts() {
  const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
  const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY

  // 針對(duì)微信小程序使用小程序云函數(shù),其他使用小程序 RESTful API
  try {
    if (isWeapp) {
      const { result } = await Taro.cloud.callFunction({
        name: 'getPosts',
      })

      return result.posts
    }
  } catch (err) {
    console.error('getPosts ERR: ', err)
  }
}

const postApi = {
  // ... 之前的 API
  getPosts,
}

// ... 其余邏輯一樣

在上面的代碼中,我們定義了 getPosts 函數(shù),它是一個(gè) async 函數(shù),用來處理異步邏輯,在 getPosts 函數(shù)中,我們對(duì)當(dāng)前的環(huán)境進(jìn)行了判斷,且只在微信小程序,即 isWeapp 的條件下執(zhí)行獲取帖子列表的操作,對(duì)于支付寶小程序和 H5,我們則放在下一節(jié)使用 LeanCloud 的 Serverless 來解決。

創(chuàng)建帖子邏輯是一個(gè) try/catch 語句,用于捕捉可能存在的請(qǐng)求錯(cuò)誤,在 try 代碼塊中,我們使用了 Taro 為我們提供的微信小程序云的云函數(shù) API Taro.cloud.callFunction 來便捷的向小程序云發(fā)起云函數(shù)調(diào)用請(qǐng)求。

這里我們調(diào)用了一個(gè) getPosts 云函數(shù),我們將在下一節(jié)中實(shí)現(xiàn)這個(gè)云函數(shù)。

如果調(diào)用成功,我們可以接收返回值,用于從后端返回?cái)?shù)據(jù),這里我們返回了 result.posts 數(shù)據(jù),即從小程序云返回的帖子列表。

如果調(diào)用失敗,則打印錯(cuò)誤。

最后我們?cè)谝呀?jīng)定義好的 postApi 對(duì)象里,添加 getPosts API 屬性然后將其導(dǎo)出,這樣在 post saga 函數(shù)里面就可以導(dǎo)入 postApi 然后通過 postApi. getPosts 的方式來調(diào)用 getPosts API 處理獲取帖子列表的邏輯了。

第五劍:創(chuàng)建對(duì)應(yīng)的微信小程序云函數(shù)

創(chuàng)建 getPosts 云函數(shù)

按照和之前創(chuàng)建 createPost 云函數(shù)類似,我們創(chuàng)建 getPosts 云函數(shù)。

創(chuàng)建成功之后,我們可以得到兩個(gè)文件,一個(gè)是 functions/getPosts/package.json 文件,它和之前的類似。

{
  "name": "getPosts",
  "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"
  }
}

第二個(gè)文件就是我們需要編寫創(chuàng)建帖子邏輯的 functions/getPosts/index.js 文件,微信小程序開發(fā)者工具會(huì)默認(rèn)為我們生成一段樣板代碼。

我們?cè)?function/getPosts 文件夾下同樣運(yùn)行 npm install 安裝對(duì)應(yīng)的云函數(shù)依賴,這樣我們才能運(yùn)行它。

編寫 getPosts 云函數(shù)

打開 functions/getPosts/index.js 文件,對(duì)其中的內(nèi)容作出對(duì)應(yīng)的修改如下:

// 云函數(shù)入口文件
const cloud = require('wx-server-sdk')

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

const db = cloud.database()
const _ = db.command

// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
  try {
    const { data } = await db.collection('post').get()

    return {
      posts: data,
    }
  } catch (e) {
    console.error(`getPosts ERR: ${e}`)
  }
}

可以看到上面的代碼改動(dòng)主要有以下處:

  • 首先我們給 cloud.init() 傳入了環(huán)境參數(shù),我們使用了內(nèi)置的 cloud.DYNAMIC_CURRENT_ENV,表示自動(dòng)設(shè)置為當(dāng)前的云環(huán)境,即在右鍵點(diǎn)擊小程序開發(fā)者工具里 functions 文件夾時(shí)選擇的環(huán)境。
  • 接著,我們通過 cloud.database() 生成了數(shù)據(jù)實(shí)例 db,用于之后在函數(shù)體中便捷的操作云數(shù)據(jù)庫(kù)。
  • 接著就是 main 函數(shù)體,里面是一個(gè) try/catch 語句塊,用于捕獲錯(cuò)誤,在 try 語句塊中,我們使用 db 的查詢操作:db.collection('post').get(),表示查詢所有的 post 數(shù)據(jù)。
  • 最后我們返回查詢到的 posts 數(shù)據(jù)。

第六劍: 定義對(duì)應(yīng)的 reducers 文件

因?yàn)檫@里 SET_POSTS 的 Action 我們?cè)谏弦?“大” 節(jié)中創(chuàng)建帖子時(shí)已經(jīng)定義了,所有在 “這一劍” 中我們無需添加額外的代碼,復(fù)用之前的邏輯就好。

“六脈神劍” 搞定 getPost 異步邏輯

在上面兩 “大” 節(jié)中,我們連續(xù)用了兩次 “六脈神劍”,相信跟到這里的同學(xué)應(yīng)該對(duì)我們接下來要做的事情已經(jīng)輕車熟路了吧。

接下來,我們將收尾 Post 邏輯的最后一公里,即帖子詳情的異步邏輯 “getPost” 接入,話不多說就是干!

第一劍:post 組件中發(fā)起異步請(qǐng)求

打開 src/pages/post/post.jsx 文件,對(duì)其中的內(nèi)容作出對(duì)應(yīng)的修改如下:

import Taro, { useRouter, useEffect } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { useDispatch, useSelector } from '@tarojs/redux'

import { PostCard } from '../../components'
import './post.scss'
import { GET_POST, SET_POST } from '../../constants'

export default function 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,
      },
    })

    return () => {
      dispatch({ type: SET_POST, payload: { post: {} } })
    }
  }, [])

  return (
    <View className="post">

可以看到,上面的內(nèi)容做了如下四處修改:

  • 首先我們使用 useDispatch Hooks 獲取到了 dispatch 函數(shù)。
  • 接著,在 useEffects Hooks 里面定義了 dispatch 了 action.type 為 GET_POST 的 action,它是一個(gè)異步 Action,并且我們?cè)?Hooks 最后返回了一個(gè)函數(shù),其中的內(nèi)容為將 post 設(shè)置為空對(duì)象,這里用到的 SET_POST 常量我們將在后面定義它。這個(gè)返回函數(shù)主要用于 post 組件卸載之后,Redux Store 數(shù)據(jù)的重置,避免下次打開帖子詳情還會(huì)渲染之前獲取到的帖子數(shù)據(jù)。
  • 接著,我們使用 useSelector Hooks 來獲取異步請(qǐng)求到的 post 數(shù)據(jù),并用于 return 語句中的數(shù)據(jù)渲染。
  • 最后我們刪除了不必要的獲取 posts 數(shù)據(jù)的 useSelector Hooks,以及刪掉了不必要的調(diào)試 console.log 語句。

第二劍: 聲明和補(bǔ)充對(duì)應(yīng)需要的異步 sagas 文件

在 “第一劍” 中,我們從組件中 dispatch 了 action.type 為 GET_POST 的異步 Action,接下來我們要做的就是在對(duì)應(yīng)的 sagas 文件中補(bǔ)齊響應(yīng)這個(gè)異步 action 的 sagas。

打開 src/sagas/post.js 文件,在其中定義 getPosts sagas 邏輯如下:

  // ... 和之前的邏輯一樣
import {
  // ... 和之前的邏輯一樣
  SET_POST,
} from '../constants';

  // ... 和之前的邏輯一樣

function* getPost(postId) {
  try {
    const post = yield call(postApi.getPost, postId)

    // 其實(shí)以下三步可以合成一步,但是這里為了講解清晰,將它們拆分成獨(dú)立的單元

    // 發(fā)起獲取帖子成功的 action
    yield put({ type: POST_SUCCESS })

    // 更新 Redux store 數(shù)據(jù)
    yield put({
      type: SET_POST,
      payload: {
        post,
      },
    })
  } catch (err) {
    console.log('getPost ERR: ', err)

    // 獲取帖子失敗,發(fā)起失敗的 action
    yield put({ type: POST_ERROR })
  }
}
function* watchGetPost() {
  while (true) {
    const { payload } = yield take(GET_POST)

    yield fork(getPost, payload.postId)
  }
}

export { watchGetPost }

可以看到,上面的改動(dòng)主要是創(chuàng)建 watcherSagahandlerSaga

創(chuàng)建 watcherSaga

  • 我們創(chuàng)建了登錄的 watcherSagawatchGetPost ,它用來監(jiān)聽 action.typeGET_POST 的 action,并且當(dāng)監(jiān)聽到 GET_POST action 之后,然后激活 handlerSagagetPost 去處理對(duì)應(yīng)的獲取單個(gè)帖子的邏輯。
  • 這里的 watcherSagawatchGetPost 是一個(gè)生成器函數(shù),它內(nèi)部是一個(gè) while 無限循環(huán),表示在內(nèi)部持續(xù)監(jiān)聽 GET_POST action。
  • 在循環(huán)內(nèi)部,我們使用了 redux-saga 提供的 effects helper 函數(shù):take,它用于監(jiān)聽 GET_POST action,獲取 action 中攜帶的數(shù)據(jù),這里我們拿到了傳過來的 payload 數(shù)據(jù)。
  • 接著我們使用了另外一個(gè) effects helper 函數(shù):fork,它表示非阻塞的執(zhí)行 handlerSagagetPost ,并傳入了獲取到 payload.postId 參數(shù)。

創(chuàng)建 handlerSaga

  • 我們創(chuàng)建了獲取單個(gè)帖子的 handlerSagagetPost ,它用來處理獲取帖子邏輯。
  • getPost 也是一個(gè)生成器函數(shù),在它內(nèi)部是一個(gè) try/catch 語句,用于處理獲取單個(gè)帖子請(qǐng)求可能存在的錯(cuò)誤情況。
  • try 語句中,首先是使用了 redux-saga 提供給我們的 effects helper 函數(shù):call 來調(diào)用登錄的 API:postApi. getPost

    • 如果獲取單個(gè)帖子成功,我們使用 redux-saga 提供的 effects helpers 函數(shù):putput 類似之前在 view 中的 dispatch 操作,,來 dispatch 了兩個(gè) action:POST_SUCCESS ,SET_POSTS ,代表更新獲取單個(gè)帖子成功的狀態(tài),設(shè)置最新獲取的帖子到 Redux Store 中。
  • 如果獲取單個(gè)帖子失敗,我們則使用 put 發(fā)起一個(gè) POST_ERROR 的 action 來更新獲取單個(gè)帖子失敗的信息到 Redux Store
一些額外的工作

為了創(chuàng)建 watcherSagahandlerSaga,我們還導(dǎo)入了 postApi.getPost ,我們將在后面來創(chuàng)建這個(gè) API。

除此之外我們還導(dǎo)入了需要使用的 action 常量:

  • SET_POST :響應(yīng)獲取帖子列表的 ACTION 常量,我們將在 “第三劍” 中創(chuàng)建它
加入 saga 中心調(diào)度文件

我們像之前將 watchGetPosts 等加入到 sagas 中心調(diào)度文件一樣,將我們創(chuàng)建好的 watchGetPost 也加入進(jìn)去:

打開 src/sagas/index.js 文件,對(duì)其中的內(nèi)容作出如下的修改:

import { fork, all } from 'redux-saga/effects'

import { watchLogin } from './user'
import { watchCreatePost, watchGetPosts, watchGetPost } from './post'

export default function* rootSaga() {
  yield all([
    fork(watchLogin),
    fork(watchCreatePost),
    fork(watchGetPosts),
    fork(watchGetPost),
  ])
}

第三劍:定義 sagas 需要的常量文件

打開 src/constants/post.js 文件,定義我們之前創(chuàng)建的常量文件 GET_POST


export const SET_POST = 'SET_POST'

第四劍:定義 sagas 涉及到的前端 API 文件

在之前的 post saga 文件里面,我們使用到了 postApi.getPost,它里面封裝了用于向后端(這里我們是小程序云)發(fā)起和獲取單個(gè)帖子有關(guān)請(qǐng)求的邏輯,讓我們馬上來實(shí)現(xiàn)它吧。

打開 src/api/post.js 文件,并在其中編寫內(nèi)容如下:

// ... 其他內(nèi)容和之前一致
async function getPost(postId) {
  const isWeapp = Taro.getEnv() === Taro.ENV_TYPE.WEAPP
  const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY

  // 針對(duì)微信小程序使用小程序云函數(shù),其他使用小程序 RESTful API
  try {
    if (isWeapp) {
      const { result } = await Taro.cloud.callFunction({
        name: 'getPost',
        data: {
          postId,
        },
      })

      return result.post
    }
  } catch (err) {
    console.error('getPost ERR: ', err)
  }
}

const postApi = {
  getPost,
}
export default postApi

可以看到上面的代碼有如下六處改動(dòng):

  • 在上面的代碼中,我們定義了 getPost 函數(shù),它是一個(gè) async 函數(shù),用來處理異步邏輯,在 getPost 函數(shù)中,我們對(duì)當(dāng)前的環(huán)境進(jìn)行了判斷,且只在微信小程序,即 isWeapp 的條件下執(zhí)行獲取單個(gè)帖子的操作,對(duì)于支付寶小程序和 H5,我們則放在下一節(jié)使用 LeanCloud 的 Serverless 來解決。
  • 創(chuàng)建帖子邏輯是一個(gè) try/catch 語句,用于捕捉可能存在的請(qǐng)求錯(cuò)誤,在 try 代碼塊中,我們使用了 Taro 為我們提供的微信小程序云的云函數(shù) API Taro.cloud.callFunction 來便捷的向小程序云發(fā)起云函數(shù)調(diào)用請(qǐng)求。
  • 這里我們調(diào)用了一個(gè) getPost 云函數(shù),并給它傳遞了對(duì)應(yīng)要獲取的帖子的 postId 我們將在下一節(jié)中實(shí)現(xiàn)這個(gè)云函數(shù)。
  • 如果調(diào)用成功,我們可以接收返回值,用于從后端返回?cái)?shù)據(jù),這里我們返回了 result.post 數(shù)據(jù),即從小程序云返回的單個(gè)帖子。
  • 如果調(diào)用失敗,則打印錯(cuò)誤。
  • 最后我們?cè)谝呀?jīng)定義好的 postApi 對(duì)象里,添加 getPost API 屬性然后將其導(dǎo)出,這樣在 post saga 函數(shù)里面就可以導(dǎo)入 postApi 然后通過 postApi. getPost 的方式來調(diào)用 getPost API 處理獲取單個(gè)帖子的邏輯了。

第五劍:創(chuàng)建對(duì)應(yīng)的微信小程序云函數(shù)

創(chuàng)建 getPost 云函數(shù)

按照和之前創(chuàng)建 getPosts 云函數(shù)類似,我們創(chuàng)建 getPost 云函數(shù)。

創(chuàng)建成功之后,我們可以得到兩個(gè)文件,一個(gè)是 functions/getPost/package.json 文件,它和之前的類似。

{
  "name": "getPost",
  "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"
  }
}

第二個(gè)文件就是我們需要編寫創(chuàng)建帖子邏輯的 functions/getPost/index.js 文件,微信小程序開發(fā)者工具會(huì)默認(rèn)為我們生成一段樣板代碼。

我們?cè)?function/getPost 文件夾下同樣運(yùn)行 npm install 安裝對(duì)應(yīng)的云函數(shù)依賴,這樣我們才能運(yùn)行它。

編寫 getPost 云函數(shù)

打開 functions/getPost/index.js 文件,對(duì)其中的內(nèi)容作出對(duì)應(yīng)的修改如下:

// 云函數(shù)入口文件
const cloud = require('wx-server-sdk')

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

const db = cloud.database()

// 云函數(shù)入口函數(shù)
exports.main = async (event, context) => {
  const { postId } = event

  try {
    const { data } = await db
      .collection('post')
      .doc(postId)
      .get()

    return {
      post: data,
    }
  } catch (e) {
    console.error(`getPost ERR: ${e}`)
  }
}

可以看到上面的代碼改動(dòng)主要有以下處:

  • 首先我們給 cloud.init() 傳入了環(huán)境參數(shù),我們使用了內(nèi)置的 cloud.DYNAMIC_CURRENT_ENV,表示自動(dòng)設(shè)置為當(dāng)前的云環(huán)境,即在右鍵點(diǎn)擊小程序開發(fā)者工具里 functions 文件夾時(shí)選擇的環(huán)境。
  • 接著,我們通過 cloud.database() 生成了數(shù)據(jù)實(shí)例 db,用于之后在函數(shù)體中便捷的操作云數(shù)據(jù)庫(kù)。
  • 接著就是 main 函數(shù)體,里面是一個(gè) try/catch 語句塊,用于捕獲錯(cuò)誤,在 try 語句塊中,我們首先從 event 對(duì)象里面獲取到了 postId,接著我們使用 db 的查詢操作:db.collection('post').doc(postId).get(),表示查詢所有的對(duì)應(yīng) _idpostId 的單個(gè)帖子數(shù)據(jù)
  • 最后我們返回查詢到的 post 數(shù)據(jù)。

第六劍: 定義對(duì)應(yīng)的 reducers 文件

因?yàn)檫@里 SET_POST 的 Action 我們?cè)谏仙?“大” 節(jié)中創(chuàng)建帖子時(shí)已經(jīng)定義了,所有在 “這一劍” 中我們無需添加額外的代碼,復(fù)用之前的邏輯就好。

小結(jié)

在這篇教程中,我們連續(xù)使用了三次 “六脈神劍” 講完了我們的 Post 邏輯的異步流程,讓我們?cè)賮韽?fù)習(xí)一下我們開頭提到的 “六脈神劍”:

  • 將組件中的同步邏輯重構(gòu)到異步邏輯
  • 聲明和補(bǔ)充對(duì)應(yīng)需要的異步 sagas 文件
  • 定義 sagas 需要的常量文件
  • 定義 sagas 涉及到的前端 API 文件
  • 創(chuàng)建對(duì)于的微信小程序云函數(shù),并編寫對(duì)應(yīng)的 Node.js 處理邏輯
  • 定義對(duì)應(yīng)的 reducers 文件

這是一套講解模式,也是一套寫代碼的最佳實(shí)踐方式之一,希望你能受用。

一點(diǎn)遺憾

這兩篇講解微信小程序云的文章有一個(gè)小小的遺憾,我們也在之前的文章中提到過了,就是微信小程序云僅限于微信小程序內(nèi)的使用,如果我們想做多端應(yīng)用,比如支付寶小程序云,H5 網(wǎng)站,那么單單使用微信小程序就顯得無能為力了,我們將在下一篇文章中引入 LeanCloud Serverless 服務(wù),并使用它來補(bǔ)齊我們跨端小程序開發(fā)的短板,敬請(qǐng)期待!?

想要學(xué)習(xí)更多精彩的實(shí)戰(zhàn)技術(shù)教程?來圖雀社區(qū)逛逛吧。

本文所涉及的源代碼都放在了 Github 上,如果您覺得我們寫得還不錯(cuò),希望您能給?這篇文章點(diǎn)贊+Github倉(cāng)庫(kù)加星?哦

作者:一只圖雀

原文地址:https://segmentfault.com/a/1190000022296526文章來源地址http://www.zghlxwxcb.cn/news/detail-838284.html

喜歡 0

到了這里,關(guān)于Taro 小程序開發(fā)大型實(shí)戰(zhàn)(七):嘗鮮微信小程序云(下篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 微信小程序 | 小程序開發(fā)

    微信小程序 | 小程序開發(fā)

    ??? 微信小程序?qū)冢盒〕绦蜷_發(fā) 初級(jí)知識(shí) ????? 個(gè)人簡(jiǎn)介:一個(gè)不甘平庸的平凡人?? ? 個(gè)人主頁:CoderHing的個(gè)人主頁 ?? 格言: ?? 路漫漫其修遠(yuǎn)兮,吾將上下而求索?? ?? 你的一鍵三連是我更新的最大動(dòng)力?? 目錄 一、認(rèn)識(shí)小程序開發(fā) 什么是小程序? 各個(gè)平臺(tái)小

    2024年01月24日
    瀏覽(61)
  • taro 支付寶/微信小程序的chooseImage真機(jī)和開發(fā)工具上的區(qū)別
  • 微信小程序開發(fā)之微信小程序交互

    微信小程序開發(fā)之微信小程序交互

    目錄 一、小程序交互 前端: 1、先在登陸界面中編寫代碼 2、在前端中編寫js代碼 后端: ? ? ? ? ? 1、先導(dǎo)入依賴: ? ? ? ? ? 2、定義好配置文件 ? ? ? ? ? 3、編寫好實(shí)體類 ? ? ? ? ? 4、將幫助類進(jìn)行配置 ? ? ? ? ? 5、編寫mapper類 ? ? ? ? ? 6、定義service層以及對(duì)應(yīng)的

    2024年02月09日
    瀏覽(21)
  • 微信小程序 -- 小程序開發(fā)能力與拓展

    微信小程序 -- 小程序開發(fā)能力與拓展

    1. 獲取用戶頭像 當(dāng)小程序需要讓用戶完善個(gè)人資料時(shí),我們可以通過微信提供的頭像、昵稱填寫能力快速完善。如圖: 想使用微信提供的頭像填寫能力,需要兩步: 將 button 組件 open-type 的值設(shè)置為 chooseAvatar 當(dāng)用戶選擇需要使用的頭像之后,可以通過 bindchooseavatar 事件回調(diào)

    2024年04月15日
    瀏覽(29)
  • taro(踩坑) npm run dev:weapp 微信小程序開發(fā)者工具預(yù)覽報(bào)錯(cuò)

    taro(踩坑) npm run dev:weapp 微信小程序開發(fā)者工具預(yù)覽報(bào)錯(cuò)

    控制臺(tái)報(bào)錯(cuò)信息: VM72:9 app.js錯(cuò)誤: ?Error: module \\\'vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js\\\' is not defined, require args is \\\'./vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js\\\' 環(huán)境: node 版本:v18.16.0 Taro 版本:v3.6.18 vue3 typeScript sass webpack5 解決辦法: webpack5 改為?webpack4 set NOD

    2024年02月03日
    瀏覽(29)
  • 【微信小程序開發(fā)零基礎(chǔ)入門】——微信小程序入門

    【微信小程序開發(fā)零基礎(chǔ)入門】——微信小程序入門

    學(xué)習(xí)小程序跟學(xué)習(xí)網(wǎng)頁開發(fā)有什么不同 1.如何創(chuàng)建微信小程序項(xiàng)目 1.1 注冊(cè)、登錄、復(fù)制appId 注冊(cè):在 https://mp.weixin.qq.com/cgi-bin/wx 進(jìn)行注冊(cè)微信小程序開發(fā)賬號(hào) 登錄:在 https://mp.weixin.qq.com 登錄小程序賬號(hào) 復(fù)制appId: 在 \\\"開發(fā)\\\" 的 \\\"開發(fā)管理\\\" 的 \\\"開發(fā)設(shè)置\\\" 的 \\\"開發(fā)者ID\\\"中 1.2 下

    2024年02月03日
    瀏覽(32)
  • 【taro react】---- 解決開發(fā)環(huán)境微信小程序由于主包體積過大不能預(yù)覽問題

    【taro react】---- 解決開發(fā)環(huán)境微信小程序由于主包體積過大不能預(yù)覽問題

    1. 開發(fā)環(huán)境代碼包大小 注意:可以看到此時(shí)主包加分包將近 5MB,上傳預(yù)覽將會(huì)超出限制?。?! 2. 預(yù)覽結(jié)果 報(bào)錯(cuò):代碼包大小超過限制,主包資源近3MB,限制最大2MB?。。?3. 解決辦法 使用webpack的壓縮插件,在開發(fā)環(huán)境編譯的時(shí)候進(jìn)行壓縮; 進(jìn)行分包處理,同時(shí)依賴也進(jìn)行

    2024年02月10日
    瀏覽(36)
  • 【小程序】零基礎(chǔ)微信小程序開發(fā)+實(shí)戰(zhàn)項(xiàng)目

    【小程序】零基礎(chǔ)微信小程序開發(fā)+實(shí)戰(zhàn)項(xiàng)目

    如何擁有一款屬于你自己的小程序,驚喜就在下面! 目前,小程序行業(yè)已經(jīng)成為互聯(lián)網(wǎng)營(yíng)銷的熱門黑馬之一,依托于各大流量平臺(tái),小程序行業(yè)具有天然的用戶基礎(chǔ)和得天獨(dú)厚的資源優(yōu)勢(shì),憑借其方便快捷的操作以及簡(jiǎn)單通俗的模式,僅短短一年的時(shí)間,就迎來了爆發(fā)性的增

    2024年02月11日
    瀏覽(22)
  • 【微信小程序開發(fā)】微信小程序集成騰訊位置項(xiàng)目配置

    【微信小程序開發(fā)】微信小程序集成騰訊位置項(xiàng)目配置

    騰訊位置服務(wù)官網(wǎng) 當(dāng)然沒賬號(hào)的要先注冊(cè)一個(gè)賬號(hào) 在我的應(yīng)用里創(chuàng)建一個(gè)新的應(yīng)用,印象中需要小程序ID,去微信開發(fā)者工具里面找到自己的小程序ID填入即可 添加 key 中勾選勾選 WebServiceAPI 從官網(wǎng)里下載,我這里下載的是 v1.2 打開微信開發(fā)者工具 在查找小程序ID的地方下滑

    2024年02月02日
    瀏覽(39)
  • 微信小程序skyline渲染引擎嘗鮮

    微信小程序skyline渲染引擎嘗鮮

    概述 官方描述 當(dāng)小程序基于 WebView 環(huán)境下時(shí),WebView 的 JS 邏輯、DOM 樹創(chuàng)建、CSS 解析、樣式計(jì)算、Layout、Paint (Composite) 都發(fā)生在同一線程,在 WebView 上執(zhí)行過多的 JS 邏輯可能阻塞渲染,導(dǎo)致界面卡頓。以此為前提,小程序同時(shí)考慮了性能與安全,采用了目前稱為「雙線程模

    2024年02月04日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包