开发一个高质量的前端组件,这些姿势一定要知

来源:http://www.chinese-glasses.com 作者:Web前端 人气:197 发布时间:2020-03-24
摘要:时间: 2019-11-10阅读: 140标签: 组件 目录 引言 包管理器 自动化构建及模块打包器 测试 Linter 持续集成 静态网站生成器 必备文档 目录规范 结尾 从今天(2019 年)这个时间节点来看,NPM无

时间: 2019-11-10阅读: 140标签: 组件

目录

  • 引言
  • 包管理器
  • 自动化构建及模块打包器
  • 测试
  • Linter
  • 持续集成
  • 静态网站生成器
  • 必备文档
  • 目录规范
  • 结尾

从今天(2019 年)这个时间节点来看,NPM 无论从知名度、模块数量、社区的话题数量来看,都算得上是一骑绝尘,将其他语言的模块仓库远远甩在了后面。

引言

在很早以前,开源软件的开发并不受待见,甚至受到排挤,普遍认为软件是商业性质的产物,开源没有任何利益,也没有一个很好的平台去分享自己的技术成果。如今,观念发生了转变,认为开源更易于发现问题,解决问题,促进技术交流,技术升级,技术创新,并且能推动公司业务的发展。随着大型同性交友平台Github的诞生,吸引了全球范围内无数的开发人员共同参与到开源项目中,开发者可以自由的创建项目,分享代码,共同合作完成一个项目。近几年,Github开源项目数量呈指数型增长,已然成为一个趋势,尤其是JavaScript脱颖而出,成为了毫无疑问的最热门语言,本文谈谈参与一个前端开源项目你需要了解并具备哪些知识。

数据来源: moudlecounts

包管理器

社区中几乎每一个开源项目都是采用NPM作为包管理器,当然也有一小部分使用了Bower包管理器,比较新的项目则采用Yarn来替代NPM管理模块。

图片 1

NPM & Yarn

NPM生成的入口文件是package.json,需要指定必要的基本信息,以及编写npm run快捷脚本命令。

  • name决定模块的名称,不允许与npm社区中的已发布模块同名,模块发布后,其他开发者可以通过该名称安装该模块。
  • version需遵循语义化版本规范,格式为[MAJOR].[MINOR].[PATCH],分别代表主版本,次版本,补丁版本,可以通过执行npm version命令自动更替版本。
  • main表示模块的入口文件,用户通过require('example')�加载该入口文件。
  • license声明版权类型,若是私有项目,则设置"license": "UNLICENSED"或者"private": "true",这种情况下,执行npm publish发布模块将会失败。
  • scripts用于编写自定义npm脚本命令,�通常上包括开发、编译、打包构建、测试、格式检查、文档构建等常用脚本命令,对于复杂脚本命令,可以单独写在shell脚本文件里,一般放在根目录下scripts文件夹中。

一个基本的package.json示例如下:

{
  "name": "example",
  "version": "1.0.0",
  "description": "This is an example project",
  "keywords": [
    "example"
  ],
  "main": "lib/index.js",
  "scripts": {
    "dev": "webpack --config webpack.dev.config.js",
    "build": "webpack --config webpack.prod.config.js",
    "lint": "eslint src",
    "test": "mocha *.test.js",
    "docs": "gitbook build",
    "clean": "rm -rf ./dist",
  },
  "dependencies": {
  },
  "author": "xxx",
  "repository": {
    "type": "git",
    "url": "https://github.com/xxx/xxx.git"
  },
  "license": "MIT"
}

NPM 的生态既已如此成熟,按说开发者对于 NPM 包的发布和维护应该非常熟悉才是,但事实真的是这样吗?环顾身边的 FE,没有发过任何 NPM 包的同学大有人在,已经发过包的同学也有相当一部分并未考虑过如何才算规范、高质量地发布一个包。

自动化构建及模块打包器

Gulp/Grunt属于自动化构建工具,是为了解决前端工程化中重复性、繁琐性的工作如配合第三方插件进行编译、压缩、合并、复制等文件操作。而Webpack/Rollup/Browserify定位是模块加载兼打包器,支持打包AMD/CMD/COMMONJS/ES6/HASTE/UMD等模块规范,随着生态的壮大,插件机制越来越丰富,越来越完善,在某些场景下已经完全可以取代Gulp/Grunt的工作。

