你还在为代码中放入长长的模版字符串所苦恼吗,如下图代码片段:

ps:这个是grqphql client在nodejs后端项目的实践,如果你是在前端使用graphql,并使用了webpack,那么这些问题你都不用担心,因为有现成的轮子供你使用,参见相关loader:https://github.com/apollographql/graphql-tag/blob/master/loader.js,

由于项目开发紧张,我们最开始就是采用这图上这种模式,查询语句和业务代码全放在一起,结果是代码阅读和修改极其麻烦,且查询语句没有字段类型提示,graphql提供的fragment也不能使用(这个特性可以复用很多相同的片段)。

随着业务的不断增加,这个问题越来越凸显,我觉得必须想个办法处理一下,思路是:将graphql查询语句抽出来放在以.grqphql结尾的文件中,然后需要的时候再引入进来。webstorm有个插件正好可以实现语法高亮和类型提示,甚至可以再ide里面进行查询,参考:https://plugins.jetbrains.com/plugin/8097-js-graphql。但是问题来了,怎么再业务代码里面引入这个.graphql文件呢? 直接require肯定是不行的,因为这不是js或c++模块,这个确实也有现成的轮子,见:https://github.com/prisma/graphql-import, 但是这个工具在typescript环境下却有不少问题,见相关issue,怎么办呢?业务又催得紧,然后就用了最简单粗暴的方法: fs.readFileSync('./user.graphql','utf8'), 虽然不够优雅但也解决了燃眉之急。

上面这个办法虽然解决了查询语句和业务代码耦合在一起的问题,但是依然不能使用fragment,随着查询语句越来越多,很多片段都是一样的,后来更新的时候不得不同时修改几处代码,我想实现的效果是将fragment也抽离出来放在以.grqphql结尾的文件中,然后再另一个graphql文件中引入,最终拼在一起返回给业务代码

// a.grapqhl
fragment info on User {
name
mail
}
// b.graphql
#import 'a.graphql'
query user{
queryUser {
...info
}
}
// c.js

const b = loadGql('./b.graphql')

返回的b应该是个字符串,像下面这这样子:

fragment info on User{
name
mail
} query user {
queryUser{
....info
}
}

那么loadGql改怎么实现呢?google一番后,发现有个轮子可以参考下: https://github.com/samsarahq/graphql-loader/blob/master/src/loader.ts 但是这轮子需要配合webpack,不能直接在nodejs环境下直接使用,那就把它改造一下吧,上改造后的代码:

