在小程序框架Taro中使用 vue3+graphql

Frame 35.png

前言:

在小程序中使用 graphql 相對(duì)來(lái)講是一個(gè)小眾的需求沛励,并且在 Taro 中就更少一些墩崩,但對(duì)我們來(lái)講卻是一個(gè)必需要解決的問(wèn)題。由于今年基礎(chǔ)服務(wù)端的技術(shù)全面升級(jí)侯勉,已經(jīng)都切換到基于 graphql api 實(shí)現(xiàn)上面鹦筹,所以新的小程序端就需要完全支持 grapqhl api的實(shí)現(xiàn)。

選型

小程序選型

首先是小程序端選型的問(wèn)題址貌,我們今年以前的所有小程序都是原生+uni來(lái)實(shí)現(xiàn)的铐拐,再早一點(diǎn)也用到過(guò) wepy,但主要還是 uni练对。但今年由于 vue3 的到來(lái)和對(duì)于 typescript的應(yīng)用遍蟋,我們需要一個(gè)能對(duì) typescript + vue3支持較好的小程序方案。現(xiàn)在市面對(duì)于這個(gè)需求支持最好的就是 taro3 了螟凭。

Graphql client 庫(kù)選型

Taro 做為小程序的實(shí)現(xiàn)是比較滿足需求的虚青,但是 taro3 官方并沒(méi)有針對(duì) graphql 支持,而社區(qū)中主要還是基于 @apollo 的庫(kù)方案比較多一些螺男,還有一些直接是基于 post 請(qǐng)求來(lái)實(shí)現(xiàn)的棒厘,但是整體來(lái)講方案都比較簡(jiǎn)陋,或者有一定兼容問(wèn)題下隧。graphql client實(shí)現(xiàn)是有一套規(guī)范標(biāo)準(zhǔn)奢人,并且針對(duì)使用復(fù)合API編寫響應(yīng)式查詢/變量、緩存還是要有一定支持才能體現(xiàn) graphql 的強(qiáng)大淆院。

經(jīng)過(guò)反復(fù)選型和試驗(yàn)何乎,市面能支持我們需求(Vue3+typescript+完善的 graphql 實(shí)現(xiàn))的最終有兩個(gè)庫(kù)可選:

  1. URQL


    urql

用于React、Svelte、Vue或JavaScript的高度可定制和通用的GraphQL客戶端支救,這個(gè) graphql 最初實(shí)現(xiàn)是基于 react 端的抢野,后期已經(jīng)對(duì)各流行的庫(kù)有了完善支持 https://formidable.com/open-source/urql/

  1. Villus


    villus

這是一個(gè)小而快速的GraphQL客戶端,對(duì) vue3 有完善的支持各墨。https://villus.logaretm.com/

實(shí)現(xiàn)

以上兩個(gè)庫(kù)的網(wǎng)絡(luò)都是基于 fetch 來(lái)實(shí)現(xiàn)的指孤,所以直接導(dǎo)入進(jìn) taro3 工程是沒(méi)有辦法實(shí)現(xiàn)小程序端網(wǎng)絡(luò)請(qǐng)求的。小程序會(huì)有自己的 wx-request欲主,taro 也是封裝了請(qǐng)求而已邓厕。所以我們要做一項(xiàng)工作就是實(shí)現(xiàn)一個(gè) "fetch" 接口來(lái)適配。
URQL這個(gè)庫(kù)經(jīng)過(guò)適配編譯會(huì)出現(xiàn)異常扁瓢,并且包較大一些不太適配详恼,最終選用的是 villus 直接將源碼引入到 taro 工程中,結(jié)構(gòu)如下:

├── villus
│   ├── Mutation.ts
│   ├── Provider.ts
│   ├── Query.ts
│   ├── Subscription.ts
│   ├── cache.ts
│   ├── client.ts
│   ├── dedup.ts
│   ├── fetch-taro.ts
│   ├── fetch.ts
│   ├── handleSubscriptions.ts
│   ├── helpers.ts
│   ├── index.ts
│   ├── shared
│   ├── symbols.ts
│   ├── types.ts
│   ├── useClient.ts
│   ├── useMutation.ts
│   ├── useQuery.ts
│   ├── useSubscription.ts
│   └── utils
└── wxcomponents

我們只需要對(duì) fetch.ts 進(jìn)行改造引几,加入一個(gè) fetch-taro.ts 的適配昧互,改造如下:
** fetch.ts 原始文件**

import { GraphQLError } from 'graphql';
import { ClientPlugin } from './types';
import { makeFetchOptions, resolveGlobalFetch, parseResponse } from './shared';
import { CombinedError } from './utils';

interface FetchPluginOpts {
  fetch?: typeof window['fetch'];
}