图片 2

ES6+Webpack+Babel

老项目仍采用Gulp+Webpack等双机制构建,主流项目采用纯粹的Webpack结合各种loader插件及配置打包构建。如今,Webpack生态和Babel生态受到了越来越多开发者的青睐,具备可配置性高、可扩展性高、功能丰富强大等特点,开发者可以自由地制定插件及引用第三方插件,两者协同工作,可以尽情地体验ES6/7/8的开发。

如今 NPM 的模块数量已上升至 100W,在这样一个 JavaScript 组件化开发时代,除了能找到好用的组件,我们自然也需要了解如何才能成为创造这个时代的一员。而第一步就是要知道并掌握如何规范地、负责任地发布一个 NPM 包?

测试

�一个大型可靠的开源项目离不开测试,测试可以帮助开发人员及时发现问题,定位问题,提高代码质量,保证输出结果的正确性,避免缺陷回归,减少每次修改代码后人工review大量代码的成本,可分为测试驱动开发(TDD)和行为驱动(BDD)开发两种类型。另一方面,开发者不仅可以通过阅读文档还可以通过阅读测试用例来看懂程序的功能,尤其是当一个项目越来越庞大,逻辑越来越复杂,就需要根据测试用例通过与否、测试覆盖率等指标来衡量项目的质量。

图片 3

Karma+Mocha+Chai

Karma是一个测试执行过程管理工具,原名Testacular,该工具主要作用是让测试用例跑在所有主流浏览器如Chrome/Firefox或者无界面浏览器PhantomJS上,所以如果你的代码是设计在浏览器上的,那么使用Karma的测试结果更贴近真实环境。
测试框架有很多,从NPM下载量上来看,Mocha是下载量最高的测试框架,它可以同时运行在浏览器和Node端,Mocha本身不带断言库,扩展性高,支持异步测试,需要结合第三方断言库如Chai(支持TDD/BDD)实现单元测试。
测试覆盖率是一项衡量测试用例设计好坏的硬性指标,如可以使用覆盖率统计工具Istanbul结合测试框架Mocha进行测试,自动分析、记录并保存测试结果,生成测试报告,最后通过Coveralls上传至远程服务器。

这就是本文接下来的主要内容。

Linter

�一个大型可靠的开源项目也需要统一代码风格,强调代码质量,遵循同一套规范标准,例如缩进格式,是否使用分号等等,其直接影响了项目的可维护性,合作性。因此,就需要引入Linting工具来检查约束代码格式,各大公司也纷纷制定了自己的Javascript编码规范,如Google/Airbnb/Clean Code等等。当然,每个项目代码风格各不相同,萝卜青菜,各有所爱,这完全取决于项目维护者的偏好!

图片 4

King of Linter

目前Linter工具主要有ESLint/JSCS/JSHint/JSLint等等。ESLint毫无疑问已经成为最主流的Linter,提供了丰富的插件,可配置性很高,支持JSX语法,生态越来越完善。ESLint的优点是完全插件化,每一个规则都是一个插件并且你可以在运行时通过注释添加更多的规则,或者禁用某项规则。此外,执行eslint --fix命令可快速修复部分常见可自动修复错误,便捷快速强大,开源项目必备。

一、组件化思考

持续集成

持续集成在开源项目中不可或缺,每次代码提交或PR后都会在持续集成服务器上触发一次自动化测试构建部署,确保开发人员对项目源码作出的更改不会导致程序发生错误,减少了风险,增加了项目的可靠性。

图片 5

Travis CI

目前,前端项目中比较流行的持续集成工具有TravisCI/CircleCI/AppVeyor等等,配置文件一般是一个YAML文件。以Travis CI为例,需要在根目录下创建一个.travis.yml文件,并指定一些基本配置信息,例如语言、运行环境、执行脚本命令、启用缓存、分支选择等等,当指定分支代码提交后,持续集成环境将会读取该文件并执行相关指令,最后将是否构建成功的结果反馈给用户。

一个Travis配置文件示例:

sudo: false
language: node_js
node_js:
  - "4"
  - "5"
  - "6"
