为了保证代码的质量,团队代码风格统一,昨天下午部门进行了前端代码评审,评审的内容要求为以下三点:
1、界面
2、业务代码
3、样式
出现的问题会议上已经提出明确的调整建议。针对此次的评审会议,觉得前端人员在开发的过程中应注意以下几点:
1、考虑组件的可封装性,以及组件封装的实用性
2、验证规则的收集,与封装通用
3、通用方法的封装:日期转换.....
4、命名规范方面
5、业务的通用方法封装,针对业务
6、多个系统中操作按钮的风格统一、界面样式统一
7、关于列表的标题样式、筛选条件、排序、分页等
.....
附上:前端代码评审checklist清单作为参考
eslint 检查的规范继承自 eslint-config-standard 检验规则,具体的规则介绍参照链接:https://cn.eslint.org/docs/ru... ,这里及以下部分不再重复介绍这些检验规则。
stylelint 检查的规范继承自 stylelint-config-standard 检验规则,具体的规则介绍参照链接:https://www.npmjs.com/package... ,这里及以下部分不再重复介绍这些检验规则。
推荐:
studentInfot
推荐:
const Car = { make: "Honda", model: "Accord", color: "Blue" };
不推荐:
const Car = { carMake: "Honda", carModel: "Accord", carColor: "Blue" };
推荐:
.block__element{} .block--modifier{}
命名需要符合语义化,如果函数命名,可以采用加上动词前缀:
动词 | 含义 |
---|---|
can | 判断是否可执行某个动作 |
has | 判断是否含有某个值 |
is | 判断是否为某个值 |
get | 获取某个值 |
set | 设置某个值 |
推荐:
//是否可阅读 function canRead(){ return true; } //获取姓名 function getName{ return this.name }
每个常量应该命名,不然看代码的人不知道这个常量表示什么意思。
推荐:
const COL_NUM = 10; let row = Math.ceil(num/COL_NUM);
不推荐:
let row = Math.ceil(num/10);
创建对象和数组推荐使用字面量,因为这不仅是性能最优也有助于节省代码量。
推荐:
let obj = { name:'tom', age:15, sex:'男' }
不推荐:
let obj = {}; obj.name = 'tom'; obj.age = 15; obj.sex = '男';
推荐:
const menuConfig = { title: "Order", // User did not include 'body' key buttonText: "Send", cancellable: true }; function createMenu(config) { config = Object.assign( { title: "Foo", body: "Bar", buttonText: "Baz", cancellable: true }, config ); // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true} // ... } createMenu(menuConfig);
不推荐:
const menuConfig = { title: null, body: "Bar", buttonText: null, cancellable: true }; function createMenu(config) { config.title = config.title || "Foo"; config.body = config.body || "Bar"; config.buttonText = config.buttonText || "Baz"; config.cancellable = config.cancellable !== undefined ? config.cancellable : true; } createMenu(menuConfig);
对象成员嵌套越深,读取速度也就越慢。所以好的经验法则是:如果在函数中需要多次读取一个对象属性,最佳做法是将该属性值保存在局部变量中,避免多次查找带来的性能开销。
推荐:
let person = { info:{ sex:'男' } } function getMaleSex(){ let sex = person.info.sex; if(sex === '男'){ console.log(sex) } }
不推荐:
let person = { info:{ sex:'男' } } function getMaleSex(){ if(person.info.sex === '男'){ console.log(person.info.sex) } }
当需要将浮点数转换成整型时,应该使用Math.floor()或者Math.round(),而不是使用parseInt()将字符串转换成数字。Math是内部对象,所以Math.floor()`其实并没有多少查询方法和调用时间,速度是最快的。
推荐:
let num = Math.floor('1.6');
不推荐:
let num = parseInt('1.6');
函数参数越少越好,如果参数超过两个,要使用 ES6的解构语法,不用考虑参数的顺序。
推荐:
function createMenu({ title, body, buttonText, cancellable }) { // ... } createMenu({ title: 'Foo', body: 'Bar', buttonText: 'Baz', cancellable: true });
不推荐:
function createMenu(title, body, buttonText, cancellable) { // ... }
使用参数默认值 替代 使用条件语句进行赋值。
推荐:
function createMicrobrewery(name = "Hipster Brew Co.") { // ... }
不推荐:
function createMicrobrewery(name) { const breweryName = name || "Hipster Brew Co."; // ... }
这是一条在软件工程领域流传久远的规则。严格遵守这条规则会让你的代码可读性更好,也更容易重构。如果违反这个规则,那么代码会很难被测试或者重用 。
在 JavaScript 中,永远不要污染全局,会在生产环境中产生难以预料的 bug。举个例子,比如你在 Array.prototype 上新增一个 diff 方法来判断两个数组的不同。而你同事也打算做类似的事情,不过他的 diff 方法是用来判断两个数组首位元素的不同。很明显你们方法会产生冲突,遇到这类问题我们可以用 ES2015/ES6 的语法来对 Array 进行扩展。
推荐:
class SuperArray extends Array { diff(comparisonArray) { const hash = new Set(comparisonArray); return this.filter(elem => !hash.has(elem)); } }
不推荐:
Array.prototype.diff = function diff(comparisonArray) { const hash = new Set(comparisonArray); return this.filter(elem => !hash.has(elem)); };
函数式变编程可以让代码的逻辑更清晰更优雅,方便测试。
推荐:
const programmerOutput = [ { name: 'Uncle Bobby', linesOfCode: 500 }, { name: 'Suzie Q', linesOfCode: 1500 }, { name: 'Jimmy Gosling', linesOfCode: 150 }, { name: 'Gracie Hopper', linesOfCode: 1000 } ]; let totalOutput = programmerOutput .map(output => output.linesOfCode) .reduce((totalLines, lines) => totalLines + lines, 0)
不推荐:
const programmerOutput = [ { name: 'Uncle Bobby', linesOfCode: 500 }, { name: 'Suzie Q', linesOfCode: 1500 }, { name: 'Jimmy Gosling', linesOfCode: 150 }, { name: 'Gracie Hopper', linesOfCode: 1000 } ]; let totalOutput = 0; for (let i = 0; i < programmerOutput.length; i++) { totalOutput += programmerOutput[i].linesOfCode; }
为了让代码更简洁易读,如果你的函数中出现了条件判断,那么说明你的函数不止干了一件事情,违反了函数单一原则 ;并且绝大数场景可以使用多态替代
推荐:
class Airplane { // ... } // 波音777 class Boeing777 extends Airplane { // ... getCruisingAltitude() { return this.getMaxAltitude() - this.getPassengerCount(); } } // 空军一号 class AirForceOne extends Airplane { // ... getCruisingAltitude() { return this.getMaxAltitude(); } } // 赛纳斯飞机 class Cessna extends Airplane { // ... getCruisingAltitude() { return this.getMaxAltitude() - this.getFuelExpenditure(); } }
不推荐:
class Airplane { // ... // 获取巡航高度 getCruisingAltitude() { switch (this.type) { case '777': return this.getMaxAltitude() - this.getPassengerCount(); case 'Air Force One': return this.getMaxAltitude(); case 'Cessna': return this.getMaxAltitude() - this.getFuelExpenditure(); } } }
代码中使用了定时器 setTimeout 和 setInterval,需要在不使用时进行清除。
利用scss中的变量配置,可以进行项目的颜色、字体大小统一更改(换肤),有利于后期项目的维护。
推荐:
$--color-success: #67C23A; $--color-warning: #E6A23C; $--color-danger: #F56C6C; $--color-info: #909399;
scss中的@import规则在生成css文件时就把相关文件导入进来。这意味着所有相关的样式被归纳到了同一个css文件中,而无需发起额外的下载请求,在构建我们自己的组件库时推荐使用。
@import "./base.scss"; @import "./pagination.scss"; @import "./dialog.scss"; @import "./autocomplete.scss"; @import "./dropdown.scss"; @import "./dropdown-menu.scss";
scss局部文件的文件名以下划线开头。这样,scss就不会在编译时单独编译这个文件输出css,而只把这个文件用作导入。
推荐:
scss的嵌套和父选择器标识符&能解决BEM命名的冗长,且使样式可读性更高。
推荐:
.el-input { display: block; &__inner { text-align: center; } }
mixin混合器用来实现大段样式的重用,减少代码的冗余,且支持传参。
@mixin button-size($padding-vertical, $padding-horizontal, $font-size, $border-radius) { padding: $padding-vertical $padding-horizontal; font-size: $font-size; border-radius: $border-radius; &.is-round { padding: $padding-vertical $padding-horizontal; } } @include m(medium) { @include button-size($--button-medium-padding-vertical, $--button-medium-padding-horizontal, $--button-medium-font-size, $--button-medium-border-radius); } @include m(small) { @include button-size($--button-small-padding-vertical, $--button-small-padding-horizontal, $--button-small-font-size, $--button-small-border-radius); }
(1)使用@extend产生 DRY CSS风格的代码(Don't repeat yourself)
(2)@mixin主要的优势就是它能够接受参数。如果想传递参数,你会很自然地选择@mixin而不是@extend
推荐:
.common-mod { height: 250px; width: 50%; background-color: #fff; text-align: center; } .show-mod--right { @extend .common-mod; float: right; } .show-mod--left { @extend .common-mod; }
插值能动态定义类名的名称,当有两个页面的样式类似时,我们会将类似的样式抽取成页面混合器,但两个不同的页面样式的命名名称根据BEM命名规范不能一样,这时我们可使用插值进行动态命名。
推荐:
@mixin home-content($class) { .#{$class} { position: relative; background-color: #fff; overflow-x: hidden; overflow-y: hidden; &--left { margin-left: 160px; } &--noleft { margin-left: 0; } } }
可通过each遍历、map数据类型、@mixin/@include混合器、#{}插值 结合使用,从而减少冗余代码,使代码更精简。
推荐:
$img-list: ( (xlsimg, $papers-excel), (xlsximg, $papers-excel), (gifimg, $papers-gif), (jpgimg, $papers-jpg), (mp3img, $papers-mp3), (mp4img, $papers-mp3), (docimg, $papers-word), (docximg, $papers-word), (rarimg, $papers-zip), (zipimg, $papers-zip), (unknownimg, $papers-unknown) ); @each $label, $value in $img-list { .com-hwicon__#{$label} { @include commonImg($value); } }
scss自带函数的应用,从而进行相关的计算,例如 mix函数的使用如下。
@include m(text) { &:hover, &:focus { color: mix($--color-white, $--color-primary, $--button-hover-tint-percent); border-color: transparent; background-color: transparent; } &:active { color: mix($--color-black, $--color-primary, $--button-active-shade-percent); border-color: transparent; background-color: transparent; } }
gulp-sass插件能实时监测scss代码检查其语法错误并将其编译成css代码,帮助开发人员检查scss语法的准确性,且其是否符合我们的预期,相关配置如下:
gulp.task('gulpsass', function() { return gulp.src('src/style/components/hwIcon.scss') .pipe(gulpsass().on('error', gulpsass.logError)) .pipe(gulp.dest('src/style/dest')); }); gulp.task('watch', function() { gulp.watch('src/style/components/hwIcon.scss', ['gulpsass']); });
我们开发过程中自定义的组件的名称需要为多个单词,这样做可以避免跟现有的以及未来的HTML元素相冲突,因为所有的 HTML 元素名称都是单个单词的。
推荐:
Vue.component('todo-item', { // ... }) export default { name: 'TodoItem', // ... }
不推荐:
Vue.component('todo', { // ... }) export default { name: 'Todo', // ... }
当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。 因为如果直接是一个对象的话,子组件之间的属性值会互相影响。
推荐:
export default { data () { return { foo: 'bar' } } }
不推荐:
export default { data: { foo: 'bar' } }
prop 的定义应该尽量详细,至少需要指定其类型。
推荐:
props: { status: String } // 更好的做法! props: { status: { type: String, required: true, validator: function (value) { return [ 'syncing', 'synced', 'version-conflict', 'error' ].indexOf(value) !== -1 } } }
不推荐:
props: ['status']
v-for 中总是有设置 key 值。在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态。
推荐:
<ul> <li v-for="todo in todos" :key="todo.id"> {{ todo.text }} </li> </ul>
不推荐:
<ul> <li v-for="todo in todos"> {{ todo.text }} </li> </ul>
组件名应该倾向于完整单词而不是缩写,编辑器中的自动补全已经让书写长命名的代价非常之低了,而其带来的明确性却是非常宝贵的。不常用的缩写尤其应该避免。
推荐:
components/ |- StudentDashboardSettings.vue
|- UserProfileOptions.vue
不推荐:
components/ |- SdSettings.vue
|- UProfOpts.vue
在 JavaScript 中,用多行分隔对象的多个属性是很常见的最佳实践,因为这样更易读。
推荐:
<MyComponent foo="a" bar="b" baz="c" />
不推荐:
<MyComponent foo="a" bar="b" baz="c"/>
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
推荐:
<!-- 在模板中 --> {{ normalizedFullName }} // 复杂表达式已经移入一个计算属性 computed: { normalizedFullName: function () { return this.fullName.split(' ').map(function (word) { return word[0].toUpperCase() + word.slice(1) }).join(' ') } }
不推荐:
{{ fullName.split(' ').map(function (word) { return word[0].toUpperCase() + word.slice(1) }).join(' ') }}
应该把复杂计算属性分割为尽可能多的更简单的属性。
推荐:
computed: { basePrice: function () { return this.manufactureCost / (1 - this.profitMargin) }, discount: function () { return this.basePrice * (this.discountPercent || 0) }, finalPrice: function () { return this.basePrice - this.discount } }
不推荐:
computed: { price: function () { var basePrice = this.manufactureCost / (1 - this.profitMargin) return ( basePrice - basePrice * (this.discountPercent || 0) ) } }
指令推荐都使用缩写形式,(用 : 表示 v-bind: 、用 @ 表示 v-on: 和用 # 表示 v-slot:)。
推荐:
<input @input="onInput" @focus="onFocus" >
不推荐:
<input v-on:input="onInput" @focus="onFocus" >
单文件组件应该总是让标签顺序保持为 <template> 、<script>、 <style> 。
推荐:
<!-- ComponentA.vue --> <template>...</template> <script>/* ... */</script> <style>/* ... */</style>
不推荐:
<!-- ComponentA.vue --> <template>...</template> <style>/* ... */</style> <script>/* ... */</script>
父子组件的通信推荐使用 prop和 emit ,而不是this.$parent或改变 prop;
兄弟组件之间的通信推荐使用 EventBus( on),而不是滥用 vuex;
祖孙组件之间的通信推荐使用 listeners 或 provide / inject(依赖注入) ,而不是滥用 vuex;
页面跳转,例如 A 页面跳转到 B 页面,需要将 A 页面的数据传递到 B 页面,推荐使用 路由参数进行传参,而不是将需要传递的数据保存 vuex,然后在 B 页面取出 vuex的数据,因为如果在 B 页面刷新会导致 vuex 数据丢失,导致 B 页面无法正常显示数据。
推荐:
let id = ' 123'; this.$router.push({name: 'homeworkinfo', query: {id:id}});
script 标签内部的声明顺序如下:
data > prop > components > filter > computed > watch > 钩子函数(钩子函数按其执行顺序) > methods
推荐:
如果运行时,需要非常频繁地切换,推荐使用 v-show 比较好;如果在运行时,条件很少改变,则推荐使用 v-if 比较好。
因为团队现在使用 vue 框架,所以在项目开发中尽量使用 vue 的特性去满足我们的需求,尽量(不到万不得已)不要手动操作DOM,包括:增删改dom元素、以及更改样式、添加事件等。
很多时候有些代码已经没有用了,但是没有及时去删除,这样导致代码里面包括很多注释的代码块,好的习惯是提交代码前记得删除已经确认弃用的代码,例如:一些调试的console语句、无用的弃用代码。
代码注释不是越多越好,保持必要的业务逻辑注释,至于函数的用途、代码逻辑等,要通过语义化的命令、简单明了的代码逻辑,来让阅读代码的人快速看懂。