import { validate as graphqlValidate } from "graphql/validation/validate"
import { resolve, join, dirname } from "path"
import { Stats, writeFile,readFileSync, readFile } from "fs"
import pify = require("pify")
import {
DocumentNode,
DefinitionNode,
print as graphqlPrint,
parse as graphqlParse,
Source,
visit,
} from "graphql" export default function loadGql(filePath: string): string | null {
if (!filePath) return null
try {
const source = readFileSync(filePath, 'utf8')
if(!source) return null
const document = loadSource(source, filePath) const content = graphqlPrint(document)
return content
} catch (err) {
console.log(err)
return null
}
} function loadSource(
source: string,
filePath: string,
) {
let document: any = graphqlParse(new Source(source, "GraphQL/file"))
document = extractImports(source, document, filePath)
return document
} async function stat(
loader: any,
filePath: string,
): Promise<Stats> {
const fsStat: (path: string) => Promise<Stats> = pify(
loader.fs.stat.bind(loader.fs),
)
return fsStat(filePath)
} function extractImports(source: string, document: DocumentNode, filePath: string): DocumentNode {
const lines = source.split(/(\r\n|\r|\n)/) const imports: Array<string> = []
lines.forEach(line => {
// Find lines that match syntax with `#import "<file>"`
if (line[0] !== "#") {
return
} const comment = line.slice(1).split(" ")
if (comment[0] !== "import") {
return
} const filePathMatch = comment[1] && comment[1].match(/^[\"\'](.+)[\"\']/)
if (!filePathMatch || !filePathMatch.length) {
throw new Error("#import statement must specify a quoted file path")
} const itemPath = resolve(dirname(filePath), filePathMatch[1])
imports.push(itemPath)
}) const files = imports
const contents = files.map(path => [
readFileSync(path, 'utf8'),
path,
]) const nodes = contents.map(([content, fileContext]) => {
return loadSource(content, fileContext)
}
) const fragmentDefinitions = nodes.reduce((defs, node) => {
defs.push(...node.definitions)
return defs
}, [] as DefinitionNode[]) const newAst = visit(document, {
enter(node, key, parent, path, ancestors) {
if (node.kind === 'Document') {
const documentNode: DocumentNode = {
definitions: [...fragmentDefinitions, ...node.definitions],
kind: 'Document',
}
return documentNode
}
return node
},
}) return newAst
}

ps:代码为typescript,使用需转换成js

至此,这项工作基本告一段落

如何引入.graphql文件并优雅的使用fragment的更多相关文章

  1. Vue在单独引入js文件中使用ElementUI的组件

    Vue在单独引入js文件中使用ElementUI的组件 问题场景: 我想在vue中的js文件中使用elementUI中的组件,因为我在main.js中引入了element包和它的css,并挂载到了全局 ...

  2. HTML引入外部文件,解决统一管理导航栏问题。

    1.IFrame引入,看看下面的代码     <IFRAME NAME="content_frame" width=100% height=30 marginwidth=0 ...

  3. html引入css文件

    在HTML中,引入CSS的方法主要有行内式.内嵌式.导入式和链接式. 行内式:即在标记的style属性中设定CSS样式,这种方式本质上没有体现出CSS的优势,因此不推荐使用.例: <html&g ...

  4. Nodejs Express下引入本地文件的方法

    Express的结构如下: |---node_modules------用于安装本地模块.     |---public------------用于存放用户可以下载到的文件,比如图片.脚本文件.样式表 ...

  5. jsp文件引入js文件的方式(项目部署于web容器中)

    在页面中引入javascript文件的方式是多种多样的,本文介绍两种. 通过<script>标签插入js文件 通过这种方式引入的js,写对js文件和jsp文件的路径很重要.下面给出一个项目 ...

  6. 引入CSS文件的@import与link的权重分析

    我很少在CSS用到@import这个标签,最近看到一句话“link方式的样式的权重 高于@import的权重”,感觉不太对,@import只是一个引入外部文件而已,怎么会有高于link的权重呢?于是我 ...

  7. 使用EasyUI的插件前需要引入的文件

    一.使用EasyUI的插件需要引入一些文件 1.引入相关文件 easyui.css: easyUi的样式文件 icon.css:easyUI的图标样式文件 easyui.min.js:easyUi的类 ...

  8. asp.net中调用javascript自定义函数的方法(包括引入JavaScript文件)总结

    通常javascript代码可以与HTML标签一起直接放在前 端页面中,但如果JS代码多的话一方面不利于维护,另一方面也对搜索引擎不友好,因为页面因此而变得臃肿:所以一般有良好开发习惯的程序员都会把 ...

  9. jquery,js引入css文件,js引入头尾

    jquery,js引入css文件,js引入头尾 今天在项目中,需要把20多个页面加上头和尾部,头和尾是我写的,所以小师傅把这个工作交给我了. 我开始往里面加,先引入common.css,在body开始 ...

随机推荐

  1. java中构造代码块、方法调用顺序问题

    1. 继承的概念 继承在本职上是特殊——一般的关系,即常说的is-a关系.子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法. 2. 继承中的初始化顺序 从类的结构上而言,其 ...

  2. Pyhon 逻辑运算符

  3. 华硕ASUSPRO P5440UA笔记本电脑安装驱动

    领了一台公司的办公笔记本,按照套路重装了系统(win10),后面发现悲剧了 采坑1: 从华硕通过型号搜索下来的无线网卡(WIFI) 驱动无法安装(双击启动没有反映) 解决:通过设备Id上google搜 ...

  4. v4l2框架分析

    参考:https://www.cnblogs.com/fengong/p/4424823.html    http://www.cnblogs.com/fengong/p/4424895.html 一 ...

  5. bootloader介绍

    Bootloader是系统加电后运行的第一段软件代码,主要任务就是将内核映像从硬盘读到RAM中,然后跳转到内核的入口点去运行,也即开始启动操作系统. 简单的说,Bootloader就是在操作系统运行内 ...

  6. bzoj5109: [CodePlus 2017]大吉大利,晚上吃鸡!

    Description 最近<绝地求生:大逃杀>风靡全球,皮皮和毛毛也迷上了这款游戏,他们经常组队玩这款游戏.在游戏中,皮皮 和毛毛最喜欢做的事情就是堵桥,每每有一个好时机都能收到不少的快 ...

  7. Ubuntu16.04下安装Hyperledger Fabric 1.0.0

    系统环境 * Ubuntu: 16.04 * Go: 1.9.2 * NodeJS: v6.12.0 * Docker: 17.09.0-ce * HyperLedger Fabric: 1.0.0 ...

  8. YoLov3训练自己的数据集

    工具:labelimg.MobaXterm 1.标注自己的数据集.用labelimg进行标注,保存后会生成与所标注图片文件名相同的xml文件,如图.我们标注的是井盖和路边栏,名称分了NoManhole ...

  9. Unity Graphics(一):选择一个光照系统

    原文链接 Choosing a Lighting Technique https://unity3d.com/learn/tutorials/topics/graphics/choosing-ligh ...

  10. java 通过jmx获取active mq队列消息

    一.修改active mq配置文件 修改\conf\activemq.xml,带下划线部分 <!-- Licensed to the Apache Software Foundation (AS ...