cache:
  directories:
    - node_modules
script:
  - npm run lint
  - npm run test
  - npm run build
after_script:
  - coveralls < ./coverage/lcov.info
branches:
  only:
    - master

发布人生中第一个 NPM 组件虽然只是在终端命令行中潇洒地敲下npm publish,静等成功通知即可,但这从 0 到 1 的跨越却并非易事。这个行为背后的始作俑者是开发者大脑中开始萌发组件化思维方式,开始去思考何为组件?为什么要发布组件?这些更深层次的问题。

静态网站生成器

每一个庞大的开源项目必然有详细的文档介绍和示例演示,但仅仅依靠README.md文件局限性太大,篇幅不够长,不足以直观地展示所有内容。另一个方案是手动编写一系列HTML文件,每一个HTML文件都包含一系列CSS文件、JS文件,最后部署到自己的服务器或Github Pages网站上。但是,当项目越来越庞大,越来越复杂,时间成本、人力成本将会大幅度增加,开发者不得不花大量精力去维护站点,无法专注于业务编码。�正由于这种原因,催生了静态网站生成器的快速发展与普及使用,你可以在StaticGen上找到目前较为流行的静态网站生成器。

图片 6

Jekyll

静态网站生成器都有一些共同特点,支持markdown格式,更好得适应文档的编写,将网站组件化、模板化,复用公共组件,消除重复组件,并且包含一个资源管道,用于处理资源编译、转译、打包、压缩。除此之外,静态网站生成器还会提供一个命令行功能用于在本地服务器构建网站进行实时预览。在项目中,一般把文档放在根目录下docs文件夹中。

组件的存在的终极意义是为了复用,一个组件只要具备了被复用的条件,并且开始被复用,那么它的价值才开始产生。组件复用的次数越高、被传播的越广,其价值就越大。而要实现组件的价值最大化,需要考虑以下几点:

必备文档

我要写一个什么组件?组件提供什么样的能力?组件的适用范围是什么?某个具体业务系统内还是整个团队、公司或者社区?组件的生产过程是否规范、健壮和值得信赖?组件如何被开发者发现和认识?以上四点中,前两点是生产组件必须要思考的问题;第四点是组件如何推广运营的问题,这是另外一个话题,本文不展开探讨;第三点是开发者的基本素养,它决定了开发者如何看待这个组件,也间接暴露了开发者的素养和可信赖程度。

README.md

开源项目的门面,让用户第一时间了解该项目具体是做什么的,一份好的README文件应包含但不仅限于项目标题、描述、徽章、主要内容、如何安装、如何使用、API文档、如何参与贡献、版权类型等等内容。

二、组件开发的最佳姿势

CHANGELOG.md

用于记录每一次版本升级后的更新说明,包括Bug修复、新功能等等,一般和项目新Tag一起发布。

一个优秀的组件除了拥有解决问题的价值,还应该具备以下三个特点:

CODE_OF_CONDUCT.md

行为守则是一套个人或组织参与开源项目所必须遵循的规则,规范,公约,大多数项目都是采用Contributor Covenant规则。

生产和交付的规范性;优秀的质量和可靠性;较高的可用性。

CONTRIBUTING.md

全方位指导你如何参与贡献代码,如何进行本地开发,如何提ISSUE,如何提PR等等,参与项目前必看的文档。

只有三者都能满足才可以称其为优秀组件,否则会给使用者带来各种各样的困惑:经常出 Bug、坑很多、不稳定、文档太简单、不敢用等等。

ISSUE_TEMPLATE.md

�ISSUE模板,提ISSUE时Github自动识别读取注入。

2.1 规范性

PULL_REQUEST_TEMPLATE.md

�PR模板,提PR时Github自动识别读取注入。

2.1.1 目录结构

LICENSE

声明项目所遵循的版权类型

事实上,社区并没有一个官方的或者所有人都认同的目录结构规范,但从耳熟能详的知名项目中进行统计和分析,可以得出一个社区优秀开发者达成非官方共识的一个目录结构清单:

其它

