在uniapp中使用GraphQL的一些探索

在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资料就更少了,希望对你有所帮助,也希望大家不吝赐教。

相关推荐
GHUIJS1 分钟前
【vue3】vue3.5
前端·javascript·vue.js
&白帝&29 分钟前
uniapp中使用picker-view选择时间
前端·uni-app
计算机学姐1 小时前
基于python+django+vue的家居全屋定制系统
开发语言·vue.js·后端·python·django·numpy·web3.py
fakaifa1 小时前
八戒农场小程序V2最新源码
小程序·uni-app·php·生活·开源软件
Ripple1111 小时前
Vue源码速读 | 第二章:深入理解Vue虚拟DOM:从vnode创建到渲染
vue.js
秋沐1 小时前
vue中的slot插槽,彻底搞懂及使用
前端·javascript·vue.js
QGC二次开发1 小时前
Vue3 : Pinia的性质与作用
前端·javascript·vue.js·typescript·前端框架·vue
想退休的搬砖人3 小时前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
啥子花道3 小时前
Vue3.4 中 v-model 双向数据绑定新玩法详解
前端·javascript·vue.js
清灵xmf3 小时前
揭开 Vue 3 中大量使用 ref 的隐藏危机
前端·javascript·vue.js·ref