初探 Reason 与 GraphQL

1 引言

2018 年了,Reason 生态发展了不少,而且正好看到一篇文章的作者也抱着这种心态尝鲜 React + graphql,索性调研一下,看看这套前沿的方案是否有落地对可能性。

2 内容概要

一切皆模块

在 reason 中,一切皆模块,而且不需要手动申明导出与引用,这个是 js 的痛点。以下面的代码为例:

 
  1. open Data;
  2. let typeDef = {|
  3. type Author {
  4. id: Int!
  5. firstName: String
  6. lastName: String
  7. posts: [Post] # the list of Posts by this author
  8. }
  9. |};
  10. type resolvers = {. "posts": Js.Array.t(post)};
  11. let resolvers = {
  12. "posts": (author: Data.author) =>
  13. Js.Array.filter((post) => post##authorId === author##id, posts)
  14. };

第一行的 open 类似 js 中的 import,不同的是,js 中需要通过 Data.post 访问对象,而 reason 可以直接访问 post。不过也可以补全引用,比如 Data.author

在定义 graphQL 类型时,graphql-tools 允许通过 [Post] 的语法将文章对象关联到作者。

内置不可变数据类型检测

reason 中,一切类型都是 immutable 的,如果使用如下代码直接修改 post.votes,则会报错:

 
  1. Mutation: {
  2. upvotePost: (_, { postId }) => {
  3. const post = find(posts, { id: postId });
  4. if (!post) {
  5. throw new Error(`Couldn't find post with id ${postId}`);
  6. }
  7. post.votes += 1;
  8. return post;
  9. },
  10. },

可以通过 ref 告诉编译器,votes 可能是 mutable 的:

 
  1. type post = {. "id": int, "authorId": int, "title": string, "votes": ref(int)};

最后作者介绍了如何通过 apollo-server 搭建后端代码,与 reason 结合使用。

我试了下,真的非常方便,后端定义好接口,会自动生成一份在线文档供前端查询,完全屏蔽了接口这一层,只要搜索要查询的元素即可。

3 精读

graphql

前端后沟通成本一直是个问题,以至于很多团队想做一个 “接口查询平台” 之类的系统。

当然,无论是解析后端代码也好,平台录入也好,还是 mock 平台反推,都不太理想:

解析后端代码,工作量比较大,而且还需要约定一些格式,其实越做越像 graphql,投入的话还不如考虑使用 graphql。

一条条接口录入方案是可行的,技术成本也几乎为零,但问题是后续代码变动会导致平台与实际接口不一致,或者某些项目甚至绕过了接口录入,导致一些接口游离在平台之外,无法聚合管理。

先通过 mock 平台联调,再读取 mock 平台数据,生成接口列表同样存在后端代码变动导致 mock 结构过期的问题。

如果不考虑需求变动,后端采用 graphql 其实是成本最小的选择,其一是类似 apollo-server 这类框架做了一个 IDE 供查询实体,同时绕过了接口,直接暴露数据,效率更高。其二是可以做到代码变动后文档实时同步,只要后端代码更新,文档也会自动更新。

不过对于后端代码并不掌握在前端的团队来说,如果不推动后端改造成 graphql,是无法享受到这个好处的,这时如果搭建一个 node 版 graphql 桥梁,那又如何衔接这个桥梁与后端呢?所以使用 graphql 的若不是第一手后端代码,使用后也不会有多少效果。

更多细节可以访问 GraphQL and Relay 浅析,那篇是基于 relay 的,现在 apollo-server 看上去是更轻量级的方案。

reason

最近的 3.0 版本使用 JavaScript 的 application/abstraction 语法代替了 OCaml 的语法,看上去稍微顺眼一些了:

 
  1. myFunction(arg1, arg2) // 3.0 语法
  2. myFunction arg1 arg2 // 2.0 语法

能看出来 reason 在往 js 开发社区靠,不过大部分语法对 js 开发者都比较陌生,相比于 typescript,跳跃性有点太大了。

reason react

使用 reason 写一个 react 组件是这样的:

 
  1. let component = ReasonReact.reducerComponent("Greeting");
  2. let make = (~name, _children) => {
  3. ...component,
  4. initialState: () => 0, /* here, state is an `int` */
  5. render: (self) => {
  6. let greeting =
  7. "Hello " ++ name ++ ". You've clicked the button " ++ string_of_int(self.state) ++ " time(s)!";
  8. <div>{ReasonReact.stringToElement(greeting)}</div>
  9. }
  10. };

~name 称为 Labeled Arguments,也就是,调用函数时,可以无视顺序,显示指定入参名:make(~name=5)initialState 对应 reactjs 中 state,其他与 reactjs 都很像。

reason react 更新 state

相比 react 的 setState,reason react 提供了 reducer 支持,这里可以类比到 redux:

 
  1. let make = (_children) => {
  2. ...component,
  3. initialState: () => {count: 0, show: false},
  4. reducer: (action, state) =>
  5. switch (action) {
  6. | Click => ReasonReact.Update({...state, count: state.count + 1})
  7. | Toggle => ReasonReact.Update({...state, show: ! state.show})
  8. },
  9. render: (self) => {
  10. let message = "Clicked " ++ string_of_int(self.state.count) ++ " times(s)";
  11. <div>
  12. <MyDialog
  13. onClick={_event => self.send(Click)}
  14. onSubmit={_event => self.send(Toggle)}
  15. />
  16. {ReasonReact.stringToElement(message)}
  17. </div>
  18. }
  19. };

除了类型提示支持模式匹配(ts 也支持了)比较完美之外,其他和 redux 还真没啥区别。

至于 immutable 特性,reason 本身也只支持 immutable 检测而已,同时支持了结构语法,可以较为方便进行 immutable 计算(es 也支持了)。

如果想在复杂场景深入使用 immutable,可以看看这个 Reason + BuckleScript bindings to Immutable.js

4 总结

graphql 很惊艳,但如果不能应用到后端第一手代码就没什么用。

reason 整体看上去比初版 react + redux 生态强大了太多,但是与现在的前端生态链 typescript + react + redux* 最新特征比起来,唯一惊艳的地方,就是对 ocaml 用户较为友好,另外在各大支持编译到 js 语言,纷纷支持 Assembly 编译后,这些语言更加趋同了,相比之下 ts 更适合用在生产环境。

5 更多讨论

讨论地址是:精读《初探 Reason 与 GraphQL》 · Issue #56 · dt-fe/weekly

如果你想参与讨论,请点击这里,每周都有新的主题,每周五发布。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值