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

相关推荐
独泪了无痕2 小时前
Vue3中防御XSS攻击的“特效药”-DOMPurify
前端·vue.js·安全
云水一下3 小时前
Vue.js从零到精通系列(五):全局状态管理——Pinia 核心与实践
前端·javascript·vue.js
老马聊技术4 小时前
AI对话功能之SpringBoot整合Vue3
vue.js·人工智能·spring boot·后端
英勇无比的消炎药4 小时前
一站式汇总TinyVue工具案例与真实落地经验
vue.js·前端框架
梵得儿SHI6 小时前
Vue 项目实战与性能优化全攻略:从代码、渲染到首屏,一站式解决卡顿慢加载
前端·vue.js·性能优化·vite·前端面试·前端优化·首屏优化
一 乐8 小时前
幼儿园管理系统|基于springboot + vue幼儿园管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·幼儿园管理系统
云水一下8 小时前
Vue.js从零到精通系列(七):高级特性实战——Teleport、异步组件、自定义指令与TypeScript深度结合
前端·vue.js·typescript
qq4356947018 小时前
Vue05
前端·vue.js
英勇无比的消炎药9 小时前
收藏备用TinyVue开发高频踩坑问题合集
vue.js
英勇无比的消炎药9 小时前
体积瘦身TinyVue打包优化与按需加载实践
vue.js·前端工程化