export function fetch(opts?: FetchPluginOpts): ClientPlugin {
  const fetch = opts?.fetch || resolveGlobalFetch();
  if (!fetch) {
    throw new Error('Could not resolve a fetch() method, you should provide one.');
  }

  return async function fetchPlugin(ctx) {
    const { useResult, opContext, operation } = ctx;
    const fetchOpts = makeFetchOptions(operation, opContext);

    let response;
    try {
      response = await fetch(opContext.url as string, fetchOpts).then(parseResponse);
    } catch (err) {
      return useResult(
        {
          data: null,
          error: new CombinedError({ response, networkError: err }),
        },
        true
      );
    }
...

fetch-taro.ts改造后文件

import {GraphQLError} from 'graphql';
import {ClientPlugin, CombinedError} from "./index";
import Taro from '@tarojs/taro';
import {makeFetchOptions} from "./shared";

export function fetch(): ClientPlugin {

  return async function fetchPlugin(ctx) {
    const {useResult, opContext, operation} = ctx;
    const fetchOpts = makeFetchOptions(operation, opContext);
    let response;
    try {
        const requestTask = Taro.request({
        header: opContext.headers,
        url: opContext.url as string,
        method: 'POST',
        data: fetchOpts.body,
        dataType: "json",
      })
      response = await requestTask
    } catch (err) {
      return useResult(
        {
          data: null,
          error: new CombinedError({response, networkError: err}),
        },
        true
      );
    }
...

重點(diǎn)是把原來(lái)的 await fetch(...) 改造為 Taro.request(...) 這樣一個(gè)適配就使我們引入了一個(gè)完善的 grapqhl 客戶端。

應(yīng)用

1. 實(shí)現(xiàn) graphql client 全局定義

import {createClient} from '../villus';
import global from "../utils/global";
import {fetch} from '../villus/fetch-taro'
import config from '../config'
function authPlugin({opContext}) {
  opContext.headers.Authorization = 'Bearer ' + global.getToken();
}

export const client = createClient({
  url: config.baseUrl + 'weapp-api',
  use: [ authPlugin, fetch() ],
  cachePolicy: 'network-only',
});

2. 定義 graphql 文件

import gql from 'graphql-tag';
export const Login = gql`
  mutation WxLogin($code: String!){
    wxLogin(code: $code){
      user{
        id
        identifier
        token
        permissions
      }
    }
  }
`
### API 式請(qǐng)求

3. auth.ts API請(qǐng)求文件

  /**
   * 開始登錄
   */
  static async doLogin() {
    // 取得微信 code
    const {code} = await this.wxLogin()

    return client.executeMutation({
      query: Login,
      variables: {
        code
      }
    })
  }

響應(yīng)式請(qǐng)求

  import { useQuery } from "villus";
  const FetchFood = `
  query QueryFoods($operator: StringOperators!){
    foods(options:{filter:{
      food: $operator
    }}){
      items{
        id
        food
        meta{
          transform{
            key
            unit
          }
        }
      }
      totalItems
    }
  }
  `;
  import SearchView from '../../components/common-search/index'
  export default {
    components: {
      SearchView
    },
    setup() {
      const {  execute, data, isFetching } = useQuery({
        query: FetchFood,
        variables: {
          "operator": {
            "contains": "豬肉"
          }
        }
      })
      return {
        data
      }
    },
...

客戶端測(cè)試

image.png

總結(jié)

此次文章中記錄了 taro3 + vue3 + graphql 的整合方案伟桅,評(píng)估了 URQL和Villus兩套方案敞掘,最終選用 Villus 的改造方案,完成了整套技術(shù)的結(jié)合楣铁,并最終在商業(yè)應(yīng)用中完美的使用玖雁。希望對(duì)有在小程序中使用 grahql 的朋友有所幫助。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盖腕,一起剝皮案震驚了整個(gè)濱河市赫冬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溃列,老刑警劉巖劲厌,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異听隐,居然都是意外死亡补鼻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門雅任,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)风范,“玉大人仗颈,你說(shuō)我怎么就攤上這事墙歪。” “怎么了戒祠?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵成玫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)哭当,這世上最難降的妖魔是什么猪腕? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮钦勘,結(jié)果婚禮上陋葡,老公的妹妹穿的比我還像新娘。我一直安慰自己彻采,他們只是感情好腐缤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肛响,像睡著了一般岭粤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上特笋,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天剃浇,我揣著相機(jī)與錄音,去河邊找鬼猎物。 笑死虎囚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蔫磨。 我是一名探鬼主播淘讥,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼堤如!你這毒婦竟也來(lái)了蒲列?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤煤惩,失蹤者是張志新(化名)和其女友劉穎嫉嘀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體魄揉,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剪侮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了洛退。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓣俯。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖兵怯,靈堂內(nèi)的尸體忽然破棺而出彩匕,到底是詐尸還是另有隱情,我是刑警寧澤媒区,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布驼仪,位于F島的核電站掸犬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绪爸。R本人自食惡果不足惜湾碎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奠货。 院中可真熱鬧介褥,春花似錦、人聲如沸递惋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)萍虽。三九已至睛廊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贩挣,已是汗流浹背喉前。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留王财,地道東北人卵迂。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绒净,于是被迫代替她去往敵國(guó)和親见咒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容