前言: 在小程序中使用 graphql 相对来讲是一个小众的需求,并且在 Taro 中就更少一些,但对我们来讲却是一个必需要解决的问题。由于今年基础服务端的技术全面升级,已经都切换到基于 graphql api 实现上面,所以新的小程序端就需要完全支持 grapqhl api的实现。
选型小程序选型首先是小程序端选型的问题,我们今年以前的所有小程序都是原生+uni来实现的,再早一点也用到过 wepy,但主要还是 uni。但今年由于 vue3 的到来和对于 typescript的应用,我们需要一个能对 typescript + vue3支持较好的小程序方案。现在市面对于这个需求支持最好的就是 taro3 了。 Graphql client 库选型Taro 做为小程序的实现是比较满足需求的,但是 taro3 官方并没有针对 graphql 支持,而社区中主要还是基于 @apollo 的库方案比较多一些,还有一些直接是基于 post 请求来实现的,但是整体来讲方案都比较简陋,或者有一定兼容问题。graphql client实现是有一套规范标准,并且针对使用复合API编写响应式查询/变量、缓存还是要有一定支持才能体现 graphql 的强大。 经过反复选型和试验,市面能支持我们需求(Vue3+typescript+完善的 graphql 实现)的最终有两个库可选: URQL
用于React、Svelte、Vue或JavaScript的高度可定制和通用的GraphQL客户端,这个 graphql 最初实现是基于 react 端的,后期已经对各流行的库有了完善支持 https://formidable.com/open-source/urql/
Villus
这是一个小而快速的GraphQL客户端,对 vue3 有完善的支持。https://villus.logaretm.com/
实现以上两个库的网络都是基于 fetch 来实现的,所以直接导入进 taro3 工程是没有办法实现小程序端网络请求的。小程序会有自己的 wx-request,taro 也是封装了请求而已。所以我们要做一项工作就是实现一个 "fetch" 接口来适配。 URQL这个库经过适配编译会出现异常,并且包较大一些不太适配,最终选用的是 villus 直接将源码引入到 taro 工程中,结构如下: ├── 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
我们只需要对 fetch.ts 进行改造,加入一个 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 ); }...
重点是把原来的 await fetch(...) 改造为 Taro.request(...) 这样一个适配就使我们引入了一个完善的 grapqhl 客户端。 应用1. 实现 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 式请求
3. auth.ts API请求文件 /** * 开始登录 */ static async doLogin() { // 取得微信 code const {code} = await this.wxLogin() return client.executeMutation({ query: Login, variables: { code } }) }
响应式请求 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 } },...
客户端测试总结此次文章中记录了 taro3 + vue3 + graphql 的整合方案,评估了 URQL和Villus两套方案,最终选用 Villus 的改造方案,完成了整套技术的结合,并最终在商业应用中完美的使用。希望对有在小程序中使用 grahql 的朋友有所帮助。 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |