图片110
类型检查和智能提示。
作为SDK,我们的目标是减少用户查看文档的时间,所以我们需要提供一些类型的检查和智能提示。一般来说,我们的做法是提供Jsdoc。大多数编辑器可以提供快速生成Jsdoc的方法。我们常用的vscode可以使用documenthis。
图片
另一种方法是使用Flow或Typescript。选择Typescript的主要原因是自动生成的Jsdoc比较原始,我们还需要在上面编辑,所以Jsdoc的维护和代码开发是分开的,代码更新往往会发生,Jsdoc忘记更新。
此外,在开发过程中,我们无法享受SDK开发的类型检查等重要特征。Typescript可以减少我们的错误和调试时间。另一方面,本次开发的SDK在提供时会进行相对简单的压缩,以确保引入后的体积,因此我们希望压缩Jsdoc,Typescript可以将declaration设置为true单独的d.ts文件,通过tsconfig。json。
提示SDK:
图片
最后,对于开发学生来说,强烈建议使用vscode提供//@ts-check注释,即使不使用typescript,也会通过一些类型的推导来检查代码的正确性,可以减少开发过程中的很多bug。
还有一个小技巧。如果您使用的图书馆没有提供智能提示,您可以通过NPM/yarn-D安装@types/{pkgname},这样您就可以在开发过程中享受vscode提供的智能提示,而-D安装在devdependencies中不会增加构建时的代码体积。
接口
既然提到了TypeScript,就提一下TypeScript的语法。基本类型不需要重复,ES6现在可以支持一些以前的高级语法。这里有一些常用但JavaScript开发者不习惯的语法。
很多人人开始使用Typescript时,他们会痴迷于使用any或默认any。建议在开发过程中打开tsconfig中的strict和noimplicany,以确保尽可能少地使用any。你知道,滥用any意味着你的类型检查没有实质性效果。
对于一些暂时无法确定内容的对象类型,可以使用{[key:string]:any},而不是直接使用any。这个接口可以在后期慢慢扩展,直到any完全消除。同时,typescript的类型支持继承。在开发过程中,接口可以拆卸,重复定义可以通过组合继承减少。
然而,接口也会带来一个小痛点。目前,vscode的智能提醒不能与接口很好地对应。当您输入相应的变量时,虽然它将是明亮的,但它只是一个定义名称的接口。没有办法直接看到接口中定义了什么。但当您输入接口中定义的key部分时,vscode会给您一个完整的key提示。虽然这对开发过程有点不友好,但vscode开发团队表示,这是他们故意设计的,因此一些必要的(重要的)参数可以直接用于API参数的基本类型,并将一些配置放入定义为接口的对象中。
枚举
您在代码中使用过:
constplatform=
ios:0,
android:1。
}
那你应该在Typescript中使用枚举:
enumplatform
ios,
android}
这样,在函数中,您可以为参数设置类型为number,然后将其传输到platformios。这样,枚举可以增加代码的维护性。它可以使用智能提示来确保您的正确输入,并且不再有魔法(magicnumber)。与对象相比,它确保了输入类型(您定义的对象可能有一天不再只是number类型的value),不再需要额外的类型判断。
装饰器
事实上,许多开发人员对装饰器既熟悉又陌生。如今,随着redux和mobx的流行,装饰器的调用在代码中很常见,但大多数开发人员并不习惯将其代码逻辑抽取成装饰器。
例如,在SDK的开发中,我们需要提供一些facade来兼容不同的平台(iOS、android或web),而facade将允许开发人员以插件的形式注册。SDK将保持注入对象。传统的使用方法是在使用函数后判断环境,然后判断对象中是否有任何插件,然后使用插件。
事实上,插件是一个拦截器,我们只需要阻止真正的函数运行,一般逻辑是:
exportunctionfacade(env:number){
returnfunction(
target:object,
name:string,
descriptor:Typedpropertydescriptor。
){
letoriginalmethod=descriptor.value;
letmethod;
return
...descriptor,
value(...args:any[]
let[arg]=args;
let{param,success,failure,polyfil}=arg;//这部分可以自定义。
if(method=polyfill[env])
method.use(param,success,failure);
return;
}
originalmethod.apply(this,args);
}
};
};
}
SDK开发过程中经常遇到的另一件事是对许多参数的验证和重新包装,我们也可以用装饰器来完成:
exportunctionsnakeParam(
target:object,
name:string,
descriptor:Typedpropertydescriptor。
){
letcalback=descriptor.value!
return
...descriptor,
value(...args:any[]
let[arg...other]=args;
arg=convertobjectname(arg,convertnamemode.tosnake);
calback.apply(this,[arg...other]);
}
};
}
泛形
最简单的例子是,泛形可以根据用户的输入来确定输出。
(arg:T)
returnarg;
}
当然,它没有什么特别的意义,但它表明返回是基于arg的类型。在一般的开发过程中,你无法逃脱Promise或前面的TypedPropertyDescriptor,这需要类型的内部输入。不要急于使用any。如果您的后端返回类似于标准结构:
exporterfaceire
status:number;
string;
data:object;
}
然后可以这样使用Promise:
functionexample():Promise{
...
}
当然,泛形有很多先进的应用比如泛形约束,泛形创建工厂函数,已经超出了本文的范围,可以去官方文档了解。
构建
如果你的构建工具是Webpack,在SDK的开发中,尽量使用node调用(即webpack.run执行),因为SDK的构建往往会应对很多不同的参数变化。与纯配置相比,node方法可以更灵活地调整输入输出参数,也可以考虑使用rollup。rollup的构建代码更面向编程。
需要注意的是,ES6模块化可以用来构建webpack3和rollup的构建,这样在将业务代码引入您的SDK后,最终业务代码的体积可以通过解构引入来减少。如果您只提供commonjs包,则构建工具的treesharking无法生效。如果使用babel,请注意关闭module的编译。
另一种减少单包体积的方法是利用lerna在git仓库建立多个NPM包,比拆除仓库更方便使用公共部分代码,但也需要注意修改公共部分代码,不影响其他包。
事实上,对于大多数SDK来说,Webpack3的使用体验与rollup相似,更常用的插件几乎与同名对应。然而,rollup有两个优点。一是rollup的构建更加详细,rollup.rollup接受inputoptions生成bundle,也可以生成sourcemap,write生成output。在这个过程中,我们可以做一些详细的工作。
第二点是rollup。rollup将返回promise,这意味着我们可以使用async编写构建代码,而webpack.run仍然使用回调函数。虽然开发人员可以将其包装成promise,但我个人认为rollup的写作方法更好。
单元测试
上周,我的同事做了一个在线分享。我发现很多学生对单一测试非常感兴趣和困惑。在前端开发中,很难测试涉及UI的业务代码开发订单,但对于SDK来说,单元测试必须是准确的充分条件。当然,事实上,我不喜欢单一测试,因为单一测试通常很无聊,但如果我不单一测试,我肯定会被老司机教育_。
一般单测以mocha为测试框架,expect为断言库,使用nyc提供单测报告,一般单测如下:
describe(xxxapitest,function(){///注意,如果要用this调用mocha,不要使用箭头函数。
(6000);
it(xxx,done=>{>
SDK.file。
.chooseImage({
count:10,
cancel:()=>{{>{{{>{}
console.log(选择图片取消-');
}
})
.then(res=>{>{>
console.dir(res);
expect.to.be.an(‘object’);
expect.to.have.keys(ids);
expect(res.ids).to.be.an(array);
expect(res.ids).to.have.length.above(0);
uploadImg(res.ids);
();
});
});
});
同样,你也可以用Typescript写单测。当然,在执行过程中,没有必要编译它。我们可以直接为mocha注册ts-node。具体方法请参考writessfortespescriptswithmochandchai-intypescript!。但是,有一点需要提醒你,在写单测试时,尽量依靠文档而不是智能提示,因为你的代码错误可能会导致你的智能提示错误,你根据错误的智能提示写的单测也必须是。
nock可用于网络请求的模拟,需要在it前加一个beforeeeach方法:
describe(proxy)
()
(http://test.com)
.post(/test1)
.delay(200)
.reply(200,{/body。
test1:1,
2:2。
},{
‘server-id’:‘test’//header。
});
});
...
}
最后,我们可以用npmscript和nyc在mocha前获得我们的单测报告。
在这里,我还提到了Typescript中使用的几个小提示供参考。
小贴士:如何在非发包的情况下向内部库添加声明。
SDK在开发过程中依赖于内部NPM包。为了让NPM支持TypeScript调用,我们有几种方法:
将d.ts文件添加到原包中,然后发布。
发布@types包需要注意的是,NPM不支持@types/@scope]/{pkgname}如果是私库包,可以用@types/scope_{pkgname}。
本次使用的标注文件夹存储相应的d.ts文件,适用于开发。如果你认为你写的d.ts不够完美,或者这个d.ts文件目前只需要这个SDK,可以这样使用,在tsconfig.json中修改:
“baseUrl”:
“paths”:
“”:[/type/]
}
提示:如何处理不同类型的resolve和rejectpromise回调?
默认情况下,reject返回的参数类型为any,可能无法满足我们的需求。这里有一个解决方案,不是最好的,作为抛砖引玉:
interfaceIPromis
then(
onfulfilled:
|(value:T)=>Tresult1|Promiselike)
|undefined。
|null,,
:
|(reason:U)=>Tresult2|Promiselike)
|undefined。
|null
):IPromise;
(catch(
:
|(reason:U)=>Tresult|Promiselike)
|undefined。
|null):Promise;