背景: 随着开发团队规模不断发展壮大,在人员增加的同时也带来了协作成本的增加,业务项目越来越多,类型也各不相同。常见的类型有组件类、活动类、基于React+redux的业务项目、RN项目、Node.js项目等等。如果想要对每个项目进行一些规范的约束比如Git提交规范、Javascript规范简直难于登天。所有的这些,只因为缺少一个好用的工程化工具。从项目创建、开发、构建、代码规范检查到最终项目上线,通过CLI可以提升效率,同时保障开发规范的实施。
Node.js实现CLI的基本原理
关键点在于package.json里面的bin字段。模块全局安装,对于类unix系统,在/usr/local/bin目录创建软链接;对于windows系统,在C:\Users\username\AppData\Roaming\npm目录创建软链接。
模块局部安装,会在项目内的./node_modules/.bin目录创建软链接。
现代化web工程的生命周期
随着前端工程的不断演进,一方面工程变得日趋复杂,同时对规范和质量的诉求在不断增加。现代化web工程应该包含以下几个阶段:初始化、开发、构建、检查、发布。如下图所示:
痛点1:项目拷贝
项目拷贝存在的问题显而易见,大致有以下三个方面:
- 容易出错;一旦某个关键文件拷贝丢失或者错误,很可能需要耗费半天到一天的时间排查环境问题。
- 不同场景下对目录结构要求不同;平时开发过程中,工程通常会分为运营活动、Hybrid业务、入口级别的项目(对性能和体验有极致和苛刻的要求)。需要基于RN或者Node.js的首屏直出,还有常用的业务组件等的开发。
- 新的Feature和BugFix难以同步;某个同学开发过程中增加的新方法或者解决的bug很难传递给其它同学并且沉淀成经验积累下来。
社区里面提供了完美的Yeoman解决方案,它是为了自动化项目的创建而生。Yeoman创建项目包括以下几个阶段:
- initializing: 初始化一些状态之类的,通常是和用户输入的 options 或者 arguments 打交道
- prompting: 和用户交互的时候(命令行问答之类的)调用
- configuring: 保存配置文件(如 .babelrc 等)
- writing: 生成模板文件
- install: 安装依赖
- end: 结束部分,初始代码自动提交
我们只需要继承Yeoman的Generator类做模板定制化,基于Yeoman的脚手架设计思路应该如下图所示:
首先,开发者会和CLI进行交互,开发者会告诉CLI需要创建哪一种类型的项目,CLI收到命令后。从本地已经安装的Yeoman脚手架里面选择某种类型的模板。然后,CLI会调用Gitlab API在远程创建仓库并且授予开发者master权限。接下来,会根据实际业务场景需要,自动化申请一些打点信息,常见的如离线包id,监控告警id等等。之后,在本地目录生成代码并且安装项目依赖的npm包,最后将本次初始化生成的所有代码自动提交到远程Git仓库。
痛点2:运营配置频繁修改
基于React+redux组件化开发方式中,一个页面或者webapp是由多个容器组件拼装后渲染而成。
某个组件通常是由:模板、cgi数据和事件组成。理想情况下,开发和产品和平共处,你可以把一个组件写成下面这个样子,比如规则组件:
render() {
return (
<div className="lottery-rule">
<div className="section">
<h3>活动时间:</h3>
<p>9月14日~9月30日</p>
</div>
<div className="section">
<h3>活动规则:</h3>
<p>1、活动期间,在NOW app上录制小视频,上传成功后即可参赛。</p>
<p>2、根据参赛小视频获得的点赞数进行排行。</p>
<p>3、按照城市评选,分别评选“明日之子”(仅限男性参加)和”闪亮女神“仅限女性参加。</p>
</div>
</div>
);
}
咋一看,上面的写法没什么问题。实际确很可能是7、8次的文案修改,甚至对外入口开放后仍然要修改文案或者图片等静态数据。然后,你需要走代码发布流程。
更好的解决思路是:在开发某个业务组件之前,结合以往的经验,分析哪些静态数据很可能是需要高频次的修改。将这些高频次修改的静态数据抽离出来,对于万年不变的数据则没有必要抽出来。那么,如何将静态数据动态化呢?
答案是: ** Schema First **, 开发组件之前先设计Schema,通过schema生成一个form表单,达到静态数据和模板分离。如果使用React开发,可以基于react-jsonschema-form定制。静态数据和模板分离之后应该如下图:
痛点3:缺少协作规范
此处以Git commit规范为例子进行相关改进介绍。
良好的Git commit规范有以下优势:
- 加快Review的流程
- 根据Commit元数据生成Changelog
- 后续维护者可以知道feature被添加的原因
此处采用Google angular项目的提交作为参考,整理出Git commit的解决方案:
具体的提交格式要求如下:
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
对格式的说明如下:
- type代表某次提交的类型,比如是修复一个bug还是增加一个新的feature。所有的type类型如下:
- feat: 新增feature
- fix: 修复bug
- docs: 仅仅修改了文档,比如README, CHANGELOG, CONTRIBUTE等等
- style: 仅仅修改了空格、格式缩进、都好等等,不改变代码逻辑
- refactor: 代码重构,没有加新功能或者修复bug
- perf: 优化相关,比如提升性能、体验
- test: 测试用例,包括单元测试、集成测试等
- chore: 改变构建流程、或者增加依赖库、工具等
- revert: 回滚到上一个版本
一键生成Changelog版本日志:
痛点4: 缺少代码规范
一次血淋淋的生产环境事故:2017年4月13日,腾讯高级工程师小圣在做充值业务时,修改了苹果iap支付配置,将JSON配置增加了重复的key。代码发布后,有小部分使用了vivo手机的用户反馈充值页面白屏,无法在Now app内进行充值。最后问题定位是:vivo手机使用了系统自带的webview而没有使用X5内核,解析JSON时遇到重复key报错,导致页面白屏。
分析:现代化的浏览器对于JSON里面的重复key会做兼容处理,但是某些老旧的浏览器内核并不会,比如此处的vivo手机,导致代码直接出错。那么,如何避免类似问题再次出现呢?
此处不得不提及ESLint,ESLint于2013年6月推出最新版本v4.6.0,是一款适用于Javascript和JSX的代码规范检查工具,相比JSLint和JSHint而言,它更加灵活,支持自定义配置、插件扩展和配置错误级别。虽然接入ESLint会给团队的同学增加不少代码修改的成本,但是从长远来看,收益肯定是大于付出的。
Javascript规范制定的原则:
- 不重复造*,基于eslint:recommend 配置并改进
- 能够帮助发现代码错误的规则,全部开启
- 配置不应该依赖于某个具体项目,而应尽可能的合理
- 帮助保持团队的代码风格统一,而不是限制开发体验
- 有对应的解释文档
为了更好的定制和维护Javascript规范,我们创建了eslint的shareable config。一方面,我们觉得eslint:recommend 里面的部分配置定义的错误级别过于严格,比如代码里面出现了console会导致校验错误,另一方面,它没有包含ESLint的最佳实践和其它规则。我们定义的部分规则解释如下:
规则名称 | 错误级别 | 说明 |
---|---|---|
for-direction | error | for 循环的方向要求必须正确 |
getter-return | error | getter必须有返回值,并且禁止返回值为undefined, 比如 return; |
no-await-in-loop | off | 允许在循环里面使用await |
no-console | off | 允许在代码里面使用console |
no-prototype-builtins | warn | 直接调用对象原型链上的方法 |
valid-jsdoc | off | 函数注释一定要遵守jsdoc规则 |
no-template-curly-in-string | warn | 在字符串里面出现{和}进行警告 |
accessor-pairs | warn | getter和setter没有成对出现时给出警告 |
array-callback-return | error | 对于数据相关操作函数比如reduce, map, filter等,callback必须有return |
block-scoped-var | error | 把var关键字看成块级作用域,防止变量提升导致的bug |
class-methods-use-this | error | 要求在Class里面合理使用this,如果某个方法没有使用this,则应该申明为静态方法 |
complexity | off | 关闭代码复杂度限制 |
default-case | error | switch case语句里面一定需要default分支 |
ESLint的执行可以接入到PUSH hook里面,步骤如下:
#1, 安装husky
$ npm install husky --save-dev
#2, 集成进npm script
{
"scripts": {
"precommit": "validate-commit-msg",
"prepush": "eslint src ./.eslintrc.js --ext '.js,.jsx'"
}
}
CLI设计
CLI的作用是将工程开发过程中遇到的一系列痛点问题连接起来,提升开发效率,同时保障规范的实施。
插件设计
插件实现原理
这里有一个非常巧妙的设计,通过使用node提供的module和vm模块,可以通注入feflow全局变量来访问到cli的实例。从而能够访问cli上的各种属性,比如config, log和一些helper等。
loadPlugin(path, callback) {
const self = this;
return fs.readFile(path).then((script) => {
const module = new Module(path);
module.filename = path;
module.paths = Module._nodeModulePaths(path);
function require(path) {
return module.require(path);
}
require.resolve = function(request) {
return Module._resolveFilename(request, module);
};
require.main = process.mainModule;
require.extensions = Module._extensions;
require.cache = Module._cache;
// Inject feflow variable
script = '(function(exports, require, module, __filename, __dirname, feflow){' +
script + '});';
const fn = vm.runInThisContext(script, path);
return fn(module.exports, require, module, path, pathFn.dirname(path), self);
}).asCallback(callback);
}
命令注册:
命令需要以feflow.cmd.register进行注册,比如:
feflow.cmd.register('deps', 'Config ivweb dependencies', function(args) {
console.log(args);
// Plugin logic here.
});
说明:
- register有3个参数,第一个是子命令名称,第二个是命令描述说明信息,第三个是对应的子命令执行逻辑函数。
- feflow会将命令行参数args解析成Object对象,传递给插件处理函数
配置
可以通过feflow.version获取当前feflow的版本,feflow.baseDir 获取feflow跟目录(在用户目录下的.feflow),通过feflow.pluginDir 获取插件目录
日志
通过feflow.log来进行相关命令行日志输出
const log = feflow.log;
log.info() // 提示日志,控制台中显示绿色
log.debug() // 调试日志, 命令行增加--debug可以开启,控制台中显示灰色
log.warn() // 警告日志,控制台中显示黄色背景
log.error() // 错误日志,控制台中显示红色
log.fatal() // 致命错误日志,,控制台中显示红色
最后
感谢OSC源创汇提供的交流机会,能和广大开发者分享和交流学习。NOW直播IVWEB团队的工程化解决方案如下:
附件:本次分享PPT
《Node.js在CLI下的工程化体系实践》成都OSC源创会分享总结的更多相关文章
-
《Node.js在CLI下的工程化体系实践》成都OSC源创汇分享总结
背景: 随着开发团队规模不断发展壮大,在人员增加的同时也带来了协作成本的增加,业务项目越来越多,类型也各不相同.常见的类型有组件类.活动类.基于React+redux的业务项目.RN项目.Node.j ...
-
Node.js编写CLI的实践
导语:通常而言,Node.js的应用场景有前后端分离.海量web页面渲染服务.命令行工具和桌面端应用等等.本篇文章选取CLI(Command Line Tools)这子领域,来谈谈Node.js编写C ...
-
基于 Angularjs&;Node.js 云编辑器架构设计及开发实践
基于 Angularjs&Node.js 云编辑器架构设计及开发实践 一.产品背景 二.总体架构 1. 前端架构 a.前端层次 b.核心基础模块设计 c.业务模块设计 2. Node.js端设 ...
-
node.js之windows下环境终极配置
大家都知道现在node.js相当流行,出门在外,如果都没听说过node.js,基本上算是out了,前段时间做一个项目,用到了实时通讯功能,当时用的就是node.js来做的,我有幸有研究了一番,别的不敢 ...
-
node.js在windows下的学习笔记(9)---文件I/O模块
开发中我们经常会有文件I/O的需求,node.js中提供一个名为fs的模块来支持I/O操作,fs模块的文件I/O是对标准POSIX函数的简单封装. 1.将"hello world" ...
-
node.js在windows下的学习笔记(8)---进程管理Process
process是一个全局内置对象,可以在代码中的任何位置访问此对象,这个对象代表我们的node.js代码宿主的操作系统进程对象. 使用process对象可以截获进程的异常.退出等事件,也可以获取进程的 ...
-
node.js在windows下的学习笔记(4)---同步,异步,回调的概念
Node.js是使用事件驱动的,非阻塞的I/O模型,用于构建快速的,可扩展的网络应用程序. Node.js想解决的问题是:处理输入,输入,高并发 1.阻塞与非阻塞 阻塞也叫同步,是指每一次执行一个操作 ...
-
node.js在windows下的学习笔记(3)---npm
1.什么是npm npm是Node.js的包管理器,它允许开发人员在Node.js的应用程序中创建,共享,重用模块.之前我们通过node的官网的安装程序安装了Node.js,那么npm就已经装好了的. ...
-
[Node.js]在windows下不得不防的小错误
TypeError: Arguments to path.join must be strings at f (path.js:204:15) at Object.filter (native) at ...
随机推荐
-
SAP CRM BOL编程基础,代码+详细注释
网络上可以找到一些使用BOL查询.维护数据的DEMO,但几乎都是单纯的代码,缺乏说明,难以理解.本文除了代码外,还给出了详细的注释,有助于理解BOL编程中的一些基本概念. 这是一篇翻译的文章,你可能会 ...
-
BZOJ 1668: [Usaco2006 Oct]Cow Pie Treasures 馅饼里的财富( dp )
dp , dp[ i ][ j ] = max( dp[ k ][ j - 1 ] ) + G[ i ][ j ] ( i - 1 <= k <= i + 1 , dp[ k ][ j - ...
-
java开发第一天
今天是项目开始的时间,整体来说还是算顺利的.提前分好组,然后是听课时可以有人帮忙占座位的,感觉上是挺好的. 项目开发的难度看了看,由于有了第一次MFC开发的经验,所以这次听课感觉非常的有目标性,而且总 ...
-
Commons-FileUpload组件的应用
<% request.setCharacterEncoding("utf-8"); String uploadFileName="";//上传的文 ...
-
double保留两位小数
public static String format(double dValue, int lScale) { // ////负数,则装化为正数后进行四舍五入 boolean bFlag = fal ...
-
UI设计师经常去的五个网站
1.站酷 站酷(ZCOOL),中国人气设计师互动平台.深耕设计领域十年,站酷聚集了470万优秀设计师.摄影师.插画师.艺术家.创意人,设计创意群体中具有较高的影响力与号召力. 2.花瓣 花瓣网, ...
-
【LeetCode】4. Median of Two Sorted Arrays (2 solutions)
Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. Find t ...
-
二.jquery.datatables.js表格数据添加
1.后台php public function addtable(){ $data = $_POST; if(M('yanfa_project')->add($data)){ $this-> ...
-
[UE4]IES光源概述文件
IES Light Profiles(IES光源概述文件) 是一条曲线,该曲线在一段弧线中定义了光源强度,虚幻引擎4将会围绕某个轴旋转该弧线,从而使得 点光源 (和从技术上讲的 聚光源,下面会提供更多 ...
-
JQuery Mobile - 为什么绑定事件后会被多次执行?
JQuery Mobile 在绑定事件时候,发现会被多次执行,为什么啊? 原来,jquery click 不是替换原有的function ,而是接着添加,所以才会执行次数越来越多,怎么办才能按需实现 ...