├─ test // 测试相关 ├─ scripts // 自定义的脚本 ├─ docs // 文档,通常文档较多,有多个 md 文档 ├─ examples // 可以运行的示例代码 ├─ packages // 要发布的 npm 包,一般用在一个仓库要发多个 npm 包的场景 ├─ dist|build // 代码分发的目录 ├─ src|lib // 源码目录 ├─ bin // 命令行脚本入口文件 ├─ website|site // 官方网站相关代码,譬如 antd、react ├─ benchmarks // 性能测试相关 ├─ types|typings// typescript 的类型文件 ├─ Readme.md // 仓库介绍或者组件文档 └─ index.js // 入口文件

目录规范

常见目录结构及文件说明,以下只列出一部分,实际项目可变通

root/                
├── .github/                # ISSUE&PR模板文件夹
├── bin/                    # 命令行指令入口文件夹
├── build/                  # 工程配置类文件夹
├── dist/                   # 打包输出后的产品文件夹
├── docs/                   # 文档文件夹,最终被静态网站生成器编译
├── examples/               # 使用示例文件夹
├── packages/               # 子模块文件夹
├── lib/                    # �es编译后的输出文件夹
├── scripts/                # 脚本命令文件夹
├── src/                    # 源代码文件夹
├── test/                   # 测试文件夹   
├── .�babelrc                # babel配置文件
├── .editorconfig           # 编辑器配置文件     
├── .�eslintrc               # ESLint配置文件     
├── .eslintignore           # ESLint忽略文件    
├── .flowconfig             # Flow配置文件    
├── .gitattributes          # Git属性配置文件    
├── .gitignore              # Git忽略文件  
├── .npmignore              # NPM忽略文件  
├── .stylelintrc            # StyleLint配置文件   
├── .travis.yml/circle.yml  # CI环境配置文件     
├── CHANGELOG.md/History.md # �版本更新说明文件   
├── CODE_OF_CONDUCT.md      # �行为守则说明文件   
├── CONTRIBUTING.md         # �贡献指导文件    
├── AUTHORS                 # 贡献者列表文件   
├── LICENSE                 # 版权声明文件   
├── PATENTS                 # �专利声明文件   
├── README.md               # 自述文件   
├── Makefile                # �Make命令文件
├── Gruntfile.js            # �Grunt配置文件    
├── gulpfile.js             # �Gulp配置文件    
├── karma.conf.js           # �Karma配置文件  
├── webpack.config.js       # �Webpack配置文件    
├── rollup.config.js        # �Rollup配置文件    
├── bower.json              # �Bower入口文件    
├── package.json            # �NPM入口文件    
└── yarn.lock               # Yarn生成的模块记录文件

以上目录清单是一个比较完整的清单,大多数组件只需根据自己的需求选择性地使用一部分即可。一份几乎适用于所有组件的最小目录结构清单如下:

结尾

无论你是小白或是大牛,希望读完此文能让你有所收获,可以对开源项目的结构、模式大致有所了解。实际上,一个大型的开源项目往往要面临和解决各种各样的问题,项目的维护者需要付出很多的时间与精力。近几年,开源项目的产出速度越来越快,不断涌现新框架、新工具、新库,但国内优秀的开源项目仍旧为数不多,大部分只是在造轮子,任重而道远,与此同时,我们更应该注重开源项目的质量,也希望更多的人参与到开源项目中。

图片 7

最后,祝开源的世界越来越美好!

├─ test // 测试相关 ├─ src|lib // 源码目录 ├─ Readme.md // 仓库介绍或者组件文档 └─ index.js // 入口文件

2.1.2 配置文件

这里的配置文件主要指的是各种工程化工具所依赖的本地化的配置文件,以及在 GitHub 上开源所需要声明的一些文件。一份比较全的配置文件清单如下:

├─ .circleci // 目录。circleci 持续集成相关文件 ├─ .github // 目录。github 扩展配置文件存放目录 │ ├─ CONTRIBUTING.md │ └─ ... ├─ .babelrc.js // babel 编译配置 ├─ .editorconfig // 跨编辑器的代码风格统一 ├─ .eslintignore // 忽略 eslint 检测的文件清单 ├─ .eslintrc.js // eslint 配置 ├─ .gitignore // git 忽略清单 ├─ .npmignore // npm 忽略清单 ├─ .travis.yml // travis 持续集成配置文件 ├─ .npmrc // npm 配置文件 ├─ .prettierrc.json // prettier 代码美化插件的配置 ├─ .gitpod.yml // gitpod 云端 IDE 的配置文件 ├─ .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.3 package.json

