当前位置: 首页 > Web前端 > JavaScript

走进开源项目——urlcat源码分析

时间:2023-03-27 16:08:55 JavaScript

在《走进开源项目 - urlcat》中,对项目进行了整体分析,对如何开源有了更深的理解。本文将进一步研究urlcat源码。该项目具体做什么?//常规写法constAPI_URL='https://api.example.com/';functiongetUserPosts(id,blogId,limit,offset){constrequestUrl=`${API_URL}/users/${id}/blogs/${blogId}/posts?limit=${limit}&offset=${offset}`;//发送HTTP请求}//常规写法2constAPI_URL='https://api.example.com/';函数getUserPosts(id,blogId,limit,offset){constescapedId=encodeURIComponent(id);constescapedBlogId=encodeURIComponent(blogId);constpath=`/users/${escapedId}/blogs/${escapedBlogId}`;consturl=newURL(path,API_URL);url.search=newURLSearchParams({limit,offset});constrequestUrl=url.href;//发送HTTP请求}//使用urlcatconstAPI_URL='https://api.example.com/';functiongetUserPosts(id,limit,offset){constrequestUrl=urlcat(API_URL,'/users/:id/posts',{id,limit,offset});//发送HTTP请求}源码一共267行,其中注释占近110行,代码只有157行。注释和代码接近1:1,接下来我们逐条分析。第一段importqs,{IStringifyOptions}from'qs';//eslint-disable-next-line@typescript-eslint/no-explicit-anyexporttypeParamMap=Record;exporttypeUrlCatConfiguration=Partial&{objectFormat:Partial>}>本项目基于qs项目,使用typescript开发,定义了2种类型,几种不同的知识点类型,Recode,Partial和Pick太好了。interface和type的区别是一样的:都可以描述对象或者函数,都可以使用extends来扩展区别:type可以声明基本类型别名,联合类型,元组,但是interface不能//基本类型别名typeName=字符串|number;//联合类型interfaceCommon{name:string;}interfacePersonextendsCommon{age:T;性别:字符串;}类型人={年龄:T;性别:字符串;}&Common;typeP1=Person|People;//元组类型P2=[Person,People];结合typeofconstname="小明";typeT=typeofname;Record的用途Reacord是TypeScript的实用程序类。//常规写法interfaceParams{[name:string]:any;}//高级写法typeParams=RecodePartial的使用将传入的属性变成了一个可选的interfaceDataModel{name:stringage:numberaddress:string}letstore:DataModel={name:'',age:0,address:''}functionupdateStore(store:DataModel,payload:Partial):DataModel{返回{...store,...payload}}store=updateStore(store,{name:'lpp',age:18})Pick的作用是从类型Type中选择一组属性,形成一个新的类型返回。属性集由Keys限定,其中Keys是字符串或字符串的并集。interfacePerson{name:stringage:numberid:string}//幼儿没有idtypeToddler=Pick第二段/***使用基本模板和指定参数构建URL。**@param{String}baseTemplate包含零个或多个:params的URL模板*@param{Object}params一个对象,其属性对应于基本模板中的:params*。未使用的属性成为查询参数。**@returns{String}一个替换了路径参数并附加了查询参数的URL**@example*```ts*urlcat('http://api.example.com/users/:id',{id:42,search:'foo'})*//->'http://api.example.com/users/42?search=foo*```*/导出默认函数urlcat(baseTemplate:string,params:ParamMap):string;/***连接基本URL和使用“/”作为分隔符指定的路径。*如果“/”出现在任一参数的串联边界处,则将其删除。**@param{String}baseUrlURL的第一部分*@param{String}路径URL的第二部分**@returns{String}连接的结果**@example*```ts*urlcat('http://api.example.com/','/users')*//->'http://api.example.com/users*```*/exportdefaultfunctionurlcat(baseUrl:string,path:string):string;/***连接基本URL以及使用“/”作为分隔符指定的路径。*如果“/”出现在任一参数的串联边界处,则将其删除。*将路径参数替换为@seeparams对象的属性,并附加*路径中未使用的属性作为查询参数。**@param{String}baseUrlURL的第一部分*@param{String}pathURL的第二部分*@param{Object}params具有对应于基本模板中的:params*属性的对象。未使用的属性成为查询参数。**@returns{String}替换路径参数并附加查询参数的URL**@example*```ts*urlcat('http://api.example.com/','/users/:id',{id:42,search:'foo'})*//->'http://api.example.com/users/42?search=foo*```*/exportdefaultfunctionurlcat(baseUrl:string,pathTemplate:string,params:ParamMap):string;/***连接基本URL和使用“/”作为分隔符指定的路径。*如果“/”出现在任一参数的串联边界处,则将其删除。*将路径参数替换为@seeparams对象的属性,并附加*路径中未使用的属性作为查询参数。**@param{String}baseUrlURL的第一部分*@param{String}pathURL的第二部分*@param{Object}params具有对应于基本模板中的:params*属性的对象。未使用的属性成为查询参数。*@param{Object}configurlcat配置对象**@returns{String}替换路径参数并附加查询参数的URL**@example*```ts*urlcat('http://api.example.com/','/users/:id',{id:42,search:'foo'},{objectFormat:{format:'RFC1738'}})*//->'http://api.example.com/users/42?search=foo*```*/导出默认函数urlcat(baseUrlOrTemplate:string,pathTemplateOrParams:string|ParamMap,maybeParams:ParamMap,config:UrlCatConfiguration):string;导出默认函数urlcat(baseUrlOrTemplate:string,pathTemplateOrParams:string,stringmaybeParams:ParamMap={},config:UrlCatConfiguration={}):string{if(typeofpathTemplateOrParams==='string'){constbaseUrl=baseUrlOrTemplate;constpathTemplate=pathTemplateOrParams;constparams=maybeParams;返回urlcatImpl,pathTemplate(,baseUrl,config);}else{constbaseTemplate=baseUrlOrTemplate;constparams=pathTemplateOrParams;返回urlcatImpl(baseTemplate,参数,未定义,配置);重载声明+一个函数实现,它的作用是保证函数调用时,函数的参数和返回值必须兼容所有的重载例如下图中,重载函数类型中不存在第三个参数类型。第三段下面的代码是核心。作者通过职责分离简化了核心方法的代码。//核心方法functionurlcatImpl(pathTemplate:string,params:ParamMap,baseUrl:string|undefined,config:UrlCatConfiguration){//第一步path('/users/:id/posts',{id:1,limit:30})返回“/users/1/posts”并限制:30const{renderedPath,remainingParams}=path(pathTemplate,params);//第二步移除Null或Undefined属性constcleanParams=removeNullOrUndef(remainingParams);//第三步{limit:30}tolimit=30constrenderedQuery=query(cleanParams,config);//第四步拼接返回/users/1/posts?limit=30constpathAndQuery=join(renderedPath,'?',renderedQuery);//第五步,当baseUrl存在时进行全url拼接returnbaseUrl?joinFullUrl(renderedPath,baseUrl,pathAndQuery):pathAndQuery;这个轮子变得更好了。通过这个项目,我也发现了自己在TypeScript方面的不足,继续学习,再接再厉。参考文章玩转TypeScript工具类型(上)你不知道的高级TypeScript类型请不要误用TypeScript重载函数类型扩展阅读玩转TypeScript工具类型(中)玩转TypeScript工具类型(下)