在软件开发的当下,存在一种针对API的查询语言,它是由开发者共同构建的开源项目,宛如一股清新的泉水,给前后端开发注入了新的生机与机遇。提及这个话题,立刻引起了众多开发者的兴趣,因为他们在开发过程中不可避免地会面临新的挑战和难题。
开发背景与需求痛点
在传统的前端后端开发模式里,交互形式较为固定。许多项目往往依赖HTTP请求来访问服务端接口。以许多公司内部开发为例,即便地点就在办公室,开发人员也整天得应对这种固定的交互问题。每当项目需求有所变动,比如要新增功能或调整显示内容,开发人员就得去修改现有接口或创建新接口。这样做既费时又容易出错。在产品经理要求紧迫的情况下,开发人员需加班加点以满足需求。数据交互格式的不确定性使得文档维护变得相当困难。随着服务端接口数量的增加,维护和扩展的成本也在持续上升。
{
data:{
title:'商品的标题',
content:'商品的描述内容',
special:'商品特点',
price:'商品价格',
image:'商品的图片'
}
}
这种情况长期影响着众多开发者,导致他们的工作效率不高,同时也很可能激起团队内部的冲突。前端觉得后端不够给力,而后端则认为前端的要求过多。
传统商品详情页交互方式
apollo: {
goods: {
query() {
return gql`{
goods(infoId:"${this.infoId}"){
title
content
price
image
}
}`
}
},
store: {
query() {
return gql`{
store(infoId:"${this.infoId}"){
store
}
}`
}
},
seller: {
query() {
return gql`{
seller(infoId:"${this.infoId}"){
name
age
}
}`
}
}
}
以商品详情页面为例,在传统开发模式中,这里需要处理特定的业务规则。通常,后端会向前端提供一个接口以获取商品详细信息。比如在一个电商项目里,开发团队确定了数据格式后,后端在接收到前端请求时,会以既定格式返回数据。这些数据涵盖了商品的各项信息,诸如标题、描述、特点、价格和图片等。数据传回前端后,前端需对其进行处理和展示,同时页面还需展示卖家信息。若前端发现页面需补充或修改信息,如同案例所示,前端需与后端协商调整接口。这无疑增加了沟通成本,延长了项目周期,可能导致项目进度延迟。
新查询语言带来的改变
这种新型的查询语言带来了出乎意料的转变。它不依赖于特定的数据库或存储引擎,而是直接利用现有的代码和数据。一旦服务端部署完成,前端采用vue框架,数据请求过程就变得简单许多。这种查询语言与SQL类似,但它是针对API设计的。只需定义所需数据,其他问题无需费心。例如,一家小型创业公司开发电商平台时,采用这种查询语言,开发人员可以迅速完成数据调用,无需持续等待后端修改接口。
$ npm install --save egg-graphql
// config/plugin.js
exports.graphql = {
enable: true,
package: 'egg-graphql',
};
//开启 cros 跨域访问
exports.cors = {
enable: true,
package: 'egg-cors'
}
开发者现在可以根据需求自由获取信息,不再必须完全依赖后端接口,这样一来,他们的工作效率必然会有显著提升。
//config/config.default.js
// graphql路由
config.graphql = {
router: '/graphql',
// 是否加载到 app 上,默认开启
app: true,
// 是否加载到 agent 上,默认关闭
agent: false,
// 是否加载开发者工具 graphiql, 默认开启。路由同 router 字段。使用浏览器打开该可见。
graphiql: true,
// graphQL 路由前的拦截器
onPreGraphQL: function*(ctx) {},
// 开发工具 graphiQL 路由前的拦截器,建议用于做权限操作(如只提供开发者使用)
onPreGraphiQL: function*(ctx) {},
}
// cors跨域
config.cors = {
origin: '*',
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS'
}
查询语言的具体应用方法
//config/config.default.js
exports.middleware = [ 'graphql' ];
这种查询语言有特定的使用规则。在app目录下,有一个专门的查询入口——app//query/,所有查询对象都需要在这里明确指定。每个查询对象在入口中提及后,都需要在相应文件夹中创建对应的子文件夹,就像上文中的goods,就需要在app/文件夹中设立相应的子文件夹。在定义字段时,还需标注其类型,这显示了该查询语言对强类型支持的特点。比如在企业级项目开发中,开发者必须遵循这些规定,以确保数据的准确调用。
├── app
│ ├── graphql //graphql 代码,所有和graphql相关的代码都在这里,已经在前面做好了配置
│ │ └── common //通用类型定义,graphql有一套自己的系统类型,除此之外还可以自定义
│ │ | |── scalars //自定义类型定义
│ │ | | └── date.js // 日期类型实现
│ │ | └── resolver.js //合并所有全局类型定义
│ │ | └── schema.graphql // schema 定义
│ │ └──goods // 商品详情的graphql模型
│ │ └── connector.js //连接数据服务
│ │ └── resolver.js //类型实现,和goods中schema.graphql定义的模型相对应,是其具体的实现
│ │ └── schema.graphql //schema 定义,在这里定义商品详情数据对象
│ │ └──store // 库存的graphql模型
│ │ └── connector.js //连接数据服务
│ │ └── resolver.js //类型实现
│ │ └── schema.graphql //schema 定义,在这里定义商品详情数据对象
│ │ └──seller // 卖家信息的graphql模型
│ │ └── connector.js //连接数据服务
│ │ └── resolver.js //类型实现
│ │ └── schema.graphql //schema 定义,在这里定义商品详情数据对象
│ │ └──query // 所有的查询都会经过这里,这里是一个总的入口
│ │ └── schema.graphql //schema 定义
│ ├── service
│ │ └── goods.js //商品详情的具体实现
│ │ └── store.js //库存的具体业务逻辑
│ │ └── seller.js //卖家信息的具体业务逻辑
│ └── router.js
.js文件是数据类型实现所需依赖的关键文档,其中.js文件在数据连接中承担着实际操作的角色,同时还能优化性能,减少数据访问的频率。在代码的具体实现中,比如this.ctx..goods,这是从app/文件夹中的goods.js文件导入的方法对象,它展示了获取数据的业务流程。获取数据的方式有多种,既可以直接从数据库中提取,也可以通过http请求从现有接口中获取。
对开发人员的积极影响
#定义查询对象,在graphql里的注释使用#号
type Query {
goods(
#查询条件,相当于接口的入参商品id
infoId: ID!
):Goods #Goods是在app/graphql/goods/schema.graphql中定义的商品详情
}
对开发者而言,这种查询语言犹如及时雨。以往因接口问题耗费的大量开发时间得以缩减,包括那些频繁与团队成员沟通修改接口的时段。无论是小团队的小项目还是大公司的系统开发,开发者都能更集中精力处理业务逻辑。
数据证明可行时,开发者可尽情施展才华,这无疑将大幅提高开发效率。同时,前端得以突破后端束缚,自行实现众多功能。这无疑在一定程度上增强了前端在项目中的自主地位和影响力。
# 商品
type Goods {
# 流水号
infoId: ID!
# 商品标题
title:String!,
# 商品内容
content:'String!,
#商品特点
special:'String!,
#商品价格
price:'nt!,
#商品图片
image:'String!,
}
未来展望
这种查询语言已在部分项目中小试牛刀,尽管尚未广泛流行。然而,它针对现有接口繁杂、维护困难、扩展费用高、数据格式难以预测、文档不易维护等问题,提供了较为完备的解决方案。随着时间的流逝,人们对于开发效率的要求日益提升,相信不久的将来,这种查询语言将成为开发界的重要组成。开发者们,你们觉得这种查询语言要多久才能被多数企业采纳?欢迎留言、点赞及转发此文。
'use strict'
module.exports = {
Query: {
goods(root, {infoId}, ctx) {
return ctx.connector.goods.fetchById(infoId)
}
}