要开发出高质量的前端组件,必须知道这些姿势,后端应用才有可能。随后几年,Node.js受到了JavaScript社区的热烈追捧,前端行业也由此进入了工程化、全栈化的新时代。回顾历史,总会让人心潮澎湃。在这波浪潮中,无数人和项目在这座丰碑上刻下了自己的名字:React、Vue、Yeoman、RequireJS、Backbone、Antd、Webpack、Mocha、Eslint等等。在这些知名项目的光辉照耀下,我们可能忽略了NPM,它为Node.js生态的繁荣构建了不可磨灭的贡献,是当之无愧的肱骨。NPM诞生于2010年1月,其使命是让Node.js社区更加繁荣。NPM致力于让JS程序员更轻松地发布和共享Node.js类库和源代码,并简化安装、更新和卸载模块的体验。从今天(2019年)的时间节点来看,NPM在社区的热度、模块数量、话题数量上都算得上是最好的,让其他语言的模块仓库望尘莫及。数据来源:moudlecounts既然NPM的生态如此成熟,按说开发者应该对NPM包的发布和维护非常熟悉,但事实真的如此吗?环顾FE,有很多同学没有分发过任何npm包,也有相当一部分分发过包的同学没有考虑过如何规范、高质量地发布一个包。现在NPM模组数量已经上升到100W。在这样一个JavaScript组件化发展的时代,我们除了要找到有用的组件,自然要懂得如何成为这个时代的一员。而第一步就是了解并掌握如何规范、负责地发布一个npm包?这是本文的主要内容。《蜕变》|2019年《十一月》,作者摄于玉岔峡谷。1.组件化思维发布人生第一个npm组件虽然只是在终端命令行输入npmpublish等待成功通知,但是从0到1的飞跃并不容易,这种行为背后的始作俑者是开发者的大脑开始萌发了基于组件的思维方式,开始思考什么是组件?为什么要发布组件?这些更深层次的问题。组件存在的最终意义是为了复用。只要一个组件满足被复用的条件并开始被复用,它的价值就开始产生。一个组件被重用的次数越高,传播的越广,它的价值就越大。要使组件的价值最大化,需要考虑以下几点:我应该写什么组件?组件提供什么功能?组件的适用性如何?在特定的业务系统或整个团队、公司或社区内?组件的生产过程是否规范、稳健且值得信赖?开发人员如何发现和识别组件?以上四点中,前两点是元器件生产必须考虑的问题;第四点是如何推广和运营组件,这是另外一个话题,本文不做讨论;第三点是开发人员的基本素质。它决定了开发者如何看待这个组件,也间接暴露了开发者的素质和可信度。2、组件开发的最佳姿态一个优秀的组件除了具有解决问题的价值外,还应该具备以下三个特征:生产和交付的标准化;优良的品质和可靠性;和高可用性。三者都满足,才能称得上是一个优秀的组件,否则会给用户带来各种困惑:BUG多、坑多、不稳定、文档太简单、不敢用等。2.1规范性2.1.1目录结构事实上,社区并没有一个官方的目录结构规范或大家都认同的目录结构列表,但是通过对知名项目的统计和分析可以得出结论,社区中一位优秀的开发者已经达成了一个非官方的目录结构列表以供共识:├─test//测试相关├─scripts//自定义脚本├─docs//文档,一般有很多文档,有多个md文档├─examples//可以运行的示例代码...行脚本入口文件├─website|site//antd、react等官网相关代码├─benchmarks//性能测试相关├─types|typings//typescript类型文件├─Readme.md//仓库介绍或组件文档└─index.js//入口文件上面的目录列表是一个比较完整的列表,大部分组件只需要根据自己的需要选择性使用一部分即可。适用于几乎所有组件的最小目录结构列表如下:├─test//测试相关├─src|lib//源代码目录├─Readme.md//仓库介绍或组件文档└─index.js//入口文件2.1.2配置文件这里的配置文件主要是指各种工程工具所依赖的本地化配置文件,以及一些需要在GitHub上声明开源的文件。比较完整的配置文件列表如下:├─.circleci//目录。circleci持续集成相关文件├─.github//目录。github扩展配置文件存放目录│├─CONTRIBUTING.md│└─...├─.babelrc.js//babel编译配置├─.editorconfig//跨编辑器统一代码风格├─.eslintignore//忽略eslint检测文件list├─.eslintrc.js//eslint配置├─.gitignore//git忽略列表├─.npmignore//npm忽略列表├─.travis.yml//travis持续集成配置文件├─.npmrc//npm配置file├─.prettierrc.json//prettier代码美化插件配置├─.gitpod.yml//gitpodcloudIDE配置文件├─.codecov.yml//codecov测试覆盖率配置文件├─LICENSE//开源协议声明├─CODE\_OF\_CONDUCT.md//贡献者行为准则└─...//更多配置以上配置可根据组件的实际情况和适用范围删除。各种场景下比较常见的列表如下:├─.babelrc.js//babel编译配置├─.editorconfig//跨编辑器统一代码风格├─.eslintignore//忽略eslint检测的文件列表├─.eslintrc.js//eslint配置├─.gitignore//git忽略列表├─.npmignore//npm忽略列表├─LICENSE//开源协议声明└─...//以上列表的更多配置去掉了那些配置去掉了只能在GitHub上使用,只关注仓库管理、合约交付管理、静态检查和编译等基础配置,适合团队内部和企业私有环境的组件开发。如果你想在GitHub上维护,你还需要继续从大列表中挑选更多的基础配置,这样你才能使用GitHub的众多强大功能。2.1.3package.json如果npm官方提供了一个包规范,那么这个规范就是package.json文件,它是包传递唯一不可或缺的文件。最精简的package.json文件之一是通过执行npminit生成的这个版本:{"name":"npm-speci-test",//组件名称"version":"1.0.0",//组件当前版本"description":"",//组件的一句话描述"main":"index.js",//组件的入口文件"scripts":{//工程脚本,使用npmrunxx来execute"test":"echo\\"Error:notestspecified\\"&&exit1"},"author":"",//组件的作者"license":"ISC"//的协议component}有这样一个版本的package.json文件,我们可以直接在这个目录下执行npmpublish操作。如果name这个名字在npm仓库中没有被占用,我们可以看到发包成功的反馈:$npmpublish\+npm-speci-test@1.0.0但是这些基本信息肯定是不够的。作为一个标准化的组件,我们还需要考虑:我的代码托管在哪里;其他人可以通过什么关键字在仓库中找到该组件;组件的运行依赖是什么;组件的开发依赖是什么;如果是命令行工具,就是入口文件;组件支持哪些节点版本和操作系统。一个更通用的package.json文件如下:{"name":"@scope/xxxx","version":"1.0.0","description":"description:xxx","keywords":"keyword1,keyword2,...","main":"./dist/index.js","bin":{},"scripts":{"lint":"eslint--ext./src/","test":"npmrunlint&istanbulcover\_mocha--test/--no-timeouts","build":"npmrunlint&npmruntest&gulp"},"repository":{"type":"git","url":"http://github.com/xxx.git"},"author":{"name":"someone","email":"someone@gmail.com","url":"http://someone.com"},"license":"MIT","dependencies":{},"devDependencies":{"eslint":"^5.2.0","eslint-plugin-babel":"^5.1.0","gulp":"^3.9.1","gulp-rimraf":"^0.2.0","istanbul":"^0.4.5","mocha":"^5.2.0"},"engines":{"node":">=8.0"}}name属性要考虑组件是public还是private。如果是public,先确认名字是否已经被占用。如果不是为了保险起见,可以先发一个空白版;如果是private,需要加上@scope前缀,同时需要确认名字是否已经被占用。version属性必须符合semver规范。简单的理解就是:第一个版本一般推荐使用1.0.0;如果当前版本有破坏性变化,不能向前兼容,考虑升级到第一位;如果有新特性和新接口,但可以向前兼容,考虑升级到第二位;如果只是bug修复、文档修改等不影响兼容性的改动,可以考虑升级到第三方。关键字将影响存储库中的搜索结果。最好固定主入口文件的位置。如果需要构建组件,建议设置为./dist/index.js。如果不需要构建,可以指定为根目录下的index.js。scriptsscripts通常由两部分组成:通用脚本和自定义脚本。无论是个人还是团队,都应该对通用的脚本进行规范,避免过于随意的脚本命名;自定义脚本可以灵活定制,例如:通用脚本:start、lint、test、build;自定义脚本:copy、clean、doc等。无论是私有环境还是公共环境都要加上repository属性,以便通过组件定位到源码仓库。如果author是一个人负责的组件,就用author,多人用contributor。更详细的package.json文件规范可以在npm-package.json中找到。2.1.4开发流程很多同学在开发组件的时候会直接使用master分支进行开发。如果他们觉得这个版本差不多可以发布了,他们会直接手动执行npmpublish,然后继续在master上做下一个版本。这是很不正常的,会出现很多问题,比如:正在开发一个比较大的版本,此时发现当前在线版本有一个重要的bug,急需修复;没有为每个发布的版本指定唯一的标签,以便回溯。git工作流有很多种,各有适合的场景和优缺点。大多数时候,组件的开发是个人行为,偶尔也是团队行为,所以不适合比较复杂的流程。我个人的观点是,如果是在GitHub上维护的开源组件,参考GitHub流程;如果是个人或者公司内部的私有环境,只要能保证多个版本并行,并且每个发布的版本都可以追溯,就可以在GitHub上使用Streamline流程,不区分feature和hotfix,统一使用分支开发,使用master作为线上分支和预发布分支,开发分支需要提前合并到master中,在master上review和单测后直接发布,并打上tag,省略拉取请求过程。2.1.5commit&&changelog一个组件从开发到发布通常要经历多次代码提交。如果能够在一开始就了解gitcommit的消息编写规范,并利用工具辅助低成本完成规范的执行,那么对于组件问题回溯和了解版本变更细节都会带来很大的好处。我们可能看到过Node.js项目的changelog文件:它非常规范的展示了当前版本的所有关键的Commit记录,每条commit记录的信息也非常完整,包括:commit、修改范围、修改说明、修改人和pullrequest地址。试想一下,如果没有很好的规范和工具来约束早期commit阶段,人工完成这项工作需要多长时间?目前社区使用最广泛的commitmessage规范是:ConventionalCommits,由AngularCommit规范演变而来,配备了非常全面的工具:从gitcommit命令行工具commitizen,到Changelog的自动生成文件,以及commitlint规范验证工具,覆盖面非常全面。2.2质量和可维护性开发组件的出发点是为了复用,其价值也体现在最大程度的复用上。团队内的组件可能在整个团队的多个系统之间复用;公司内部的通用组件可以降低整个公司的开发成本;React、antd等优秀的开源组件将为整个社区和行业带来巨大的价值。一个组件是否可以放心使用,最简单直接的标准之一就是它的质量。质量好坏,除了上手试用外,一般从几个方面来判断:是否有覆盖率高的单元测试用例?源代码是否有标准化的编码风格和语法检查?源代码是否使用类型系统?这些直接决定了开发者对该组件的评价。试想,如果开发一个公共组件,没有规范的开发流程和代码风格检查,也没有单元测试,随便发布一个有bug的版本。这时候,用户在第一次安装使用时就会报错,导致开发者对该组件产生强烈的不信任感,这种不信任感甚至会影响到作者本人。因此,一个规范合格的组件至少要做到两点,才能保证组件的质量:1)引入JavaScript代码检查工具,规范代码风格,降低出错概率;2)引入单元测试框架进行必要的单元测试。此外,类型系统(TypeScript)的加入将有助于组件提升代码质量和可维护性,是组件开发的推荐选择。2.2.1JavaScript检查工具JavaScript语言的第一个检查工具是前端大师DouglasCrockford于2002年发布的JSLint。在随后前端行业高速发展的十年中,两个检查工具JSHint和ESLint逐渐进化。关于这三个工具的演变史,可以参考尚淳在知乎上发表的一篇文章:《JS Linter 进化史》。本文不再赘述,我们可以通过谷歌趋势来简单了解一下这三个工具的热度,这里附上JSCS的对比:可以看出,在过去的一年里,全球用户在Google的热度情况,这张图很符合身处前端行业的感觉。所以在JavaScript检查工具的选择上,大家可以毫不犹豫的选择ESLint。在实践中使用ESLint有几点需要考虑:团队和个人都需要对配置规范达成理解和共识,这样配置才能沉淀为通用的脚手架和规范。对于不同的组件类型,比如react或者vue,都有自己独特的语法,需要特定的ESLint插件来支持,而与框架无关的组件,比如lodash,则不需要这些插件.因此,如何对配置进行分类和抽象,从而沉淀出多套配置规范,而不用每次开发一个组件都重新调整和修正配置。更常规的做法是根据应用端(浏览器、Node、通用、Electron……)和运行时依赖框架(React、VUE、Angular等)来配置组件。使用IDE插件自动修复以提高效率。如果是团队通用的规范,还需要形成一个规范变更的流程,这样当团队成员对规范有争议时,可以有一个固定的渠道来讨论、决定并最终落实到规范中去。规格。随着ESLint的引入,还需要考虑是否将ESLint加入验收流程,以及如何加入验收流程。2.2.2单元测试和覆盖率一直是业务项目要不要写单元测试的问题。不写是个人选择。互联网提倡敏捷开发,快速迭代在线试错,需求变化太快,前端代码写一个单元测试的成本可能不低于代码本身。但是组件的情况就完全不同了。组件是具有清晰边界和可预测效果的接口和功能的集合。而且,与业务类代码相比,组件的通用性更强,也就是说,它们不太可能随着业务的变化而变化。而组件的升级通常会对依赖组件的系统产生潜在的影响。每次发布版本都要对功能进行详细的回归测试,以保证发布版本的质量。由于组件的测试通常由开发者自己来保证,不会有专业的QA资源,单元测试是最好的解决方案。JavaScript的单元测试方案如此之多,呈现出百花齐放、百家争鸣之势。耳熟能详的如:Jasmine、Mocha、Jest、AVA、Tape等,每个测试框架都有自己独特的设计,有的是开箱即??用的一套完整的解决方案,有的非常简单,需要与其他库一起使用。其实这些框架没有绝对的好坏之分,如何选择完全取决于个人和团队的喜好。这里有一篇关于测试框架评估的文章,你不妨看看:《JavaScript unit testing frameworks: Comparing Jasmine, Mocha, AVA, Tape and Jest [2018]》。此外,我们还可以通过GitHub上的star数和Googletrends上的搜索量,一窥流行趋势。国内谷歌趋势数据:美国谷歌趋势数据:可以看出,Jest自2014年发布以来,增长势头最为强劲,仅用3年时间就超越了其他老对手,成为当前最热门的测试框架。除了测试框架的选择,还有一个重要的指标需要关注,那就是测试覆盖率。推荐使用nyc,可能很多同学用过一个库,名字特别:istanbul。这两个库以前的起源可以在下面的Issue中找到。https://github.com/istanbuljs...2.2.3类型系统如今的JavaScript已经不再是当初那个在浏览器里写动效和交互的少年了,它已经被用在了Web,Server,Desktop,App,它有在物联网等众多场景中证明了它的价值,证明了它可以用来解决复杂的问题。事实上,JavaScript是通过将许多优秀的优质组件和框架有机地组合在一起来提供这种能力的。但值得深思的是,JavaScript采用了动态弱类型设计,过于灵活的类型转换往往会带来一些不好的事情。想象这样一个场景:调用一个组件的API函数,但是不知道函数的参数类型,只能自己写代码;优化和重构一个组件的重要功能的参数,但你无法评估影响。这些问题在强类型语言中有很好的解决方案。许多可能的错误会在编译时被发现,许多变化的影响会被IDE立即警告。事实上,越来越多的知名组件库已经开始引入强类型系统来帮助提高代码质量和可维护性,例如Vue.js、Angular、Yarn、Jest等。如果你想让自己有类型思维并做出组件具有更好的质量和可维护性,可以考虑将类型系统添加到组件的脚手架中。目前可用的为JavaScript添加强类型检查的解决方案包括FaceBook的Flow和微软的TypeScript。从目前的趋势来看,TypeScript是绝对的首选。2.3可用性组件的可用性主要是指站在组件使用者的角度来看待组件的使用体验:组件的文档是否完整、易读?组件暴露的API是否有详细规范的输入输出描述?有没有可以直接运行或者参考的Demo?该文件是否被认为是国际化的?2.3.1Documentation一份好的组件文档至少应该有以下内容结构:一句话描述组件是什么,解决什么问题#Usage//如何安装和使用,提供简单明了的例子#APIDocumentation//ProvidespecificationsanddetailsAPI接口文档,包括示例代码或示例链接#补充信息,如兼容性说明等//如果是浏览器端组件,最好添加兼容性支持;如果是Node端组件,还需要说明支持的Node.js版本范围#ChangeLog//各版本重要变更说明及commit链接#Contribution,contactauthor,License等//如果是组件想让别人参与贡献,需要有参与贡献的指引;否则,最好提供直接联系作者的方式。很多优秀的开发者可以很好的把控代码,但是对于如何写一个组件文档却有些苦恼。这是因为代码是给自己看的,文档是给用户看的,这两种思维方式有天然的区别。写文档的时候需要换位思考,甚至??可以把用户当成小白,尽可能为小白考虑。这样可以提高文档的可读性,减少上手的难度和使用的挫败感。2.3.2DEMO对于一个组件来说,Demo的重要性不言而喻。还记得Node.js中经典的几行代码来创建一个http服务器的签名演示吗?可以说,它几乎成了Node.js的招牌和广告。组件的demo和文档都是对易用性负责的,但是应该相互关注,相辅相成。Documentation侧重于介绍关键信息,Demo侧重于交付具体应用场景下的使用。对于比较小的组件,两者可以合二为一;对于代码量多、使用方法和场景多的demo代码,建议在examples目录下针对每个场景写一个可以直接运行的demo。Epilogue组件是开发者创建的产品,第一次发布只是这个产品生命周期的开始。如何让更多的用户关注它,成为它的忠实用户,甚至参与投稿,是接下来要解决的问题。关于此主题的本文到此结束。欢迎您在下方留言,分享您在组件推广方面的经验和技巧。由于本人能力所限,文中难免有一些不正确的解读,还望大家交流指正!想了解更多技术知识欢迎在评论区留言或者私信我!欢迎关注公众号:fkdcxy疯狂程序员,发现更多技术知识!
