关于规范化:代码健壮性

问题:原生js 代码走到一个中央报错,后续代码无奈执行 vue与原生js不同:原生js 代码走到一个中央报错,后续代码无奈执行vue 多个生命周期,前一个生命周期的代码有了bug,后一个生命周期的代码还会执行 如何解决js代码出错,而后继续执行呢 try{}catch(err){}finally{} 解决好哪些状况,能够显得代码健壮性好呢?后端返回的数据类型多样,比方Object,Array,null,前端都思考到了用户不按惯例出牌,不按程序操作,前端是否束缚或者给出适合提醒(防暴力点击)适配各种分辨率,B端element-ui的el-table应用min-width。 C端流动列表页应用min-height:100vh用户体验,(1)B端:当页面内容居多时,能不能跳转到第一次出错的地位(2)B端:新增胜利,返回列表页时,能不能在缓存表单数据的状况下,再次调用查问接口 (3)C端:图片因为带宽过低或者网络不稳固未能加载到图片,应用@error办法,再次加载一次图片&t={$index} beforeRouteEnter(to, from, next) { next(async (vm) => { //因为当钩子执行前,组件实例还没被创立 if ( from.path == vm.questionnaireCreateUrl || from.path == vm.questionnaireViewUrl || from.path == vm.questionnaireModifyUrl ) { vm.resetForm(); await vm.getList(); } }); },加油哦,冲鸭!!!!!

October 19, 2021 · 1 min · jiezi

关于规范化:前端规范elint代码格式化

前端-标准-elint代码格式化eslint配置文件// .eslintignore文件// 不检测iconfont.js文件**/iconfont.jsroot = true [*]charset = utf-8end_of_line = lfindent_size = 2indent_style = spaceinsert_final_newline = truetrim_trailing_whitespace = true常见命令主动格式化修复src文件夹下文件:eslint --fix src自动检测并列举不合乎的格局:eslint src检测指定文件夹下的.js和.vue文件:eslint --ext .js,.vue src依据配置文件检测指定类型文件:eslint --config .eslintrc.js --ext .js,.vue src设置代码跳过eslint检测不检测当行:// eslint-disable-line不检测下一行:// eslint-disable-next-line整个文件不检测:头部放/* eslint-disable */跳过多行: /* eslint-disable */function a() { return this;}/* eslint-enable */

September 18, 2021 · 1 min · jiezi

关于规范化:前端规范jscsshtmlvue文件夹等命名规范

文件局部文件夹: 小驼峰,尽量简洁,一眼能看出什么用途内容,示例:user, userSetting 文件名(js/css/vue/jsx/png): 小驼峰,示例:userSetting.js/css/vue/jsx/png js局部模块,组件,类名: 大驼峰,示例:MyTestName js常量: 大写,多个单词两头用下划线,示例:MY_TEST_NAME js办法名: 小驼峰,尽量语义化,可动词前缀,示例:getTableData 路由地址: 小驼峰,长的话能够局部单词缩写,但须要语义明确,示例:sysSetting css局部css款式名: 小写,中横线链接,示例:flex-between 其余局部其余未说明均为小驼峰,简洁,语义化

September 18, 2021 · 1 min · jiezi

关于规范化:EditorConfig代码书写规范的魅力-vscode

通知EditorConfig插件,这是根文件,不必持续往上查找 root = true 匹配全副文件 [*] 设置字符集 charset = utf-8 缩进格调,可选space、tab indent_style = space 缩进的空格数 indent_size = 2 结尾换行符,可选lf、cr、crlf end_of_line = lf 在文件结尾插入新行 insert_final_newline = true 删除一行中的前后空格 trim_trailing_whitespace = true 匹配md结尾的文件 [*.md] insert_final_newline = false trim_trailing_whitespace = false

August 13, 2021 · 1 min · jiezi

关于规范化:一位攻城狮的自我修养在于良好的编程规范

命名格调类名应用UpperCamelCase格调,但下列情景除外: DO: Data Object. 与数据库表构造一一对应,通过DAO层向上传输数据源对象BO: Business Object,业务对象. 由Service层输入的封装业务逻辑的对象DTO: Data Transfer Object,数据传输对象. Service或Manager向外传输的对象VO: View Object,显示对象. 通常是Web向模板渲染引擎层传输的对象AO: Application Object,利用对象. 在Web层与Service层之间形象复用的对象模型PO: POJO缩写,Plain Ordinary Java Object. 专指只有setter/getter/toString的简略类,包含DO,DTO,BO,VO等, 禁止应用xxxPOJO来命名UID办法名,参数名,成员变量,局部变量都对立应用lowerCamelcase格调常量命名全副大写,单词间用下划线隔开, 力求语义表白残缺分明,不要嫌名字长抽象类命名应用Abstract或者Base结尾异样类命名应用Exception结尾测试类命名要以要测试的类的名称命名,以Test结尾类型与中括号紧挨来示意数组POJO类中布尔类型的变量都不要加is前缀,在局部框架中会引起序列化谬误包名对立应用小写,点分隔符之间有且仅有一个天然语义的英语单词.包名对立应用复数模式.然而类名如果有复数含意,能够应用复数模式杜绝不标准的缩写,防止望文不知义为了达到代码自解释的指标,任何自定义的编程元素在命名时,应用尽量残缺的单词组合来表白含意在常量与变量命名的同时,示意类型的名词放在词尾,以晋升辨识度如果模块,接口,类,办法应用了设计模式,在命名时须要体现出设计模式接口类中的办法和属性不要加任何润饰符号(不要加public), 放弃代码的简洁尽量不要在接口中定义变量,如果肯定要定义变量,肯定是与接口办法无关的,并且是整个利用的根底变量 接口办法签名: void commit()接口根底常量: String COMPANY="Oxford"接口和实现类: 对于Service和DAO类,基于SOA的理念,裸露进去的服务肯定是接口,外部的实现类用Impl的后缀与接口的区别如果是形容能力的接口名称,去对应的形容词为接口(-able的模式)枚举类带上Enum后缀,枚举成员名称需全副大写 枚举类是非凡的类,域成员均为常量,且构造方法被默认强制是公有的各层命名标准: Service或者DAO层办法命名标准: 获取单个对象的办法用get做前缀获取多个对象的办法用list做前缀 ,复数模式结尾获取统计值的办法用count做前缀插入方法应用save或者insert做前缀删除的办法应用remove或者delete做前缀批改的办法应用update做前缀畛域模型命名标准: 数据对象: XxxDO,Xxx为数据表名数据传输对象: XxxDTO,Xxx为业务畛域相干的名称展现对象: XxxVO,xxx个别为网页名称POJO为DO,DTO,BO,VO的统称,禁止命名成XxxPOJO 常量定义不容许任何未经事后定义的常量呈现在代码中在long或者Long赋值时,数值后应用大写的L, 不能是小写的l. 因为小写容易和数字1混同,造成误会不要应用一个常量类保护所有常量,要按常量的性能进行归类,离开保护 大而全的常量类横七竖八,应用查找性能能力定位到批改的常量,不利于了解和保护常量的复用档次有五层: 跨利用共享常量: 搁置在二方库中,通常是client.jar中的constant目录下利用类共享常量 搁置在一方库中,通常是子模块中的constant目录下子工程内共享常量 在以后子工程的constant目录下包内共享常量 在以后包的constant目录下类内共享常量 间接在类外部private static final定义如果变量值仅在一个固定范畴内变动,应用enum类型定义 如果存在名称之外的延长属性应应用enum类型,比方节令,示意一年中第几个节令: public enum SeasonEnum {SPRING(1),SUMMER(2),AUTUMN(3),WINTER(4);private int seq;SeasonEnum(int seq) { this.seq=seq;}} 代码格局大括号的应用约定: 如果大括号内为空,则简洁地写成 { } 即可,不须要换行如果是非空代码块: 左大括号前不换行左大括号后换行右大括号前换行右大括号后如果还有else则不换行示意终止的右大括号后必须换行小括号的应用约定: 左小括号和字符之间不要呈现空格右小括号和字符之间也不要呈现空格左大括号之前须要空格if,for,while,switch,do等保留字与括号之间都必须加空格任何二目,三目运算符左右两边都须要加一个空格 运算符包含: ...

June 30, 2021 · 3 min · jiezi

关于规范化:规范化个人笔记

规范化规范规范化是咱们践行前端工程化中重要的一部分 为什么有标准规范哪些须要规范化规范施行规范化的办法为什么有标准规范 软件开发须要多协同,不同开发者具备不同的编码习惯和爱好,不同的爱好减少我的项目保护老本,每个我的项目或者团队须要明确对立的规范哪些须要规范化规范 代码,文档,甚至是提交日志,开发过程中人为编写的内容,代码标准化标准最为重要(对立关键词和操作符左右空格,对立代码的缩进形式,对立是否应用分号结尾,对立变量函数的命名标准)施行规范化的办法 编码前人为的规范约定通过工具实现 Lint(更谨严牢靠,同时能够配合自动化工具,主动查看)常见的规范化实现形式 ESLint 工具应用定制 ESLint 校验规定ESLint 对 Typescript 的反对ESLint 联合自动化工具或者 Webpack基于 ESLint 的衍生工具Stylelint 工具的应用 (对css进行校验操作)ESLint最为支流的JavaScript Lint 工具, 监测 JS 代码品质ESLint 很容易对立开发者的编码格调ESLint 能够帮忙开发者晋升编码能力初始化我的项目,装置 ESLint 模块为开发依赖 装置:yarn add eslint --dev 实现 eslint 应用配置,编写问题代码,应用 eslint 执行检测 应用 yarn eslint 文件.js 时,会报错说未找到eslint 配置文件 通过yarn eslint --init 进行命令行交互 只查看语法查看语法发现问题查看语法发现问题并且对代码格调校验 装置过后生成 .eslintrc.js 文件 执行 yarn eslint ./文件.js,查看谬误 配置文件# .eslintrc.jsmodule.exports = { env: { browser: true, es2021: true }, extends: [ 'standard' ], parserOptions: { ecmaVersion: 12 }, rules: { 'no-alert':"error" }}env 设置运行环境,参数并不互斥 ...

June 5, 2021 · 2 min · jiezi

restful规范

Restful全称是Representational State Transfer,即表现层状态转移。REST指的是一组架构约束条件和原则,如果一个架构符合REST的约束条件和原则,就称之为RESTful架构。RESTful是一种软件架构风格,而不是标准。那么表现层状态转移怎么理解?外国人取名总是这么抽象,难以理解。 1、资源REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。 2、表现层"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。 3、状态转化访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。综合上面的解释,我们总结一下什么是RESTful架构:(1)每一个URI代表一种资源;(2)客户端和服务器之间,传递这种资源的某种表现层;(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。总之,一句话描述就是用 URL定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作。 参考链接:http://www.ruanyifeng.com/blog/2011/09/restful.html https://www.toutiao.com/i6746910619393327629/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1571272396&app=news_article&utm_source=weixin&utm_medium=toutiao_android&req_id=201910170833160100260772133F90C589&group_id=6746910619393327629

October 17, 2019 · 1 min · jiezi

Java并发20并发设计模式-Guarded-Suspension模式等待唤醒机制的规范实现

在开发中我们或许回遇到这样的情况:有一个Web 版的文件浏览器,通过它用户可以在浏览器里查看服务器上的目录和文件。这个项目依赖运维部门提供的文件浏览服务,而这个文件浏览服务只支持消息队列(MQ)方式接入。消息队列在互联网大厂中用的非常多,主要用作流量削峰和系统解耦。在这种接入方式中,发送消息和消费结果这两个操作之间是异步的,你可以参考下面的示意图来理解。 消息队列(MQ)示意图 在这个 Web 项目中,用户通过浏览器发过来一个请求,会被转换成一个异步消息发送给 MQ,等 MQ 返回结果后,再将这个结果返回至浏览器。问题来了:给 MQ 发送消息的线程是处理 Web 请求的线程 T1,但消费 MQ 结果的线程并不是线程 T1,那线程 T1 如何等待 MQ 的返回结果呢?示例代码如下。 class Message{ String id; String content;}// 该方法可以发送消息void send(Message msg){ // 省略相关代码}//MQ 消息返回后会调用该方法// 该方法的执行线程不同于// 发送消息的线程void onMessage(Message msg){ // 省略相关代码}// 处理浏览器发来的请求Respond handleWebReq(){ // 创建一消息 Message msg1 = new Message("1","{...}"); // 发送消息 send(msg1); // 如何等待 MQ 返回的消息呢? String result = ...;}Guarded Suspension 模式上面遇到的问题,在现实世界里比比皆是,只是我们一不小心就忽略了。比如,项目组团建要外出聚餐,我们提前预订了一个包间,然后兴冲冲地奔过去,到那儿后大堂经理看了一眼包间,发现服务员正在收拾,就会告诉我们:“您预订的包间服务员正在收拾,请您稍等片刻。”过了一会,大堂经理发现包间已经收拾完了,于是马上带我们去包间就餐。 我们等待包间收拾完的这个过程和前面的等待 MQ 返回消息本质上是一样的,都是等待一个条件满足:就餐需要等待包间收拾完,程序里要等待 MQ 返回消息。 ...

July 9, 2019 · 3 min · jiezi

如何在企业各团队间推广开发规范

前些日在阿里技术公众号回复了一个“开发规范如何推广”的问题,获得了蛮多点赞。故写此一篇文章细讲一下我们的推广方式,给困惑的朋友贡献一点思路。开发规范很重要领导要一盘西红柿炒蛋,交给A,B,C几个团队去实现。团队A没控制好火候把鸡蛋炒老了,团队B只放了糖未放盐,团队C选用了已经馊了的西红柿... 领导大怒: “我们的愿景是做全国连锁餐饮,这么搞下去很快就完蛋!”,团队leader也委屈,没人告诉我这样不行啊! 规范就是要告诉团队成员哪些做法不行,使团队不同成员的代码尽可能做到标准统一,提高代码质量,降低维护成本。 如何推广开发规范?1. 获得上层支持大领导交办的事情你加班加点也会尽力做好,同样规范的推广如果没有上层支持,最终的落地效果肯定大打折扣。如果上层未能意识到规范的重要性,你就要说服上层支持。比如:汇总现有IT系统暴露的问题,重点梳理出代码不规范、标准不统一导致的问题,说服IT上层支持推动规范落地,以带来IT系统开发质量的提升和维护成本的降低。 2. 参与制定规范如果你直接把《阿里Java开发手册》扔给开发人员,大多数开发是抵触的。阿里的开发手册很详细,但不建议你直接作为你企业的规范。因为:开发场景不同遇到的问题就不同,阿里不出现的问题很可能你的企业会出现,比如一些循环SQL等问题。另外阿里手册也不涉及技术栈规约,这些都需要你去制定。其他对于阿里手册里适用的规范建议直接吸收进来。 开发规范制定的过程最好让各个团队leader都参与进来,贡献内容。有个词叫“禀赋效应”,比起你直接扔给他的,他更愿意推动自己参与制定的。 3. 组织全员培训规范制定完成,与各个团队leader达成一致后就要组织统一的全员培训。把规范的制定过程以及规范背后发生的故事讲给开发人员。统一培训后续如有新人加入,可以组织1v1讲解或新人反讲。 4. 应用规范规范还有一个重要目的是可作为code review的准则,基于规范去做代码检查,保证代码质量标准统一。 5. 更新规范后续如发现新的问题,需要加入规范的,可以由开发人员或leader去补充更新到规范中(规范可以放WIKI上),保持规范持续可用。 diboot 简单高效的轻代码开发框架

June 28, 2019 · 1 min · jiezi

Web-项目编码规范化工具

Web 项目编码规范化工具工具ESLintThe pluggable linting utility for JavaScript and JSX代码校验工具(linting utility),让代码更一致和避免 bug。 PrettierPrettier is an opinionated code formatter.支持 JavaScript · TypeScript · Flow · JSX · JSON · CSS · SCSS · Less · HTML · Vue · Angular ·GraphQL · Markdown · YAML 等文件的格式化。 代码格式化工具(code formatter),少数服从多数,任性的风格统一,来确保所有输出的代码符合一致。 集成编辑器与终端集成,以便开发时代码不规范及语法错误时,编辑器与终端都将信息暴露给用户,以方便查看与及时解决问题。项目集成代码格式化过程终端下发出格式化命令 ☟ESLint 收到命令 ☟ESLint 读取项目目录下的 ESLint 配置文件 ☟如果配置文件里面有 Prettier ☟ 插件则读取项目目录下的Prettier配置文件,反之则跳过该步骤 ☟ESLint 发出格式化命令 ↺Prettier 配置安装 prettier 包。项目根目录下添加配置.prettierrc文件。ESLint 配置安装 eslint 包。安装项目特定语法校验规则eslint扩展插件,如Vue项目eslint-plugin-vue,React项目eslint-plugin-react、eslint-plugin-react-hooks 等。安装 eslint-plugin-prettier 集成 prettier 语法规则,安装 eslint-config-prettier 解决 prettier 与其他规则等冲突问题。项目根目录下添加配置.eslintrc文件。将上述等插件及扩展规则添加到配置文件,才会生效。在配置文件到rules项可对单条规则一一进行改写。项目完整配置参考React 项目集成Vue 项目集成编辑器集成安装插件以 VSCode 为例,其他编辑器类似。 ...

June 24, 2019 · 1 min · jiezi

Design-Review-架构规范

Design Review 是 TTM 过程中至关重要的一环,优秀的 Design review 不但能让技术方案的考虑更加周全,更多意义是避免潜在的线上 Bug 以及不必要的反复。 下面是我经常思考的一些问题,虽然不是每个项目都会涉及到这些点,而且也不应该被这些问题所局限,但作为一个参考,依然希望能给团队提供一个好的思考框架。 可用性外部依赖有哪些?如果这些外部依赖崩溃了我们有什么处理措施?我们 SLA 是什么?主要是指可用性目标几个 9? 50/90/99 分位数的响应时间是多少?QPS 是多少?我们的超时、重试、过载保护、服务降级机制是什么?如何避免雪崩我们的调用方有哪些?分别有什么服务配额?是否需要对关键的服务调用方单独部署?运维我们都有配置了哪些监控?如果出现问题,我们需要查看哪些信息?这些信息是否都有记录?报警的处理流程是什么?系统上线流程和步骤是什么,出了问题后是否可以回滚,以及怎么回滚?安全XSS,CSRF,SQL 注入这些是否需要处理?3 防怎么搞:防抓,防 DDOS,防恶意访问是否有请安全团队 review是否有风控的需求?信息存储时是否设计到密码、信用卡、身份证等敏感信息,这些信息是怎么存储和访问的?扩展性分层,分模块怎么拆分比较合理?拆分出来的模块可以搞成服务单独部署吗?应用层可以水平扩展吗?有用 session 吗?可以去掉 session 吗?如果系统的负载提升到以前的 3 到 10 倍,当前系统是否依然可用存储层面如果需要扩展存储怎么做?系统中有哪些上下依赖的节点 / 系统 / 服务?这些依赖是否会导致无法并行开发?能否去掉这些依赖?是否有数据访问 API? 数据 API 的设计对性能的考虑是什么?数据 API 对异常数据 (超大数据集、空数据集、错误数据、schema 异常...) 的处理是什么?存储数据计划怎么存储?会有可能的性能瓶颈吗?需要考虑一些缓存方案吗?有什么复杂 SQL 可能会导致慢查询吗?数据库的操作什么地方用了事务?什么情况会导致锁竞争?我们的锁策略是什么?一致性和可用性如何平衡?未来如果分库分表会有什么影响?缓存失效会有什么影响?缓存大量失效会有什么影响?冷启动有问题吗?有热点数据吗?多个缓存节点需要权衡可用性和一致性吗?存储时,是否需要分库,分表,选择的理由是什么?技术选型开发语言是什么,框架是什么为什么用他们?缓存用什么(tair/medis/redis/memached),web server 用什么?(nginx+php fpm/ apach php 扩展/jetty/tomcat/jboss),消息队列用什么 (rebbitmq/beanstalk/kafka/mafka/metaq/notify)?为什么用它们?DB 是否可以用、以及用哪种 no sql (hbase/tair/mangodb/redis) 来优化?业界或者其他团队是否有处理过类似问题?他们是怎么处理的?是否可以 copy 或者借鉴?服务调用和服务治理请求同步处理还是异步队列处理比较好?服务接口的 URI 设计合理吗?可以向下兼容吗?服务间的调用协议是什么(dubbo/hsf/thrift) ?有公司标准的调用协议可以用吗(hession/protobuffer)?客户端和服务端的调用协议是什么(http/ws/私有)?有公司标准的调用协议可以用吗?有什么服务治理相关的要考虑的吗?能否接入 SLA 服务治理?业务监控正常的业务逻辑外,可能会有哪些奇葩或者恶意的操作?我们应该怎么处理?除了系统上的监控外,需要什么业务维度的监控吗?log 是怎么记的?如果要 debug 能有什么开关迅速打开吗?log 怎么 rotate?log 会影响性能吗?复用项目中有用什么新技术吗?为什么要用新技术?未来其他人接手容易吗?项目中有什么复杂计算的地方吗?这些计算可以用什么算法优化吗?这个项目可以抽象出来什么可以复用的东西吗?项目中的什么可以不用自己做,调用现成服务吗?测试新的系统设计是否容易独立测试兼容性新的系统是否和已有系统冲突,怎么融进去

June 24, 2019 · 1 min · jiezi

可能是你需要的-React-TypeScript-50-条规范和经验

转载:https://juejin.im/post/5ce24f...

June 3, 2019 · 1 min · jiezi

技术三板斧关于技术规划管理架构的思考

阿里妹导读:实践需要理论的指导,理论从实践中来。作为技术工程师,要不断地从事件中反思经验、总结规律,才能避免踏入同一个坑,才能更高效地完成 KPI ,甚至是晋升。今天的文章来自阿里巴巴高级技术专家毕啸,从五个方面总结工程技术的核心要点,相信对你能有所启发。大约半年前,开始总结自己关于工程技术的一些核心要点,关于规划、技术管理以及架构,三个方面的一些心得。结合自己团队的现状、自己对于周边做得比较好的同学的观察,于是有了文中的这几张图。 一、关于技术规划三板斧技术规划规划做得好,能起到比较好的正向引导作用,个人及团队的整体目标感会好很多,分为三个部分的内容: 第一部分是全局分析,这需要溯源历史,思考未来,要对未来有一定的预判。能够基于数据,基于专业,基于客户价值,同时结合顶层的战略、公司的战役情况和组织的现状做分析。 第二部分是定目标。这一部分非常关键,定义好目标以及非目标,哪些事情是不要做的也要讲明白,并且确认目标的实现路径,做好拆解。 最后一部分是以终为始,从最终结果的角度,来溯源开始。从技术支撑业务发展、平台能力输出或者赋能、平台研发效能以及技术数据驱动业务等不同的角度审视结果。 另外,关于创新,可以有几个不同维度的方法,例如通过上下左右的比较,用比较思维法来获取信息;例如移花接木,通过不同行业的分析来完成方案的嫁接;例如第一性思考,深度分析业务以及技术的场景,产出最后的方案。 二、关于技术管理三板斧这里的管理,不是团队管理,是指技术本身的管理。其实最近一年多,一直在倡导一件事情,就是技术的微观化管理,技术和其他的事务不太一样,一旦宏观化管理,不能 Deep Dive 细节,就非常容易引发各种各样的问题。例如,在研发质量中体现为研发效率降低,架构孵化。总体提供三个方面的建议: 首先,把控核心细节。软件工程这些年,本质是没变的。不管是偏互联网的部分还是偏企业级的部分,关键细节是需要严格把关的。 另外,就是数据化度量。通过数据驱动研发体系的重建,通过质量风险文化的宣导以及核心指标的跟进,起到督导的作用。 最后,就是清单革命。清单革命是一本书的名字,这里借用过来,合适是 checklist,不管是代码规约、应用规范还是稳定性治理等,都容易由于不重视或者不 check 而逐渐孵化。这时候,一个好的 checklist 非常关键重要。 三、关于技术架构三板斧关于架构,其实讲架构模式、TOGAF 架构、互联网架构等的书不少。架构本身有一些通用的方法的,但是方法一旦通用,就会偏虚无缥缈。这里总结了三个部分: 第一,是多元多维。这个概念来自穷查理宝典,因为架构需要良好的上下文输入,需要思考时间和空间维度,需要思考组织人才和 KPI ,需要思考目标过程和结果,这些和架构本身关系不大,但是关联到架构是否能够良好地落地。 第二和第三是相辅相成的,核心是分而治之,各个击破。架构本身是解决问题的过程,问题太复杂了,只能采用分而治之的办法。怎么分?利用金字塔原理,不遗漏、不重复,重点在业务架构和技术架构,同时在数据化上做思考,之后按照架构主题做拆分。怎么击破?分层架构和模块化架构,是比较通用的两个方法,业界有架构模式的参照,也可以用一些移花接木的方法。另外,关键架构主题和架构模式,也可以有checklist,方便在做架构的时候,通过清单对照不会漏掉重要内容。 四、关于赛车、赛道、赛手三段论去年六七月份,针对市面上已有的物流机器人公司,做了一个全局的分析。在分析公司上,是有一些方法论的,正当尝试总结的时候,正好看到“得到”的一个专栏,一名证券公司的分析师分析如何快速搞懂一家公司。于是,做了总结:一个前提,就是分析宏观背景,例如经济形势。接下来是三部分:赛道很容易理解,就是这个行业发展的情况,赛道够不够宽,赛道够不够长;第二个就是赛车,公司有没有核心的商业模式以及核心竞争力;第三个就是赛手,公司的人和文化,这个也非常重要。 五、关于点线面体的思考曾鸣老师的智能商业,是比较有深度的讲解互联网的一本书,中间有一篇文章是讲点线面体的,内容很不错。公司战略和个人发展战略,都可以从点、线、面、体的角度来思考。这里做一个引用: 点线面体,是一种全新的战略定位思考方法。这些年,很多人来找我讨论,公司的下一步应该怎么做。讨论多了我慢慢发现,传统的战略理论框架很多已经不适应现在新的环境了。战略的最核心是定位,很多人都耳熟能详。定位最传统的理论框架是波特提出来的成本领先、差异化和利基市场的竞争战略。虽然在未来这种定位还是大家需要去思考的,但实际上在网络时代有更重要的问题要先回答。曾鸣《智能商业》 本文作者:毕啸阅读原文 本文来自云栖社区合作伙伴“阿里技术”,如需转载请联系原作者。

May 30, 2019 · 1 min · jiezi

对接口规范的一些思考

起因团队中如果不同的项目,不同的人员可能在接口设计上有许多不统一的地方。导致了开发效率低下的问题。由于我在工作中遇到了,所以整理下来,说一说自己的一些看法。 怎样进行接口规范化因为每个人对自己使用语言有不同的理解、HTTP协议熟悉程度不同、思维逻辑、开发经验不一样。对接口规范有想法的人应该提出自己的观点,给出自己的理由。让别人去评价,讨论出一套统一的规则,最终统一成一个内部的标准。形成统一标准后由相关人员写出示例。例如前端要对GET请求针对jQuery.ajax、fetch、axios等请求库给出示例代码。以后直接参照示例代码进行开发。 由于每个项目在定义接口时有许多不同的方式,我根据以往的经验,从请求方法、请求头、请求体、响应状态码、响应体等几个方面对接口的规范说说自己的看法。 我对标准的理解我们不同的项目使用的请求方式大概有两种: GET、POSTGET、POST、PUT、DELETE如果使用前者,PSOT的URL中应该指明要执行的动作,而后者不需要指定。 POST /api/user/add HTTP/1.1POST /api/user/set HTTP/1.1POST /api/user/delete HTTP/1.1# 这里例子中约定PUT是新增,POST是修改POST /api/user HTTP/1.1PUT /api/user HTTP/1.1DELETE /api/user HTTP/1.1至于使用前者还是后者,我更倾向于前者。如果使用后者时,什么情况下使用PSOT、什么情况下使用PUT,搜索到了到了一些资料,但看不懂。 接口URL接口URL应该见名知意,有一定的规律。这里我不太懂,就不做过多赘述。 数据类型的约定前端请求中不应该有undefined,因为后端不支持(json也不支持)该数据类型。如果Content-Type为multipart/form-data,前端不应该传null,因为会被转化成字符串,后端不能判断出这是用户输入还是null类型。 每个项目应该约定请求时下面这些数据代表什么意思 null数据类型表示什么空字符串类型表示什么GET请求作用GET请求应该读取数据,不应该产生任何的“副作用”操作。这里要注意一点浏览器对URL长度是有限制的,如果查询的URL长度过长会引起不可预期的后果。可以采用POST/PUT进行查询。 方式GET请求的参数应该放在请求的URL中而不应该放在请求体中。例如下面是一个标准的这个GET请求(不相关HTTP头字段已剔除) GET /api/user?userId=12345 HTTP/1.1Host: http://www.example.comPOST/PUT/DELETE请求这三种请求方法传参数格式都相同,下面以POST为例。POST类型使用的方式非常多样,见识过各种各样奇葩的方式,也是耽误时间最长的,严重影响开发进度。这里只讨论我认为标准的方式。 作用POST请求用于新增、修改或删除数据,少数情况下用于查询数据。 方式POST请求的参数必须放在请求体中。而POST的请求方式有四种方式: application/x-www-form-urlencodedmultipart/form-dataapplication/jsontext/xml这几种方式通过HTTP头中的Content-Type头字段进行控制。 multipart/form-data我们现在使用的最多的是multipart/form-data。 POST /api/user/set HTTP/1.1Host: http://www.example.comContent-Type: multipart/form-data; boundary=----WebKitFormBoundary2KbanAZwv0mKceX0------WebKitFormBoundary2KbanAZwv0mKceX0Content-Disposition: form-data; name="userName"张三------WebKitFormBoundary2KbanAZwv0mKceX0Content-Disposition: form-data; name="userId"123456------WebKitFormBoundary2KbanAZwv0mKceX0--这种方式不适合复杂数据类型的传递,例如有个接口需要同时修改多个用户: const userList = [ { userID: 123, userName: '张三', isAdmin: true, }, { userID: 456, userName: '李四', isAdmin: false, },];那么在POST请求时只能这么做 POST /api/userlist/set HTTP/1.1Host: http://www.example.comContent-Type: multipart/form-data; boundary=----WebKitFormBoundary2KbanAZwv0mKceX0------WebKitFormBoundary2KbanAZwv0mKceX0Content-Disposition: form-data; name="userID"123,456------WebKitFormBoundary2KbanAZwv0mKceX0Content-Disposition: form-data; name="userName"张三,李四------WebKitFormBoundary2KbanAZwv0mKceX0Content-Disposition: form-data; name="isAdmin"1,0------WebKitFormBoundary2KbanAZwv0mKceX0--更重要的是这种方式不支持数据类型,传入的所有格式的数据都会转成字符串类型。后端经常要使用1表示true,需要将数组或对象拆分开。 ...

May 29, 2019 · 1 min · jiezi

Javascript编码规范

原文链接命名规范标准变量采用驼峰式命名‘ID’在变量名中全大写常量全大写,用下划线连接构造函数,大写第一个字母jquery对象必须以’$’开头命名let thisIsMyName;let goodID;let reportURL;let AndroidVersion;let iOSVersion;let MAX_COUNT = 10;function Person(name) {this.name = name;}// not goodlet body = $(‘body’);// goodlet $body = $(‘body’);局部变量命名规范s:表示字符串。例如:sName,sHtml;n:表示数字。例如:nPage,nTotal;b:表示逻辑。例如:bChecked,bHasLogin;a:表示数组。例如:aList,aGroup;r:表示正则表达式。例如:rDomain,rEmail;f:表示函数。例如:fGetHtml,fInit;o:表示以上未涉及到的其他对象,例如:oButton,oDate;函数命名小驼峰命名法,可使用常见动词约定:can 判断是否可执行某个动作,函数返回一个布尔值。true:可执行;false:不可执行has 判断是否含有某个值, 函数返回一个布尔值。true:含有此值;false:不含有此值is 判断是否为某个值,函数返回一个布尔值。true:为某个值;false:不为某个值get 获取某个之,函数返回一个非布尔值set 设置某个值,无返回值、返回是否设置成功或者返回链式对象load 加载某些数据,无返回值或者返回是否加载完成的结果// 是否可阅读function canRead() { return true;}// 获取名称function getName() { return this.name;}引用 References对所有的引用使用 const ;不要使用 var。eslint: prefer-const, no-const-assign这可以确保你无法对引用重新分配,重新分配可能会导致 bug 和难以理解的代码。// badvar a = 1;var b = 2;// goodconst a = 1;const b = 2;如果你一定需要可变动的引用,使用 let 代替 var 。eslint: no-var jscs: disallowVar// badvar count = 1;if (true) {count += 1;}// good, 使用 let.let count = 1;if (true) {count += 1;}对象Objects使用字面量语法创建对象。eslint: no-new-object// badconst item = new Object();// goodconst item = {};当创建带有动态属性名称的对象时使用计算的属性名称。它们允许你在一个地方定义一个对象的所有属性。function getKey(k) { return a key named k;}// badconst obj = {id: 5,name: ‘San Francisco’,};obj[getKey(’enabled’)] = true;// goodconst obj = { id: 5, name: ‘San Francisco’, [getKey(’enabled’)]: true,};使用对象方法速记语法。eslint: object-shorthand jscs: requireEnhancedObjectLiterals// badconst atom = {value: 1,addValue: function (value) { return atom.value + value; },};// goodconst atom = {value: 1,addValue(value) { return atom.value + value; },};使用对象属性速记语法。eslint: object-shorthand jscs: requireEnhancedObjectLiteralsconst lukeSkywalker = ‘Luke Skywalker’;// badconst obj = { lukeSkywalker: lukeSkywalker,};// goodconst obj = { lukeSkywalker,};将速记属性分组写在对象声明的开始处更容易看出哪些属性在使用速记语法const anakinSkywalker = ‘Anakin Skywalker’;const lukeSkywalker = ‘Luke Skywalker’;// badconst obj = {episodeOne: 1,twoJediWalkIntoACantina: 2,lukeSkywalker,episodeThree: 3,mayTheFourth: 4,anakinSkywalker,};// goodconst obj = {lukeSkywalker,anakinSkywalker,episodeOne: 1,twoJediWalkIntoACantina: 2,episodeThree: 3,mayTheFourth: 4,};只用引号引无效标识符的属性。eslint: quote-props jscs: disallowQuotedKeysInObjects一般来说,我们认为比较容易阅读。它改进了语法高亮显示,并且更容易被许多JS引擎优化。// badconst bad = {‘foo’: 3,‘bar’: 4,‘data-blah’: 5,};// goodconst good = {foo: 3,bar: 4,‘data-blah’: 5,};用对象展开操作符浅复制对象,优先于Object.assign 。使用对象剩余操作符来获得一个省略某些属性的新对象。// very badconst original = { a: 1, b: 2 };const copy = Object.assign(original, { c: 3 }); // original 是可变的 _delete copy.a; // so does this// badconst original = { a: 1, b: 2 };const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }// goodconst original = { a: 1, b: 2 };const copy = { …original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }const { a, …noA } = copy; // noA => { b: 2, c: 3 }数组 Arrays使用字面量创建数组。eslint: no-array-constructor// badconst items = new Array();// goodconst items = [];使用数组展开操作符 … 复制数组。// badconst len = items.length;const itemsCopy = [];let i;for (i = 0; i < len; i += 1) {itemsCopy[i] = items[i];}// goodconst itemsCopy = […items];使用展开操作符 … 代替 Array.from,来将一个类数组(array-like) 对象转换成数组。const foo = document.querySelectorAll(’.foo’);// goodconst nodes = Array.from(foo);// bestconst nodes = […foo];实用 Array.from 代替展开操作符 … 来映射迭代,因为它避免了创建媒介数组。// badconst baz = […foo].map(bar);// goodconst baz = Array.from(foo, bar);解构 Destructuring当访问和使用对象的多个属性时,请使用对象解构。eslint: prefer-destructuring jscs: requireObjectDestructuring// badfunction getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return firstName lastName;}// goodfunction getFullName(user) { const { firstName, lastName } = user; return firstName lastName;}// bestfunction getFullName({ firstName, lastName }) { return firstName lastName;}使用数组解构。eslint: prefer-destructuring jscs: requireArrayDestructuringconst arr = [1, 2, 3, 4];// badconst first = arr[0];const second = arr[1];// goodconst [first, second] = arr;使用对象解构来实现多个返回值,而不是数组解构。jscs: disallowArrayDestructuringReturn您可以随着时间的推移添加新的属性或更改排序,而不会改变调用时的位置。// badfunction processInput(input) { return [left, right, top, bottom];}// 调用者需要考虑返回数据的顺序const [left, __, top] = processInput(input);// goodfunction processInput(input) { return { left, right, top, bottom };}// 调用者只选择他们需要的数据const { left, top } = processInput(input);字符串 Strings字符串使用单引号 ‘’。eslint: quotes jscs: validateQuoteMarks// badconst name = “Capt. Janeway”;// bad - 模板字面量应该包含插值或换行符const name = Capt. Janeway;// goodconst name = ‘Capt. Janeway’;以编程方式构建字符串时,请使用模板字符串而不是字符串连接。eslint: prefer-template template-curly-spacing jscs: requireTemplateStrings/ badfunction sayHi(name) { return ‘How are you, ’ + name + ‘?’;}// badfunction sayHi(name) { return [‘How are you, ‘, name, ‘?’].join();}// badfunction sayHi(name) { return How are you, ${ name }?;}// goodfunction sayHi(name) { return How are you, name?;}永远不要在字符串上使用 eval() ,它会打开太多的漏洞。eslint: no-eval函数 Functions使用命名函数表达式而不是函数声明。eslint: func-style jscs: disallowFunctionDeclarations函数声明很容易被提升(Hoisting),这对可读性和可维护性来说都是不利的;/ badfunction foo() { // …}// badconst foo = function () { // …};// good // 用明显区别于变量引用调用的词汇命名const short = function longUniqueMoreDescriptiveLexicalFoo() { // …};用圆括号包裹立即调用函数表达式 (IIFE)。eslint: wrap-iife jscs: requireParenthesesAroundIIFE一个立即调用函数表达式是一个单独的单元 – 将函数表达式包裹在括号中,后面再跟一个调用括号,这看上去很紧凑。// 立即调用函数表达式 (IIFE)(function () {console.log(‘Welcome to the Internet. Please follow me.’);}());不要使用 arguments。可以选择 rest 语法 … 替代。使用 … 能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而 arguments 是一个类数组(Array-like)。// badfunction concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(’’);}// goodfunction concatenateAll(…args) { return args.join(’’);}使用默认参数语法,而不要使用一个变化的函数参数// really badfunction handleThings(opts) { // 更加糟糕: 如果参数 opts 是 falsy(假值) 的话,它将被设置为一个对象, // 这可能是你想要的,但它可以引起一些小的错误。 opts = opts || {}; // …}// still badfunction handleThings(opts) { if (opts === void 0) { opts = {};}// …}// goodfunction handleThings(opts = {}) { // …}始终将默认参数放在最后。// badfunction handleThings(opts = {}, name) {// …}// goodfunction handleThings(name, opts = {}) {// …}隔开函数签名,括号两边用空格隔开。// badconst f = function(){};const g = function (){};const h = function() {};// goodconst x = function () {};const y = function a() {};不要改变参数。eslint: no-param-reassign操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。(对象是引用类型)// badfunction f1(obj) {obj.key = 1;}// goodfunction f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, ‘key’) ? obj.key : 1;}箭头函数 Arrow Functions当您必须使用匿名函数(如在传递一个内联回调时),请使用箭头函数表示法。eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。// bad[1, 2, 3].map(function (x) { const y = x + 1; return x * y;});// bad[1, 2, 3].map( _ => { return 0;});// good[1, 2, 3].map((x) => { const y = x + 1; return x * y;});// good[1, 2, 3].map(() => { return 0;});如果函数体由一个返回无副作用(side effect)的expression(表达式)的单行语句组成,那么可以省略大括号并使用隐式返回。否则,保留大括号并使用 return 语句。// bad[1, 2, 3].map(number => {const nextNumber = number + 1;return A string containing the nextNumber.;});// good[1, 2, 3].map(number => A string containing the number.);如果表达式跨多行,将其包裹在括号中,可以提高可读性。// bad[‘get’, ‘post’, ‘put’].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ));// good[‘get’, ‘post’, ‘put’].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, )));如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号。// bad[1, 2, 3].map((x) => x * x);// good[1, 2, 3].map(x => x * x);// good[1, 2, 3].map(number => (A long string with the number. It’s so long that we don’t want it to take up space on the .map line!));// 总是添加()// bad[1, 2, 3].map(x => { const y = x + 1; return x * y;});// good[1, 2, 3].map((x) => { const y = x + 1; return x * y;});避免使用比较运算符(< =, >=)时,混淆箭头函数语法(=>)。// badconst itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;// badconst itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;// goodconst itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);// goodconst itemHeight = (item) => { const { height, largeSize, smallSize } = item; return height > 256 ? largeSize : smallSize;};类 Classes & 构造函数 Constructors总是使用 class。避免直接操作 prototype 。// badfunction Queue(contents = []) { this.queue = […contents];}Queue.prototype.pop = function () { const value = this.queue[0]; this.queue.splice(0, 1); return value;};// goodclass Queue { constructor(contents = []) { this.queue = […contents]; } pop() { const value = this.queue[0]; this.queue.splice(0, 1); return value; }}使用 extends 继承。因为 extends 是一个内置的原型继承方法并且不会破坏 instanceof。// badconst inherits = require(‘inherits’); function PeekableQueue(contents) { Queue.apply(this, contents);}inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function () { return this.queue[0];};// goodclass PeekableQueue extends Queue { peek() { return this.queue[0]; }}如果没有指定,类有一个默认的构造函数。一个空的构造函数或者只是委托给父类则不是必须的。eslint: no-useless-constructor// badclass Jedi { constructor() {} getName() { return this.name; }}// badclass Rey extends Jedi { constructor(…args) { super(…args); }}// goodclass Rey extends Jedi { constructor(…args) { super(…args); this.name = ‘Rey’; }}避免重复类成员。eslint: no-dupe-class-members// badclass Foo { bar() { return 1; } bar() { return 2; }}// goodclass Foo { bar() { return 1; }}// goodclass Foo { bar() { return 2; }}模块 Modules总是使用模块 (import/export) 而不是其他非标准模块系统。// badconst AirbnbStyleGuide = require(’./AirbnbStyleGuide’);module.exports = AirbnbStyleGuide.es6;// okimport AirbnbStyleGuide from ‘./AirbnbStyleGuide’;export default AirbnbStyleGuide.es6;// bestimport { es6 } from ‘./AirbnbStyleGuide’;export default es6;不要使用通配符 import(导入)。这样能确保你只有一个默认 export(导出)。// badimport * as AirbnbStyleGuide from ‘./AirbnbStyleGuide’;// goodimport AirbnbStyleGuide from ‘./AirbnbStyleGuide’;不要从 import(导入) 中直接 export(导出)。虽然一行代码简洁明了,但有一个明确的 import(导入) 方法和一个明确的 export(导出) 方法,使事情能保持一致。// bad// filename es6.jsexport { es6 as default } from ‘./AirbnbStyleGuide’;// good// filename es6.jsimport { es6 } from ‘./AirbnbStyleGuide’;export default es6;一个地方只在一个路径中 import(导入) 。// badimport foo from ‘foo’;// … 其他一些 imports … //import { named1, named2 } from ‘foo’;// goodimport foo, { named1, named2 } from ‘foo’;// goodimport foo, { named1, named2,} from ‘foo’;不要 export(导出) 可变绑定。eslint: import/no-mutable-exports一般应该避免可变性,特别是在导出可变绑定时。虽然一些特殊情况下,可能需要这种技术,但是一般而言,只应该导出常量引用。// badlet foo = 3;export { foo };// goodconst foo = 3;export { foo };在只有单个导出的模块中,默认 export(导出) 优于命名 export(导出)。eslint: import/prefer-default-export为了鼓励更多的文件只有一个 export(导出),这有利于模块的可读性和可维护性。// badexport function foo() {}// goodexport default function foo() {}将所有 import 导入放在非导入语句的上面。eslint: import/first由于 import 被提升,保持他们在顶部,防止意外的行为。// badimport foo from ‘foo’;foo.init();import bar from ‘bar’;// goodimport foo from ‘foo’;import bar from ‘bar’;foo.init();多行导入应该像多行数组和对象字面量一样进行缩进。// badimport {longNameA, longNameB, longNameC, longNameD, longNameE} from ‘path’;// goodimport {longNameA,longNameB,longNameC,longNameD,longNameE,} from ‘path’;属性 Properties使用 点语法(.) 来访问对象的属性。eslint: dot-notation jscs: requireDotNotationconst luke = {jedi: true,age: 28,};// badconst isJedi = luke[‘jedi’];// goodconst isJedi = luke.jedi;当通过变量访问属性时使用中括号 []。const luke = {jedi: true,age: 28,};function getProp(prop) {return luke[prop];}const isJedi = getProp(‘jedi’);求幂时使用求幂运算符 ** 。eslint: no-restricted-properties.// badconst binary = Math.pow(2, 10);// goodconst binary = 2 ** 10;变量 Variables总是使用 const 或 let 来声明变量。 不这样做会导致产生全局变量。 我们希望避免污染全局命名空间。eslint: no-undef prefer-const// badsuperPower = new SuperPower();// goodconst superPower = new SuperPower();将所有的 const 和 let 分组 。当你需要把已分配的变量分配给一个变量时非常有用// badlet i, len, dragonball,items = getItems(),goSportsTeam = true;// badlet i;const items = getItems();let dragonball;const goSportsTeam = true;let len;// goodconst goSportsTeam = true;const items = getItems();let dragonball;let i;let length;变量不要链式赋值。eslint: no-multi-assign链接变量赋值会创建隐式全局变量。// bad(function example() {// JavaScript 将其解析为// let a = ( b = ( c = 1 ) );// let关键字只适用于变量a;// 变量b和c变成了全局变量。let a = b = c = 1;}());console.log(a); // 抛出 ReferenceError(引用错误)console.log(b); // 1console.log(c); // 1// good(function example() {let a = 1;let b = a;let c = a;}());console.log(a); // 抛出 ReferenceError(引用错误)console.log(b); // 抛出 ReferenceError(引用错误)console.log(c); // 抛出 ReferenceError(引用错误)// 同样适用于 const避免使用一元递增和递减运算符(++, –)。根据 eslint 文档,一元递增和递减语句会受到自动插入分号的影响,并可能导致应用程序中的值递增或递减,从而导致无提示错误。使用像 num += 1 而不是 num++ 或 num ++ 这样的语句来改变你的值也更具有表现力。不允许一元递增和递减语句也会阻止您无意中预先递增/递减值,这也会导致程序中的意外行为。// badconst array = [1, 2, 3];let num = 1;num++;–num;let sum = 0;let truthyCount = 0;for (let i = 0; i < array.length; i++) { let value = array[i]; sum += value; if (value) { truthyCount++; } }// good const array = [1, 2, 3]; let num = 1; num += 1; num -= 1; const sum = array.reduce((a, b) => a + b, 0);const truthyCount = array.filter(Boolean).length;比较运算符 Comparison Operators 和 等号 Equality使用 === 和 !== 优先于 == 和 !=。eslint: eqeqeq对于布尔值使用简写,但对于字符串和数字使用显式比较。// badif (isValid === true) {// …}// goodif (isValid) {// …}// badif (name) {// …}// goodif (name !== ‘’) {// …}// badif (collection.length) {// …}// goodif (collection.length > 0) {// …}在 case 和 default 子句中,使用大括号来创建包含词法声明的语句块(例如 let, const, function, 和 class).eslint: no-case-declarations// badswitch (foo) { case 1: let x = 1; break; case 2: const y = 2; break; case 3: function f() { // … } break;default: class C {}}// goodswitch (foo) { case 1: { let x = 1; break; } case 2: { const y = 2; break; } case 3: { function f() { // … } break; } case 4: bar(); break; default: { class C {} }}三元表达式不应该嵌套,通常写成单行表达式。eslint: no-nested-ternary// badconst foo = maybe1 > maybe2? “bar”: value1 > value2 ? “baz” : null;// 拆分成2个分离的三元表达式const maybeNull = value1 > value2 ? ‘baz’ : null;// betterconst foo = maybe1 > maybe2? ‘bar’: maybeNull;// bestconst foo = maybe1 > maybe2 ? ‘bar’ : maybeNull;避免不必要的三元表达式语句。eslint: no-unneeded-ternary/ badconst foo = a ? a : b;const bar = c ? true : false;const baz = c ? false : true;// goodconst foo = a || b;const bar = !!c;const baz = !c;当运算符混合在一个语句中时,请将其放在括号内。混合算术运算符时,不要将 和 % 与 + , -,,/ 混合在一起。eslint: no-mixed-operators这可以提高可读性,并清晰展现开发者的意图。/ badconst foo = a && b < 0 || c > 0 || d + 1 === 0;// badconst bar = a ** b - 5 % d;// badif (a || b && c) {return d;}// goodconst foo = (a && b < 0) || c > 0 || (d + 1 === 0);// goodconst bar = (a ** b) - (5 % d);// goodif ((a || b) && c) {return d;}// goodconst bar = a + b / c * d;代码块 Blocks使用大括号包裹所有的多行代码块。eslint: nonblock-statement-body-position// badif (test) return false;// goodif (test) return false;// goodif (test) { return false;}// badfunction foo() { return false; }// goodfunction bar() { return false;}如果通过 if 和 else 使用多行代码块,把 else 放在 if 代码块闭合括号的同一行。eslint: brace-style// badif (test) { thing1(); thing2();}else { thing3();}// goodif (test) { thing1(); thing2();} else { thing3();}如果一个 if 块总是执行一个 return 语句,后面的 else 块是不必要的。在 else if 块中的 return,可以分成多个 if 块来 return 。eslint: no-else-return// badfunction foo() { if (x) { return x; } else { return y; }}// badfunction cats() { if (x) { return x; } else if (y) { return y; }}// badfunction dogs() { if (x) { return x; } else { if (y) { return y; }}}// goodfunction foo() { if (x) { return x; }return y;}// goodfunction cats() { if (x) { return x; } if (y) { return y; }}//goodfunction dogs(x) { if (x) { if (z) { return y; }} else { return z; }}控制语句 Control Statements如果您的控制语句(if, while 的)太长或超过最大行长度,那么每个(分组)条件可以放单独一行。逻辑运算符应该放在每行起始处。// badif ((foo === 123 || bar === ‘abc’) && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { thing1();}// badif (foo === 123 && bar === ‘abc’) { thing1();}// badif (foo === 123 && bar === ‘abc’) { thing1();}// badif ( foo === 123 && bar === ‘abc’) { thing1();}// goodif ( foo === 123 && bar === ‘abc’) { thing1();}// goodif ( (foo === 123 || bar === “abc”) && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) { thing1();}// goodif (foo === 123 && bar === ‘abc’) { thing1();}注释 Comments多行注释使用 / … /。/** @param {Grid} grid 需要合并的Grid* @param {Array} cols 需要合并列的Index(序号)数组;从0开始计数,序号也包含。* @param {Boolean} isAllSome 是否2个tr的cols必须完成一样才能进行合并。true:完成一样;false(默认):不完全一样* @return void* @author 单志永 2018/11/8*/function mergeCells(grid, cols, isAllSome) { // Do Something}单行注释使用 // 。将单行注释放在续注释的语句上方。在注释之前放置一个空行,除非它位于代码块的第一行。// badconst active = true; // is current tab// good// is current tabconst active = true;// badfunction getType() { console.log(‘fetching type…’); // set the default type to ’no type’ const type = this.type || ’no type’; return type;}// goodfunction getType() { console.log(‘fetching type…’); // set the default type to ’no type’ const type = this.type || ’no type’; return type;}// also goodfunction getType() { // set the default type to ’no type’ const type = this.type || ’no type’; return type;}所有注释符和注释内容用一个空格隔开,让它更容易阅读。eslint: spaced-comment// bad//is current tabconst active = true;// good// is current tabconst active = true;// bad/make() returns a new elementbased on the passed-in tag name/function make(tag) {// …return element;}// good/** make() returns a new element* based on the passed-in tag name*/function make(tag) {// …return element;}给注释增加 FIXME 或 TODO 的前缀,可以帮助其他开发者快速了解这个是否是一个需要重新复查的问题,或是你正在为需要解决的问题提出解决方案。这将有别于常规注释,因为它们是可操作的。使用 FIXME – need to figure this out 或者 TODO – need to implement。使用 // FIXME: 来标识需要修正的问题。注:如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明。lass Calculator extends Abacus { constructor() { super(); // FIXME: shouldn’t use a global here total = 0; }}使用 // TODO: 来标识需要实现的问题。注:如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明。class Calculator extends Abacus { constructor() { super(); // TODO: total should be configurable by an options param this.total = 0; }}空格 Whitespace使用 2 个空格作为缩进// badfunction foo() {∙∙∙∙let name;}// badfunction bar() {∙let name;}// goodfunction baz() {∙∙let name;}在大括号前放置 1 个空格。eslint: space-before-blocks jscs: requireSpaceBeforeBlockStatements// badfunction test(){ console.log(’test’);}// goodfunction test() { console.log(’test’);}// baddog.set(‘attr’,{ age: ‘1 year’, breed: ‘Bernese Mountain Dog’,});// gooddog.set(‘attr’, { age: ‘1 year’, breed: ‘Bernese Mountain Dog’,});在控制语句(if、while 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。eslint: keyword-spacing jscs: requireSpaceAfterKeywords// badif(isJedi) { fight ();}// goodif (isJedi) { fight();}// badfunction fight () { console.log (‘Swooosh!’);}// goodfunction fight() { console.log(‘Swooosh!’);}使用空格把运算符隔开。eslint: space-infix-ops jscs: requireSpaceBeforeBinaryOperators, requireSpaceAfterBinaryOperators// badconst x=y+5;// goodconst x = y + 5;在文件末尾插入一个空行。eslint: eol-last// badimport { es6 } from ‘./AirbnbStyleGuide’;// …export default es6;// badimport { es6 } from ‘./AirbnbStyleGuide’;// …export default es6;// goodimport { es6 } from ‘./AirbnbStyleGuide’;// …export default es6;长方法链式调用时使用缩进(2个以上的方法链式调用)。使用一个点 . 开头,强调该行是一个方法调用,不是一个新的声明。eslint: newline-per-chained-call no-whitespace-before-property// bad$(’#items’).find(’.selected’).highlight().end().find(’.open’).updateCount();// bad$(’#items’).find(’.selected’).highlight().end().find(’.open’).updateCount();// good$(’#items’).find(’.selected’).highlight().end().find(’.open’).updateCount();// badconst leds = stage.selectAll(’.led’).data(data).enter().append(‘svg:svg’).classed(’led’, true).attr(‘width’, (radius + margin) * 2).append(‘svg:g’).attr(’transform’, translate(${radius + margin},${radius + margin})).call(tron.led);// goodconst leds = stage.selectAll(’.led’).data(data).enter().append(‘svg:svg’).classed(’led’, true).attr(‘width’, (radius + margin) * 2).append(‘svg:g’).attr(’transform’, translate(${radius + margin},${radius + margin})).call(tron.led);// goodconst leds = stage.selectAll(’.led’).data(data);不要在圆括号内加空格。// badfunction bar( foo ) { return foo;}// goodfunction bar(foo) { return foo;}// badif ( foo ) { console.log(foo);}// goodif (foo) { console.log(foo);}不要在中括号内添加空格。eslint: array-bracket-spacing jscs: disallowSpacesInsideArrayBrackets// badconst foo = [ 1, 2, 3 ];console.log(foo[ 0 ]);// goodconst foo = [1, 2, 3];console.log(foo[0]);在大括号内添加空格// badconst foo = {clark: ‘kent’};// goodconst foo = { clark: ‘kent’ };类型转换 Type Casting & Coercion在声明语句的开始处就执行强制类型转换.字符串:eslint: no-new-wrappers// => this.reviewScore = 9;// badconst totalScore = new String(this.reviewScore); // typeof totalScore 是 “object” 而不是 “string”// badconst totalScore = this.reviewScore + ‘’; // 调用 this.reviewScore.valueOf()// badconst totalScore = this.reviewScore.toString(); // 不能保证返回一个字符串// goodconst totalScore = String(this.reviewScore);使数字: 使用 Number 进行转换,而 parseInt 则始终以基数解析字串。eslint: radix no-new-wrappersconst inputValue = ‘4’;// badconst val = new Number(inputValue);// badconst val = +inputValue;// badconst val = inputValue >> 0;// badconst val = parseInt(inputValue);// goodconst val = Number(inputValue);// goodconst val = parseInt(inputValue, 10);布尔值:eslint: no-new-wrappersconst age = 0;// badconst hasAge = new Boolean(age);// goodconst hasAge = Boolean(age);// bestconst hasAge = !!age;命名规则 Naming Conventions避免使用单字母名称。使你的命名具有描述性。eslint: id-length// badfunction q() {// …}// goodfunction query() {// …}当命名对象,函数和实例时使用驼峰式命名。eslint: camelcase jscs: requireCamelCaseOrUpperCaseIdentifiers// badconst OBJEcttsssss = {};const this_is_my_object = {};function c() {}// goodconst thisIsMyObject = {};function thisIsMyFunction() {}当命名构造函数或类的时候使用 PascalCase 式命名,(注:即单词首字母大写)。eslint: new-cap// badfunction user(options) { this.name = options.name;}const bad = new user({ name: ’nope’,});// goodclass User { constructor(options) { this.name = options.name; }}const good = new User({ name: ‘yup’,});当 导出(export) 一个默认函数时使用驼峰式命名。你的文件名应该和你的函数的名字一致。function makeStyleGuide() {// …}export default makeStyleGuide;当导出一个 构造函数 / 类 / 单例 / 函数库 / 纯对象时使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。const AirbnbStyleGuide = { es6: { },};export default AirbnbStyleGuide;存取器 Accessors属性的存取器函数不是必须的。別使用 JavaScript 的 getters/setters,因为它们会导致意想不到的副作用,而且很难测试,维护和理解。相反,如果要使用存取器函数,使用 getVal() 及 setVal(‘hello’)。// badclass Dragon { get age() { // … } set age(value) { // … }}// goodclass Dragon { getAge() { // … } setAge(value) { // … }}如果属性/方法是一个 boolean, 使用 isVal() 或 hasVal() 方法。// badif (!dragon.age()) { return false;}// goodif (!dragon.hasAge()) { return false;}) ...

February 27, 2019 · 13 min · jiezi

git commit 代码提交规范

git commit 代码提交规范一、为什么需要制定提交规范?在团队协作开发时,每个人提交代码时都会写 commit message。每个人都有自己的书写风格,翻看我们组的git log, 可以说是五花八门,十分不利于阅读和维护。一般来说,大厂都有一套的自己的提交规范,尤其是在一些大型开源项目中,commit message 都是十分一致的。因此,我们需要制定统一标准,促使团队形成一致的代码提交风格,更好的提高工作效率,成为一名有追求的工程师。二、业界通用的 git 提交规范有哪些?1. commitizenAngularJS 在 github上 的提交记录被业内许多人认可,逐渐被大家引用。格式:type(scope) : subject( 1 ) type(必须) : commit 的类别,只允许使用下面几个标识:feat : 新功能fix : 修复bugdocs : 文档改变style : 代码格式改变refactor : 某个已有功能重构perf : 性能优化test : 增加测试build : 改变了build工具 如 grunt换成了 npmrevert : 撤销上一次的 commitchore : 构建过程或辅助工具的变动( 2 ) scope(可选) : 用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。( 3 ) subject(必须) : commit 的简短描述,不超过50个字符。commitizen 是一个撰写合格 Commit message 的工具,遵循 Angular 的提交规范。安装:全局安装 commitizennpm install -g commitizen进入项目文件夹,运行如下命令:commitizen init cz-conventional-changelog –save –save-exact使用:用 git cz 命令取代 git commit(先使用git add),这时会出现如下选项:( 1 )选择 type( 2 )填写 scope(选填)? What is the scope of this change (e.g. component or file name)? (press enter to skip)core( 3 )填写 subject? Write a short, imperative tense description of the change:set a to b完成,运行 git log 命令,查看我们刚才提交的 commit message,如下:fix(core): set a to b优点:符合业内标准(许多项目使用 AngularJS 的commit 规范)提交过程更加规范(使用 commitizen 规范工具,风格统一)能够生成风格统一的 commit log(type(scope):subject)缺点:需要安装 commitizen 工具包,使项目更大、更重了(适合大型开源项目)提交过程受约束较大有一定的学习成本2. 设置 git commit 模板步骤如下:( 1 ) 建立模板文件在项目中建立 .git_template 文件,内容可以自定义:type:scope:subject:( 2 ) 设置模板运行如下命令:git config commit.template .git_template // 当前项目<!– git config commit.template .git_template // 全局设置 –>( 3 ) 提交代码先使用 git add 添加代码使用 git commit 按照模板填写最后 git push 推送到远端优点:规则可配置,更自由配置方式简洁(只需添加配置文件)缺点:便利性差,每次都要用 vim 编辑器填写模板易出错,没有可靠的校验方式三、制定适合我们的 git commit 提交规范第二章中提到的两种业内普遍使用的规范,都不完全适合我们。第一种方式适合大型开源项目,我们如果也照搬会比较麻烦,但我们可以借鉴 type(scope): subject 的提交格式,也算是与大厂同步;第二种方式虽然自由,但是也不比较麻烦,要配置模板。因此,我们只模仿 type(scope): subject 的提交格式,不使用工具 or 模板校验,靠大家自觉遵守即可。格式type: description1. type 类型type 是 commit 的类别,只允许如下几种标识:fix: 修复bugadd: 新功能update: 更新style : 代码格式改变test: 增加测试代码revert: 撤销上一次的commitbuild: 构建工具或构建过程等的变动,如:gulp 换成了 webpack,webpack 升级等2. descriptiondescription 是对本次提交的简短描述。不超过50个字符。推荐以动词开头,如: 设置、修改、增加、删减、撤销等最后,既然制定了规则,大家就遵守起来吧~~~~ ...

December 1, 2018 · 1 min · jiezi