更好的接口
更自动化更稳定的前后端交互模式
- why?
- how?
why?
JavaScript本身的类型系统完全是runtime的
js是解释性语言(“非编译语言”又是不恰当的)
虽然有JIT可以很大程度优化速度问题
但一切质量问题都要线上来检验,这谁顶得住
前端视角
async askForSomething function () {
const result = await askServer()
if (_.isString(result)) {
// 可能是字符串
return
}
if (_.isNumber(result)) {
// 也可能是数字
return
}
// 具体是类型依赖运行时的判断
if (_.isObject(result) && _.isFunction(someTool.alert)) {
// 所谓防御型编程就是要在运行时把所有的准备安排明白
// 从而让调用和取值安全
someTool.alert(result.msg || '未知消息')
return
}
}因为JavaScript变量类型的不确定,所以开发者需要依赖繁琐的防御型编程,无趣的类型转化,来保证变量可以正确的被使用,从而减少runtime error
- 缺字端了
- 类型错了
- undefined is not a function
- cannot read property 'xxx' of undefind
- 😂...
否则
变量来源
- 外部([网络, 存储] + unserializer)
- 内部(var,let,const...)
由于JavaScript本身没有帮助开发者解决这个问题,特别是外部变量,规模大,语言相关(java vs golang?),不确定性更大,一般来说解决外部变量的类型安全性问题,内部变量的安全性问题也就迎刃而解了。更好的接口,解决的其实就是外部数据在JavaScript中的类型安全性问题。
从根基上稳固类型安全。
how?
如果将前后端当做一个整体,那么接口调用的本质即为跨平台调用。在微服务体系中,IDL作为中立桥接,让平台间的调用更容易,更可靠。我也曾在前后端交互中使用IDL。
thrift IDL
java client stub code
go client stub code
py thrift service
- thrift
- protobuf
- ...
项目实践
- 通过IDL + 内部工具
- 后端:生成api架子,可直接开写逻辑
- 前端:生成接口调用,可直接调用
后端step1
编写protobuf描述文件



后端step2.1
生成api架子
#!/bin/bash
export GOOS=darwin
export GOARCH=amd64
binpath="./bin"
echo "binpath="$binpath
if [ -n "$GOPATH" ]; then
# ...
# proto编译器protoc,有一个插件体系
# 分别调用不同的插件,以生成不同的代码
# protoc-gen-go用来生成golang的stub代码
# protoc-gen-tttool用来生成自定义架子,这个是内部自研的工具
${binpath}/protoc -I ./example/proto --plugin=${binpath}/protoc-gen-go # 一些参数
${binpath}/protoc -I ./example/proto --plugin=${binpath}/protoc-gen-tttool # 一些参数
else
echo "GOPATH is needed!"
fi
后端step2.2
看看架子
func Add(
c *context.Context,
request pb_gen.ReqOfAdd
) (resp *pb_gen.RespOfAdd, err error) {
// Developer fill here
return &pb_gen.RespOfAdd{}, nil
}前端step1.1
解析protoc-gen-go生成的文件,生成前端调用代码,先看看生成的go数据结构


前端step1.2
解析方式是golang ast加模板,核心是生成TypeScript的结构定义。ts帮助开发者在compile阶段提前发现错误,以减少runtime error,再加上现代ide,可提升防御型编程的体验。
declare interface ITSUserInfo {
avatar_uri: string;
child: ITSChild | null;
...
gender: ITSGender;
klass_info: (ITSKlassInfo | null)[] | null;
...
}- compile phase(前端文明时代👏)
- runtime phase(文明的背后依然荒蛮)
拓宽思路
compile phase

typescript
- flow
- eslint
- ...
TypeScript运行在compile阶段,runtime阶段线上跑的仍然是JavaScript,ts对runtime错误束手无策,所以可以酌情加入runtime类型检测。
runtime phase

在绩效系统使用了mobx-state-tree,types.model限制了runtime阶段数据的类型,从根源掐住。Instance+typeof限制了compile阶段的类型,实现两个阶段校验的对称。



mobx-state-tree + typescript
方式很多,但
万变不离其宗
唯一的接口定义 + 稳定的工具链 =
- 更自动化
- 自动的服务端接口代码
- 自动的客户端调用代码
- 更稳定
- 稳定的compile phase
- 稳定的runtime phase
所谓的接口定义,唯一即可,甚至一段强类型语言的有规律的代码段,也可以用ast读取结构加模板生成目标代码。
问题本源
JavaScript本身的变量类型系统完全是runtime的
工程化中的意义
提升项目效率
提升项目质量
方式
IDL + 工具链
思考角度
compile phase
runtime phase
Q&A
更好的接口
By shaomingquan
更好的接口
- 362