如果说NPM官方给出了一个发包规范的话,那么这个规范就是package.json文件,这是发包时唯一不可或缺的文件。一个最精简的 package.json 文件是执行npm init生成的这个版本:

{ "name": "npm-speci-test", // 组件名 "version": "1.0.0", // 组件当前版本 "description": "", // 组件的一句话描述 "main": "index.js", // 组件的入口文件 "scripts": { // 工程化脚本,使用 npm run xx 来执行 "test": "echo \"Error: no test specified\"  exit 1" }, "author": "", // 组件的作者 "license": "ISC" // 组件的协议 }

有这样一个版本的 package.json 文件,我们就可以直接在该目录下直接执行npm publish发布操作了,如果 name 的名称在 npm 仓库中尚未被占用的话,就可以看到发包成功的反馈了:

$ npm publish + npm-speci-test@1.0.0

但光有这些基础信息肯定是不够的,作为一个规范的组件,我们还需要考虑:

我的代码托管在什么位置了;别人可以在仓库里通过哪些关键词找到组件;组件的运行依赖有哪些;组件的开发依赖有哪些;如果是命令行工具,入口文件是哪个;组件支持哪些 node 版本、操作系统等。

一份比较通用的 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": "npm run lint  istanbul cover _mocha -- test/ --no-timeouts", "build": "npm run lint  npm run test  gulp" }, "repository": { "type": "git", "url": "" }, "author": { "name": "someone", "email": "someone@gmail.com", "url": "" }, "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 修复,文档修改等不影响兼容性的变更,则考虑升第三位。keywords会影响在仓库中进行检索的结果。main入口文件的位置最好可以固定下来,如果组件需要构建,建议统一设置为./dist/index.js, 如果不需要构建,可以指定为根目录下的index.js。scriptsscripts 通常会包含两部分:通用脚本和自定义脚本。无论是个人还是团队,都应该为通用脚本建立规范,避免过于随意的命名 scripts;自定义脚本则可以灵活定制,比如:通用 scripts:start、lint、test、build;自定义 scripts:copy、clean、doc 等。repository属性无论在私有环境还是公共环境,都应该加上,以便通过组件可以定位到源码仓库。author 如果是一个人负责的组件,用 author,多个人就用contributors。

更详细的 package.json 文件规范可以参见 npm-package.json。

2.1.4 开发流程

很多同学在开发组件时都会使用 master 分支直接进行开发,觉得差不多可以发版了就直接手动执行一下npm publish,然后下一个版本,继续在 master 上搞。

这样做是非常不规范的,会存在很多问题,譬如:

正在开发一个比较大的版本,此时当前线上版本发现一个重要 bug 需要紧急修复;没有为每一个发布的版本指定唯一的 tag 标签以便回溯。

git 的 workflow 有很多种,各有适合的场景和优缺点。开发组件大多数时候是个人行为,偶尔是 team 行为,所以不太适合用比较复杂的流程。个人观点是,如果是在 GitHub 上维护的开源组件,则参照 GitHub 流程;如果是个人或者公司内私有环境,只要能保障并行多个版本,并且每一个发布的版本可回溯即可,可以在 GitHub 流程上精简一下,不区分 feature 和 hotfix,统一采用分支开发,用 master 作为线上分支和预发分支,开发分支要发版需要预先合并到 master 上,然后在 master 上 review 和单测后直接发布,并打 tag 标签,省略掉 pull request 的流程。

2.1.5 commit changelog

一个组件从开发到发布通常会经历很多次的代码 commit,如果能在一开始就了解 git commit 的 message 书写规范,并通过工具辅助以便低成本地完成规范的实践落地,则会为组件的问题回溯、了解版本变更明细带来很大的好处。我们可能都见过 Node.js 项目的 changelog 文件:

本文由10bet发布于Web前端,转载请注明出处:开发一个高质量的前端组件,这些姿势一定要知

关键词:

最火资讯