Skip to content

在uniapp中使用GraphQL的一些探索

前言

前面做了一个小程序,传送门,最近正在重构这个小程序,线上的暂时不能访问,敬请期待!

其中使用了GraphQL的技术,对于服务端如nest.js应用程序对于GraphQL的支持以及相关资料都非常丰富,这里就不细谈了;但是关于国内技术栈uniapp对于graphQL的资料就相对来说比较少了。

所以这里简单谈谈我在uniapp这个client中是如何使用GraphQL的。当然,请注意标题《探索》二字,这并不是最佳实践,如果你有更好的写法,也欢迎讨论~

其他:如果你对GraphQL一无所知,关于GraphQL是什么,有什么好处,可以瞧瞧我之前写的这篇文章--了解API相关范式(RPC、REST、GraphQL)

此文章基于vue3/vite版

使用Villus

如果你的项目属于Vue+GraphQL,也许你就可以试试这个小而美的库Villus

这个库和Vue的响应式结合得非常好,对Vue熟悉的话使用起来也较为优雅,比如官网中的这个例子使用查询:

html
<template>
  <div>
    <div v-if="data">
      <pre>{{ data }}</pre>
    </div>
  </div>
</template>
<script setup>
import { useQuery } from 'villus';
const { data } = useQuery({
  query: '{ posts { title } }',
});
</script>

值得注意的是,data变量就已经被封装成了响应式,所以你可以很方便地在template中使用它。

更改villus的网络请求方式为uni.request

由于Uniapp等小程序中使用的都是自己的网络请求方法,比如封装的uni.request,这里我们需要使用uni.request替换其中的fetch插件,大致过程如下:

具体来说,我们在某个文件夹下(比如我是src/graphql)中创建一个setup.ts,这里参考了这篇文章的相关逻辑:

ts
import { createClient, fetch } from "villus";

type Methods =
  | "OPTIONS"
  | "GET"
  | "HEAD"
  | "POST"
  | "PUT"
  | "DELETE"
  | "TRACE"
  | "CONNECT";

// 此处重写fetch,请求采用UniAPP提供的uni.request
const fetchPlugin = fetch({
  fetch(url, options) {
    return new Promise((resolve, reject) => {
      uni.request({
        url: url.toString(),
        method: options?.method as Methods,
        data: options?.body as any,
        header: options?.headers,

        success(res) {
          resolve({
            ok: true,
            status: res.statusCode,
            headers: res.header,
            text: async () => JSON.stringify(res.data),
            json: async () => res.data,
          } as Response);
        },
        fail(e) {
          reject(e);
        },
      });
    });
  },
});

export const apolloClient = createClient({
  url: `${import.meta.env.VITE_SERVER_IP}/graphql`,
  use: [fetchPlugin],
});

此时,关于在uniapp中使用villus相对于web方便使用的特殊逻辑就处理完成了

稍微封装一下

平常我们在使用axios请求时,基本都会将api字符串单独提出来放在一个文件夹如src/apis/之类的。这里我们也将需要使用到的graphql字符串单独提出来,放在我们之前创建的文件夹src/graphql下。当然,你可以根据自己的习惯进行自定义。

针对每一个模块,我都单独建立了一个文件graphql-XXX.ts,比如用户模块就是graphql-user.ts,都放置于src/graphql/下。

其中的文件内容就是关于graphql的查询字符串,比如:

ts
// graphql-user.ts

import gql from "graphql-tag";

export const refreshToken = gql`
  mutation refreshToken($token: JWT!) {
    refreshToken(token: $token) {
      accessToken
      refreshToken
    }
  }
`;

// 更多...

然后为了方便其他文件导入,我又在src/graphql/下新建了一个index.ts文件:

ts
import * as home from "./graphql-home";
import * as questionnaire from "./graphql-questionnaire";
import * as analyze from "./graphql-analyze";
import * as user from "./graphql-user";
// ...

/* how to use
import GQL from "@/graphql"
const curGQL = GQL.home.listAsOwner
**/
export default {
  home,
  questionnaire,
  analyze,
  user,
};

这里你也可以选择直接export * from "./graphql-home";这样,但是由于可能不容模块的查询字符串存在命名冲突,所以选择了上述方式导出,这样就有了一个命名空间,使用其他就是上方注释的部分:

ts
import GQL from "@/graphql"
const curGQL = GQL.home.listAsOwner

示例

好的,写了这么多,接下来就开始在实际需求中使用上述封装的部分,比如这样一个查询数量并展示的组件就是这么写的:

html
    class="analytics-count-container uni-white-bg uni-shadow-sm uni-radius-lg"
  >
    <uni-row>
      <uni-col :span="14">
        <image
          class="img"
          style="width: 100%"
          mode="widthFix"
          :src="countLeft"
        ></image>
      </uni-col>
      <uni-col :span="10">
        <view class="label uni-mt-8 uni-primary-dark">
          <view class="top">已填写</view>
          <view class="bottom uni-mt-4">{{ count }}</view>
        </view>
      </uni-col>
    </uni-row>
  </view>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { countLeft } from "../const/img-url";
import { useQuery } from "villus";
import GQL from "@/graphql";

const { data, execute: _execute } = useQuery({ query: GQL.home.countAsFriend });

const count = computed(() => data.value?.me.countAsFriend ?? "--");
</script>

关键代码就是:

ts
const { data, execute: _execute } = useQuery({ query: GQL.home.countAsFriend });
const count = computed(() => data.value?.me.countAsFriend ?? "--");

_execute是我自定义的eslint规则:下划线开头的变量定义但未使用不会报错,因为这里可能父组件会调用这个方法,所以解构了这个变量

最后

好了,大功告成,正如标题所说,这些是笔者在uniapp中使用graphql的一些探索,国内关于graphql的资料较少,结合uniapp资料就更少了,希望对你有所帮助,也希望大家不吝赐教。