前端规范

 

一、  命名规则

“计算机科学只存在两个难题:缓存失效和命名。” ——Phil KarIton

 

代码和文件命名要严谨,禁止使用中文和英文混合的方式,更不允许直接用汉字的方式命名,无法用英文表达的可以使用拼音代替(如weibo、wuhan),代替的时候不要用缩写;英文单词不确定的可以多查翻译软件,不要有低级的拼写错误。

 

强烈推荐安装 Code Spell Checker 插件,使用方式参考第十四章

 

1.  项目命名

1、全部小写的方式,中划线分割

2项目名称规范(则写无则不写):scrm/tcloud(事业部)-car/retail/qw(业务线名称)-wit/royal/gwm(项目名称)-mc/oc(微服务)-web/h5/applet/iOS/Android/app(客户端)

 

//❌bad
scrm_web_chery-gf
scrmWebCheryGf
ScrmWebCheryGf

//✔good
scrm-cherygf-web
scrm-royal-web

 

注意:1、微服务名称可有可无;

      2、项目名称私化的时候才有;

      3、项目中统一用中划线禁止用下划线命名(自动化变量k8s容器名称不识别)前端Gitlab-CICD&SonarQube使用

      4、项目名称不要使用数字命名;例如:71360等;

2.  目录命名

文件夹的命名统一用小写,保证项目有良好的可移植性,可跨平台 (windows/mac/linux)运行没有问题,文件内文件的命名按照组件的命名规范来执行相关参考

基于同一的命名规范,引用也需要注意大小写问题

项目目录规范:

├── build                      // 构建相关  

├── config                     // 配置相关

├── src                        // 源代码

│   ├── api                    // 所有请求

│   ├── assets                 // 主题 字体等静态资源

│   ├── components             // 全局公用组件

│   ├── directive              // 全局指令

│   ├── filtres                // 全局 filter

│   ├── icons                  // 项目所有 svg icons

│   ├── lang                   // 国际化 language

│   ├── mock                   // 项目mock 模拟数据

│   ├── router                 // 路由

│   ├── store                  // 全局 store管理

│   ├── styles                 // 全局样式

│   ├── utils                  // 全局公用方法

│   ├── vendor                 // 公用vendor

│   ├── views                   // view

│   ├── App.vue                // 入口页面

│   ├── main.js                // 入口 加载组件 初始化等

│   └── permission.js          // 权限管理

├── public                     // 第三方不打包资源

│   └── Tinymce                // 富文本

│   └── favicon.ico              // favicon图标

│   └── index.html              // 入口html

├── .babelrc                   // babel-loader 配置

├── eslintrc.js                // eslint 配置项

├── .gitignore                 // git 忽略项

└── package.json               // package.json

 

3.  文件命名

3.1.  js、ts、css、scss、htmlpng文件命名

全部采用小写的方式,以中划线分割

 

//❌bad
tableMixin.js/CustomerSOP.html

//✔good
table-mixin.js/global-color.scss/index.html/company-brand.png

 

 

3.2.  vue文件

3.2.1.  基础组件名

应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。

 

//❌bad
components/
|- MyButton.vue
|- VueTable.vue
|- Icon.vue

//✔good
components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue

 

 

3.2.2.  单例组件名

只应该拥有单个活跃实例的组件应该以 The 前缀命名,以示其唯一性。

这不意味着组件只可用于一个单页面,而是每个页面只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的,而不是它们在你的应用中的上下文。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,只是目前在每个页面里只使用一次。

 

 

//❌bad
components/
|- Heading.vue
|- MySidebar.vue

//✔good
components/
|- TheHeading.vue
|- TheSidebar.vue

 

 

3.2.3.  紧密耦合的组件名

和父组件紧密耦合的子组件应该以父组件名作为前缀命名。

 

如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。

 

 

//❌bad
components/
|- SearchSidebar.vue
|- NavigationForSearchSidebar.vue

//✔good
components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue

 

 

3.2.4.  组件名中的单词顺序

组件名应该以高级别的 (通常是一般化描述的) 单词开头,以描述性的修饰词结尾。

 

//❌bad
components/
|- ClearSearchButton.vue
|- ExcludeFromSearchInput.vue
|- LaunchOnStartupCheckbox.vue
|- RunSearchButton.vue
|- SearchInput.vue
|- TermsCheckbox.vue

//✔good
components/
|- SearchButtonClear.vue
|- SearchButtonRun.vue
|- SearchInputQuery.vue
|- SearchInputExcludeGlob.vue
|- SettingsCheckboxTerms.vue
|- SettingsCheckboxLaunchOnStartup.vue

 

 

3.2.5.  模板中的组件名大小写

总是 PascalCase 的

 

//❌bad
<!-- 在单文件组件和字符串模板中 -->
<mycomponent/>
<!-- 在单文件组件和字符串模板中 -->
<myComponent/>

//✔good
<!-- 在单文件组件和字符串模板中 -->
<MyComponent/>

 

 

3.2.6.  完整单词的组件名

组件名应该倾向于完整单词而不是缩写。

 

//❌bad
components/
|- SdSettings.vue
|- UProfOpts.vue

//✔good
omponents/
|- StudentDashboardSettings.vue
|- UserProfileOptions.vue

 

 

4.  参数和属性命名

4.1.  js/ts和vue命名

4.1.1.  变量

命名方式:小驼峰式命名法.

命名规范:前缀名词

命名建议:语义化

eslint规范:prefer-constno-var

 

//❌bad
var a = 1
var b = 2

//✔good
const a = 1
const b = 2

//❌bad
var count = 1
if (count < 10) {
  count += 1
}

//✔good
let count = 1
if (count < 10) {
  count += 1
}

 

 

4.1.2.  常量

命名方式:全部大写

命名规范:使用大写字母和下划线来组合命名,下划线用以分割单词

命名建议:语义化

 

const MAX_COUNT = 10;

const URL = ‘http://www.foreverz.com'

 

4.1.3.  函数

命名方式:小驼峰式命名法。

命名规范:前缀应当为动词。

命名建议:语义化

可以参考如下的动作

eslint规则:camelcase

代码起名常用动词及其含义

动词

含义

返回值

can

判断是否可执行某个动作(权限)

函数返回一个布尔值。true:可执行;false:不可执行

has

判断是否含有某个值

函数返回一个布尔值。true:含有此值;false:不含有此值

is

判断是否为某个值

函数返回一个布尔值。true:为某个值;false:不为某个值

get

获取某个值

函数返回一个非布尔值

set

设置某个值

无返回值、返回是否设置成功或者返回链式对象

load/save

加载/保存某些数据

无返回值或者返回是否加载完成的结果

handle

处理某些数据

无返回值或者返回是否处理完成的结果

add update delete remove

操作数据

 

create / destory

创建 / 销毁

 

start / stop

启动 / 停止

 

open / close 

打开 / 关闭

 

read / write

读取 / 写入 

 

begin / end

开始 / 结束

 

backup / restore 

备份 / 恢复

 

import / export

导入 /  导出

 

split / merge

分割 /  合并

 

inject / extract 

注入 / 提取

 

attach / detach

附着 / 脱离

 

bind / separate

绑定 / 分离

 

view / browse

查看 / 浏览

 

edit / modify

编辑 / 修改

 

select / mark 

选取 / 标记

 

copy / paste

复制 / 粘贴

 

undo / redo

撤销 / 重做

 

insert / delete

插入 / 移除

 

append / remove

加入 / 移除

 

clean / clear

清理 / 清除

 

index / sort

索引 / 排序

 

find / search

查找 / 搜索

 

increase / decrease

增加 / 减少

 

play / pause

播放 / 暂停

 

launch / run

启动 / 运行

 

compile / execute 

编译 / 执行

 

debug / trace

调试 / 跟踪

 

observe / listen

观察 / 监听

 

build / publish

构建 / 发布 

 

input / output

输入 / 输出

 

encode /decode 

编码 / 解码

 

encrypt /decrypt

加密 / 解密

 

compress/decompress

压缩 / 解压缩

 

pack / unpack

打包 / 解包

 

parse /emit

解析 / 生成

 

connect/disconnect 

连接 / 断开

 

subscribe /unsubscribe

订阅 / 取消订阅

 

send / receive

发送 / 接收

 

download / upload 

下载 / 上传

 

refresh /synchronize

刷新 / 同步

 

update / revert 

更新 / 复原

 

lock / unlock

锁定 / 解锁

 

checkOut / checkIn

签出 / 签入

 

submit / commit 

提交 / 交付

 

push / pull

推 / 拉

 

expand / collapse 

展开 / 折叠

 

enter / exit

进入 /  退出

 

abort / quit 

放弃 / 离开

 

collect / aggregate 

收集 / 聚集

 

 

// 是否可阅读
function canRead() {
  return true;
}
// 获取名称
function getName() {
  return this.name;
}

 

 

4.1.4.  类和构造函数

命名方式:大驼峰式命名法,首字母大写,且不允许有返回值

命名规范:前缀为名称。

命名建议:语义化

eslint规则:no-constructor-return、new-cap

 

class Person {
  public name: string;
  constructor(name) {
    this.name = name;
  }
}
const person = new Person('mevyn');

 

公共属性和方法:跟变量和函数的命名一样。

私有属性和方法:前缀为_(下划线),后面跟公共属性和方法一样的命名方式。

 

class Person {
  private _name: string;
  constructor() { }
  // 公共方法
  getName() {
    return this._name;
  }
  // 公共方法
  setName(name) {
    this._name = name;
  }
}
const person = new Person();
person.setName('mervyn');
person.getName(); // ->mervyn

 

 

4.2.  css命名

(1)class命名使用BEM其实是块(block)、元素(element)、修饰符(modifier)的缩写,利用不同的区块,功能以及样式来给元素命名。这三个部分使用__与--连接(这里用两个而不是一个是为了留下用于块儿的命名)。

命名约定的模式如下:

 

.block{}
.block__element{}
.block--modifier{}

 

block 代表了更高级别的抽象或组件

block__element 代表 block 的后代,用于形成一个完整的 block 的整体

block--modifier代表 block 的不同状态或不同版本

为什么使用BEM?

4.2.1.  性能

CSS引擎查找样式表,对每条规则都按从右到左的顺序去匹配

以下这段代码看起来很快,实际上很慢。

通常我们会认为浏览器是这样工作的:找到唯一ID元素ul-id —> 把样式应用到li元素上。

事实上: 从右到左进行匹配,遍历页面上每个li元素并确定其父元素

#ul-id li {}

所以不要让你的css超过三层

4.2.2.  语义化

看以下例子是否一目了然。

 

.person{} /*人*/
.person__hand{} /*人的手*/
.person--female{} /*女人*/
.person--female__hand{} /*女人的手*/
.person__hand--left{} /*人的左手*/

 

如果写成以下这样,你将不知道这是hand指的是手还是指针,female是女性还是雌性,female-hand是女性还是雌性的手?left-hand什么的左手,还是指针的左边?

 

.person{}
.hand{}
.female{}
.female-hand{}
.left-hand{}

 

   

在scss中如何使用?

使用@at-root内联选择器模式,编译出来的CSS无任何嵌套(这是关键)

 

.person {
  @at-root #{&}__hand {
    color: red;
    @at-root #{&}--left {
     color: yellow;
    }
  }
  @at-root #{&}--female {
    color: blue;
    @at-root #{&}__hand {
      color: green;
    }
  }
}
/*生成的css*/
.person__hand {
  color: red;
}
.person__hand--left {
  color: yellow; 
}
.person--female{
  color: blue;
}
.person--female__hand {
  color: green;
}

 

(2)id一般参与样式,命名的话使用驼峰,如果是给js调用钩子就需要设置为js_xxxx的方

 

二、  JS开发规范

1.  对象创建/使用规范

1.1.  使用字面量值创建对象

eslint规范:no-object-constructor

 

 

//❌bad
const obj = new Object()
//✔good
const obj = {}

 

 

1.2.  禁止使用保留字作为对象的键值

原因:在 IE8 下不会运

eslint规则:no-restricted-properties

 

 

//❌bad
const a = {
  default: {},  // default 是保留字
  common: {}
}

//✔good
const a = {
  defaults: {},
  common: {}
}

 

 

1.3.  动态属性名创建对象

当使用动态属性名创建对象时,请使用对象计算属性名来进行创建

原因:因为这样做就可以让你在一个地方定义所有的对象属性

 

function getKey(k) {
return `a key named ${k}`
}

//❌bad
const obj = {
  id: 5,
  name: 'San Francisco'
};
obj[getKey('enabled')] = true

//✔good
const obj = {
  id: 5,
  name: 'San Francisco',
[getKey('enabled')]: true
};

 

 

1.4.  对象方法简写

 

//❌bad
const item = {
  value: 1,
addValue: function (val) {
return item.value + val
}
}

//✔good
const item = {
  value: 1,
addValue (val) {
return item.value + val
}
}

 

 

1.5.  对象属性简写

对象属性值的简写方式

原因:这样更简短且描述更清楚

 

const job = 'FrontEnd'

//❌bad
const item = {
  job: job
}

//✔good
const item = {
  job
}

 

 

1.6.  非法标识符定义

只对非法标识符的属性使用引号

原因:因为通常来说我们认为这样主观上会更容易阅读,这样会带来代码高亮上的提升,同时也更容易被主流 JS 引擎优化

 

//❌bad
const bad = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5
}

//✔good
const good = {
  foo: 3,
  bar: 4,
  'data-blah': 5
}

 

 

1.7.  对象拷贝

优先使用对象展开运算符 ... 来做对象浅拷贝而不是使用 Object.assign,使用对象剩余操作符来获得一个包含确定的剩余属性的新对象

eslint规则:prefer-object-spread

 

//❌bad
const original = { a: 1, b: 2 }
const copy = Object.assign({}, original, { c: 3 }) // copy => { a: 1, b: 2, c: 3 }

//❌very bad
const original = { a: 1, b: 2 }
const copy = Object.assign(original, { c: 3 }) // this mutates `original` ಠ_ಠ
delete copy.// so does this

//✔good
const 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 }

 

 

1.8.  属性访问

使用 . 来访问对象属性

eslint规则:dot-notation

 

const joke = {
  name: 'haha',
  age: 28
}
//❌bad
const name = joke['name']

//✔good
const name = joke.name

 

2.  代码逻辑规范

2.1.  使用变量前必须先声明

变量和函数声明被提升到作用域的顶部,因此可以在代码中的正式声明之前使用标识符。 这可能会令人困惑,最好在使用变量和函数之前始终声明它们。

 

//❌bad
alert(a);
var a = 10;

f();
function f() {}

//✔good
const a = 10;
function f() {}

alert(a);
f();

 

 

2.2.  使用 ES6 语法规范

开发中优先使用 ES6 中新增的语法糖和函数简化代码逻辑,并让代码灵活可复用

如箭头函数、await/async、解构、let、for...of 等等

 

2.3.  switch使用

switch语句的每个case中做好防穿透处理,switch最后需要有default默认出口

eslint规则:default-case

 

//❌bad
switch (foo) {
    case 1:
        bar();
        break;
    default:
        baz();
        break;
    case 2:
        quux();
        break;
}
//❌bad
switch (foo) {
    case "x":
        bar();
        break;
    default:
    case "y":
        baz();
        break;
}

//✔good
switch (foo) {
    case 1:
        bar();
        break;
    case 2:
        quux();
        break;
    default:
        baz();
        break;
}

 

 

2.4.  括号使用

代码中的逻辑块必须有大括号包裹,即使代码块的内容只有一行,涉及到一些判断和循环语句如 if、else、for、while、do、switch、try、catch、finally

 

//❌bad
if (condition) doSomething();

//✔good
if (condition) { 
doSomething();
}

 

 

2.5.  条件判断和循环最多三层

条件判断能使用三目运算符和逻辑运算解决的就不要使用条件判断,但是不要写太长的三目运算,如果超过三层就要单独求值,并写清楚注释

eslint规则:no-nested-ternary

 

 

//❌bad
for (let i = 0; i < list.length; i++{
...
for (let j = 0; j < list[i].children; j++) {
...
        for (let k = 0; k < list[i][j].labelsk++) {
}
}
}

//✔good
for (let i = 0; i < list.length; i++) {
...
    for (let j = 0; j < list[i].children; j++) {
        ...
checkLabel(list[i][j]// 对于嵌套层级太深的做逻辑封装
    }
}

//❌bad
getName() {
  const customerType = this.customerType
if (customerType === 1{
return '客户总数'  
} else {
return '新增客户数'
}
}
//✔good
getName() {
  return this.customerType === 1 ? '客户总数' : '新增客户数'
}

//❌bad
<template>
  <div>
    <!-- 过于复杂 三元运算 -->
    <el-input
      v-model="departName"
      :disabled="
        editForm.status == 1
          ? true
          : editForm.status == 2
          ? true
          : editForm.status == 3
          ? true
          : false
      "
    />

    <!-- 以下这种判断很长的也禁止这样写 -->
    <el-input
      v-model="departName"
      :disabled="
        editForm.status == 1 ||
        editForm.status == 2 ||
        editForm.status == 3 ||
        editForm.status == 4 ||
        (editForm.status == 5 && editForm.age > 20)
      "
    />

    <!-- 更不能图省事直接在模板中写实际的操作代码 -->
    <el-input
      v-model="departName"
      :disabled="
        findStatus.some((item) => {
          return item.status == editForm.status;
        })
      "
    />
  </div>
</template>

//✔good
<template>
  <div>
    <!-- 简单的 一元运算 -->
    <el-input v-model="name" :disabled="editForm.id" />
    <el-input v-model="age" :disabled="editForm.age > 20" />

    <!-- 简单的 三元运算 -->
    <el-input
      v-model="departName"
      :disabled="editForm.status == 1 ? true : false" />

    <!-- 需要判断多种状态的逻辑,把同类的抽取出来放到数组中也可以 -->
    <el-input
      v-model="departName"
      :disabled="[1,2,3].includes(editForm.status])" />

    <!-- 更多一点的逻辑判断,需要封装为计算属性,或者方法 -->
    <el-input v-model="departName" :disabled="canEditDepartName()" />
  </div>
</template>
<script>
export default {
  methods: {
    canEditDepartName() {
      return this.editForm.age > 20 || [1, 2, 3].includes(editForm.status);
    },
  },
};
</script>

 

 

3.  数组创建/使用规范

3.1.  创建数组

请使用字面量值创建数组

eslint规则:no-array-constructor

 

 

//❌bad
const items = new Array()

//✔good
const items = []

 

 

3.2.  添加元素

添加元素时,请使用 push 方法

 

const items = []
//❌bad
items[items.length] = 'test'

//✔good
items.push('test')

 

 

3.3.  复制数组

使用展开运算符 ... 复制数组

 

//❌bad
const items = []
const itemsCopy = []
const len = items.length
for (let i = 0; i < len; i++) {
  itemsCopy[i] = items[i]
}

//✔good
itemsCopy = [...items]

 

 

3.4.  数组 Array.from

使用 Array.from 来将一个类数组对象转换为数组

 

const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }
//❌bad
const arr = Array.prototype.slice.call(arrLike)

//✔good
const arr = Array.from(arrLike)//log输出 ['foo', 'bar', 'baz']

 

 

3.5.  遍历迭代器进

遍历迭代器进行映射时使用 Array.from 代替扩展运算符 ..., 因为这可以避免创建中间数组

 

//❌bad
const baz = [...foo].map(bar)

//✔good
const baz = Array.from(foo, bar)

 

 

3.6.  数组map方法

使用map 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return

eslint规则:array-callback-return

 

 

//❌bad
const flat = {}
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
  const flatten = memo.concat(item)
  flat[index] = flatten
})

//✔good
[1, 2, 3].map(x => {
  const y = x + 1
  return x * y
})

//✔good
[1, 2, 3].map(x => x + 1)

 

 

3.7.  数组解构赋值

使用数组的多个值时,请使用解构赋值

 

const arr = [1, 2, 3, 4]
//❌bad
const first = arr[0]
const second = arr[1]

//✔good
const [first, second] = arr

 

 

4.  字符串使用规范

4.1.  定义

统一使用单引号的形式 ''

eslint规则:quotes

 

 

//❌bad
const department = "JDC"

//✔good
const department = 'JDC'

 

 

4.2.  换行

字符串太长的时候,请不要使用字符串连接符换行 \,而是使用 +

 

const str = '凹凸实验室 凹凸实验室 凹凸实验室' +
  '凹凸实验室 凹凸实验室 凹凸实验室' +
  '凹凸实验室 凹凸实验室'

 

 

4.3.  动态生成

程序化生成字符串时,请使用模板字符串

eslint规则:prefer-template

 

 

const test = 'test'

//❌bad
const str = ['a', 'b', test].join()

//❌bad
const str = 'a' + 'b' + test

//✔good
const str = `ab${test}`

 

 

4.4.  谨慎使用eval()

1.  尽量不要对字符串使用eval(),会导致太多漏洞。

eslint规则:no-eval

 

4.5.  谨慎使用转义字符

不要在字符串中使用不必要的转义字符

eslint规则:no-useless-escape

 

 

//❌bad
const foo = '\'this\' \i\s \"quoted\"'

//✔good
const foo = `my name is '${name}'`

 

 

5.  函数使用规范

5.1.  函数签名和函数体之间使用空格

 

//❌bad
const f = function(){}
const g = function (){}
const h = function() {}

//✔good
const x = function b() {}
const y = function a() {}

 

 

5.2.  不要使用Function构造函数创建函数

eslint规则:no-new-func

 

 

//❌bad
const add = new Function('a', 'b', 'return a + b')

//❌still bad
const subtract = Function('a', 'b', 'return a - b')

 

 

5.3.  执行匿名函数

用圆括号包裹自执行匿名函数

 

(function () {
  console.log('Welcome to the Internet. Please follow me.')
}())

 

 

5.4.  不要在非函数代码块(if , while 等)中声明函数

 

//❌bad
if (isUse) {
  function test () {
    // do something
  }
}

//✔good
let test
if (isUse) {
  test = () => {
    // do something
  }
}

 

 

5.5.  不要使用 arguments,使用 剩余运算符 ...

eslint规则:prefer-rest-params

 

 

//❌bad
function test () {
  const args = Array.prototype.slice.call(arguments)
  return args.join('')
}

//✔good
function test (...args) {
  return args.join('')
}

 

 

5.6.  使用参数默认值语法而不是修改函数参数

 

//❌bad
function handleThings (opts) {
  // No! We shouldn't mutate function arguments.
  // Double bad: if opts is falsy it'll be set to an object which may
  // be what you want but it can introduce subtle bugs.
  opts = opts || {}
  // ...
}

//❌still bad
function handleThings (opts) {
  if (opts === void 0) {
    opts = {}
  }
  // ...
}

//✔good
function handleThings (opts = {}) {
  // ...
}

 

 

5.7.  避免参数默认值的副作用

对函数给定默认参数的候,要给定确认值

而不是计算表达式

 

let b = 1

//❌bad
function countBad (a = b++) {
  console.log(a)
}
countBad()  // 1
countBad()  // 2
countBad(3) // 3
countBad()  // 3


//✔good
function countGood(a = 1) {
    console.log(a)
}

 

 

5.8.  将默认值的参数放在最后

eslint规则:default-param-last

 

 

//❌bad
function handleThings (opts = {}, name) {
  // ...
}

//✔good
function handleThings (name, opts = {}) {
// ...
}

 

 

5.9.  不要更改参数和对参数进行重新赋值

对声明为函数参数的变量进行赋值可能会产生误导,并导致令人困惑的行为

eslint规则:no-param-reassign

 

//❌bad
const foo = function(bar) {
    bar = 13;
}

//❌bad
function f2 (a) {
  if (!a) { a = 1 }
}

//✔good
const foo = function(bar) {
let baz = bar;
}
//✔good
function f3 (a) {
const b = a || 1
}
//✔good
function f4 (a = 1) {
}

 

 

5.10.  调用可变参数函数时建议使用展开运算符

 

//❌bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]))

//❌bad
const x = [1, 2, 3, 4, 5]
console.log.apply(console, x)

//✔good
new Date(...[2016, 8, 5])

//✔good
const x = [1, 2, 3, 4, 5]
console.log(...x)

 

 

 

6.  箭头函数使用规范

6.1.  使用场景

当可以使用函数表达式(传递匿名函数)时,优先使用箭头函数标记

eslint规则:prefer-arrow-callback

 

//❌bad
[1, 2, 3].map(function (x) {
  const y = x + 1
  return x * y
})

//✔good
[1, 2, 3].map((x) => {
const y = x + 1
return x * y
})

 

 

6.2.  简写

如果函数体只包含一条没有副作用的返回表达式的语句,可以省略花括号并使用隐式的 return, 否则保留花括号并使用 return 语句

eslint规则:arrow-body-style

 

//❌bad
foo(() => bool = true)

//✔good
[1, 2, 3].map(number => `A string containing the ${number}.`)

//✔good
[1, 2, 3].map((number) => {
const nextNumber = number + 1
return `A string containing the ${nextNumber}.`
})

//✔good
[1, 2, 3].map((number, index) => ({
  index: number
}))

let bool = false

//✔good
// 有副作用的时候用大括号括起来
foo(() => {
  bool = true
})

 

 

6.3.  跨行表达式

一旦表达式跨多行,使用圆括号包裹以便更好阅读

 

//❌bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod
  )
)

//✔good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
    httpMagicObjectWithAVeryLongName,
    httpMethod
)
))

 

 

7.  构造函数使用规范

7.1.  创建

使用 class,避免直接操作 prototype

 

//❌bad
function Queue (contents = []) {
  this._queue = [..contents]
}
Queue.prototype.pop = function () {
  const value = this._queue[0]
  this._queue.splice(0, 1)
  return value
}

//✔good
class Queue {
 constructor(contents = []) {
this._queue = [...contents]
}

pop () {
const value = this._queue[0]
this._queue.splice(0, 1)
return value
}
}

 

 

7.2.  继承

使用 extends 来实现继承

 

//❌bad
const inherits = require('inherits')
function PeekableQueue(contents) {
  Queue.apply(this, contents)
}
inherits(PeekableQueue, Queue)
PeekableQueue.prototype.peek = function () {
  return this.queue[0]
}

//✔good
class PeekableQueue extends Queue {
peek () {
return this.queue[0]
}
}

 

 

7.3.  避免类成员重复

如果类成员中有同名的声明,最后一个声明会默默地覆盖其他声明。 它可能会导致意外行为。

eslint规则:no-dupe-class-members

 

 

//❌bad
class Foo {
  bar () { return 1 }
  bar () { return 2 }
}

//✔good
class Foo {
bar () { return 2 }
}

 

 

8.  模块化开发规范

8.1.  标准语法

使用标准的 ES6 模块语法 import 和 export

 

//❌bad
const util = require('./util')
module.exports = util

//✔good
import Util from './util'
export default Util

//👍better
import { Util } from './util'
export default Util

 

 

8.2.  确保只有一个默认的 export

不要使用 import 的通配符 *,这样可以确保你只有一个默认的 export

 

//❌bad
import * as Util from './util'

//✔good
import Util from './util'

 

 

8.3.  同个文件每个模块只允许 import 一次,有多个 import 请书写在一起

eslint规则:no-duplicate-imports

 

//❌bad
import foo from 'foo'
// … some other imports … //
import { named1, named2 } from 'foo'

//✔good
import foo, { named1, named2 } from 'foo'

//✔good
import foo, {
  named1,
  named2
} from 'foo'

 

 

8.4.  import 语句放在文件最前方

 

//❌bad
import foo from 'foo'
foo.init()
import bar from 'bar'

//✔good
import foo from 'foo'
import bar from 'bar'

foo.init()

 

 

8.5.  多行导入

多行导入应该像多行数组和对象文字一样缩进

eslint规则:sort-imports

 

//❌bad
import { longNameA, longNameB, longNameC, longNameD, longNameE } from 'path'

//✔good
import {
  longNameA,
  longNameB,
  longNameC,
  longNameD,
  longNameE
} from 'path'

 

 

8.6.  在模块 import 声明中禁止使用 Webpack 的 loader 语法

 

//❌bad
import fooSass from 'css!sass!foo.scss'
import barCss from 'style!css!bar.css'

//✔good
import fooSass from 'foo.scss'
import barCss from 'bar.css'

 

 

 

三、css规范

1.  代码风格

1、采用UTF-8编码,在CSS头部引用 @charset "utf-8";

2、采用统一约定的缩进

3、每行不得超过120个字符,除非单行不可分割

4、选择器嵌套层级不大于3级,位置靠后的限定条件应尽可能精确。

5、尽量不要使用@import,与<links>相比,@import速度较慢,增加了额外的页面请求,并可能导致其他无法预料的问题。

6、  Media Query不得单独编排,必须与相关的规则一起定义。

7、  css 选择器中避免使用标签名

 

2.  样式属性顺序

1、布局位置(position,top,right,z-index,display,float等)

2、尺寸大小(宽度,高度,填充,边距)

3、文字系列(字体,行高,字母间距,颜色文字对齐等)

4、视觉效果(background,border等)

5、  其他(动画,转换等)

 

 

/* ❌bad */
.header {
    background: #CCC;
    font-size: 20px;
    height: 50px;
    border: 1px solid #bbb;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    color: #f94000;
}

/* ✔good */
.header {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 50px;
    font-size: 20px;
    color: #f94000;
    background: #CCC;
    border: 1px solid #bbb;
}

 

 

3.  避免使用ID选择器及全局标签选择器

样式匹配尽量用类名来实现,避免使用全局标签选择器来定义样式,包括我们使用的组件库也不要对全局样式进行修改,可以另外添加样式来做修改

 

/* ❌bad */
div {
  padding-bottom: 0px; 
  margin: 0em; 
}
button {
  border:1px solid #ccc;
}
.el-form {
  margin: 20px
}

/* ✔good */
.header { 
  padding-bottom: 0px; 
  margin: 0em; 
}
.sop-el-form {
margin: 20px
}

 

4.  css嵌套层级

将嵌套层级限制的4级别,大量的嵌套规则不仅可读性差,而且会影响渲染性能。可以通过第一章中提到的 BEM 命名规范减少嵌套层级

 

 /* ❌bad */
 .main {
   .content {
       .title { 
          .name { 
               color: #fff;  
            } 
       }
   }
}

 /* ✔good */
.main-title {
.name {
color: #fff; 
}
}

 

5.  缩写属性和属性值

1、  有些属性是可以缩写的,比如填充,边缘,字体,背景,边框等等。缩写代码可以提高用户的阅读体验

 

/* ❌bad */
.header {
    #fc6;
    background-image: url("../images/test.jpg");
    background-repeat: no-repeat;
    bnackground-attachment: fixed;
    background-position: left top;
}

/* ✔good */
.header {
background: #fc6 url("../images/test.jpg") no-repeat fixed left top;
}

 

 

2、  16进制的颜色代码重叠的字符可以缩写的尽量缩写

 

 

/* ❌bad */
.header {
    #cccccc;
    color: #ff4400;
}

/* ✔good */
.header {
    #ccc;
    color: #f40;
}

 

 

6.  规则声明块样式规范

1、当规则声明块中有多个样式声明时,每条样式独占一行。

2、列表属性并排书写时,用逗号分隔,逗号后必须跟一个空格

3、选择器与左大括号 {之间必须加一个空格

4、属性名与冒号之间不允许包含空格,冒号与属性值之间必须包含空格

5、在每条样式后面都以分号; 结尾。

6、规则声明块的右大括号} 独占一行。

7、当一个属性有多个属性值时,以逗号, 分隔属性值,每个逗号后添加一个空格,当单个属性值过长时,每个属性值独占一行。

8、每个规则声明间用空行分隔。

 

7.  数值

当数值为 0 - 1 之间的小数时,建议省略整数部分的 0。

 

.panel {
opacity: .8
}

 

 

8.  

当长度为 0 时建议省略单位。

 

/* ❌bad */
.list {
padding: 0px 5px;
}

/* ✔good */
.list {
padding: 0 5px;
}

 

 

9.  颜色

建议不使用命名色值。

 

/* ❌bad */
.button--success {
    color: lightgreen;
}

/* ✔good */
.button--success {
color: #9e9;
}

 

 

10.  清除浮动

建议当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 clear 或触发 BFC 的方式进行 clearfix。尽量不使用增加空标签的方式。

 

11.  !important

除公共样式之外,在业务代码中尽量不能使用 !important

 

12.  z-index

建议将 z-index 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。

 

13.  字体排版

13.1.  字号

因为 Windows 的字体渲染机制,小于 12px 的文字显示效果极差、难以辨认。所以在 Windows 平台显示的中文内容,其字号应不小于 12px。结合ui规范对于字体尽量采用插入变量的方式

 

13.2.  字重

font-weight 属性建议使用数值方式描述。

 

/* ❌bad */
.title {
  font-weight: bold;
}

/* ✔good */
.title {
font-weight: 700;
}

 

 

13.3.  行高

line-height 在定义文本段落时,应使用数值。将line-height 设置为数值,浏览器会基于当前元素设置的 font-size 进行再次计算。在不同字号的文本段落组合中,能达到较为舒适的行间间隔效果,避免在每个设置了 font-size 都需要设置line-height。如果line-height 用于控制垂直居中,应该设置成与容器高度一致。

 

/* ❌bad */
.container {
    line-height: 15px;
}

/* ✔good */
.container {
line-height: 1.5;
}

 

14.  变量的使用

对于项目中有明确的ui规范的需要将ui定好的规范集中配置在全局变量中,使用的时候采用引入变量的方式引入,方便集中管理

 

/* ❌bad */
h1 {
    color: #165dff;
font-size: 14px;
    font-weight: 600;
}

/* ✔good */
body {
--color-neutral-7: 134,144,156;
   --color-text-3: var(--color-neutral-7);
--font-size-body-3: 14px;
   --font-weight-600: 600;
}
.title { font-size: var(--font-size-body-3);
color: rgb(var(--color-text-3));
    font-weight: var(--font-weight-600);
}

 

四、  vue2开发规范

1.  组件数据

组件的 data 必须是一个函数。

当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数。

 

//❌bad
export default {
 data: {
    foo: 'bar'
 }
}

//✔good
// In a .vue file
export default {
data () {
return {
           foo: 'bar'
}
}
}
// 在一个 Vue 的根实例上直接使用对象是可以的,
// 因为只存在一个这样的实例。
new Vue({
    data: {
       foo: 'bar'
}
})

 

 

2.  Prop定义

Prop 定义应该尽量详细。

 

在你提交的代码中,prop 的定义应该尽量详细,至少需要指定其类型。

eslint:vue/require-prop-type-constructor

 

 

//❌bad
// 这样做只有开发原型系统时可以接受
props: ['status']

//✔good
props: {
 status: String
}

//👍better
props: {
 status: {
     type: String,
     required: true,
  default: 'syncing',
     validator: function (value) {
         return [
         'syncing',
         'synced',
         'version-conflict',
         'error'
         ].indexOf(value) !== -1
     }
 }
}

 

 

3.  为v-for设置键值

总是用 key 配合 v-for。

在组件上_总是_必须用 key 配合 v-for,以便维护内部组件及其子树的状态。甚至在元素上维护可预测的行为,比如动画中的对象固化 (object constancy),也是一种好的做法。

eslint:vue/require-v-for-key

 

//❌bad
<ul>
 <li v-for="todo in todos">
 {{ todo.text }}
 </li>
</ul>

//✔good
<ul>
 <li
   v-for="todo in todos"
   :key="todo.id">
   {{ todo.text }}
 </li>
</ul>

 

 

4.  避免 v-if 和 v-for 用在一起

永远不要把 v-if 和 v-for 同时用在同一个元素上。

eslint规则:vue/no-use-v-if-with-v-for

 

一般我们在两种常见的情况下会倾向于这样做:

为了过滤一个列表中的项目 (比如 

 

v-for="user in users" v-if="user.isActive"

 

)。在这种情形下,请将 users 替换为一个计算属性 (比如 activeUsers),让其返回过滤后的列表。

为了避免渲染本应该被隐藏的列表 (比如

 

 v-for="user in users" v-if="shouldShowUsers

 

")。这种情形下,请将 v-if 移动至容器元素上 (比如 ul, ol)。

 

//❌bad
<ul>
 <li
 v-for="user in users"
 v-if="shouldShowUsers"
 :key="user.id">
 {{ user.name }}
 </li>
</ul>

//✔good
<ul v-if="shouldShowUsers">
 <li
 v-for="user in users"
 :key="user.id">
 {{ user.name }}
 </li>
</ul>

 

 

5.  为组件样式设置作用域

对于应用来说,顶级 App 组件和布局组件中的样式可以是全局的,但是其它所有组件都应该是有作用域的。

 

这条规则只和单文件组件有关。你不一定要使用 scoped 特性。设置作用域也可以通过 CSS Modules,那是一个基于 class 的类似 BEM 的策略,当然你也可以使用其它的库或约定。

 

不管怎样,对于组件库,我们应该更倾向于选用基于 class 的策略而不是 scoped 特性。 

这让覆写内部样式更容易:使用了常人可理解的 class 名称且没有太高的选择器优先级,而且不太会导致冲突。

 

//❌bad
<template>
 <button class="btn btn-close">X</button>
</template>
 
<style>
.btn-close {
 red;
}
</style>

//✔good
<template>
 <button class="button button-close">X</button>
</template>
<!-- 使用 `scoped` 特性 -->
<style scoped>
.button {
 border: none;
 border-radius: 2px;
}
.button-close {
 red;
}
</style>

//✔good
<template>
 <button class="c-Button c-Button--close">X</button>
</template>
<!-- 使用 BEM 约定 -->
<style>
.c-Button {
 border: none;
 border-radius: 2px;
}
.c-Button--close {
 background-color: #f00;
}
</style>

 

 

6.  组件独立成文件

一个文件中应该最多注册一个组件,当你需要编辑一个组件或查阅一个组件的用法时,可以更快速的找到它。

 

//❌bad
Vue.component('TodoList', {
 // ...
})
Vue.component('TodoItem', {
 // ...
})

//✔good
components/
|- TodoList.vue
|- TodoItem.vue

 

 

7.  分多行撰写

超过三个属性的元素应该分多行撰写,每个特性一行。

eslint规则:vue/max-attributes-per-line

 

 

//❌bad
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent foo="a" bar="b" baz="c"/>

//✔good
<img src="https://vuejs.org/images/logo.png" alt="Vue Logo">
<MyComponent
 foo="a"
 bar="b"
 baz="c"/>

 

 

8.  模板中简单的表达式

组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。

复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。

 

//❌bad
<template>
 <div>
 {{
     fullName.split(' ').map(function (word) {
     return word[0].toUpperCase() + word.slice(1)
     }).join(' ')
}}
 </div>
</template>

//✔good
<template>
<div>{{ normalizedFullName }}</div>
</template>
<script>
export default {
// 复杂表达式已经移入一个计算属性
    computed: {
     normalizedFullName: function () {
         return this.fullName.split(' ').map(function (word) {
             return word[0].toUpperCase() + word.slice(1)
         }).join(' ')
     }
}
}
</script>

 

 

9.  简单的计算属性

复杂的computed求值要注意封装,如果有中间值可以重用的可以抽取出来

 

//❌bad
computed: {
 finalPrice: function () {
     const basePrice = this.manufactureCost / (1 - this.profitMargin)
     return (
         basePrice -
         basePrice * (this.discountPercent || 0)
     )
 }
}

//✔good
computed: {
 basePrice: function () {
     return this.manufactureCost / (1 - this.profitMargin)
 },
 discount: function () {
     return this.basePrice * (this.discountPercent || 0)
 },
 finalPrice: function () {
     return this.basePrice - this.discount
 }
}

 

 

10.  带引号的特性值

非空 HTML 特性值应该始终带引号 (单引号或双引号,选你 JS 里不用的那个)。

在 HTML 中不带空格的特性值是可以没有引号的,但这样做常常导致带空格的特征值被回避,导致其可读性变差。

 

//❌bad
<AppSidebar :style={width:sidebarWidth+'px'}>

//✔good
<AppSidebar :style="{ width: sidebarWidth + 'px' }"/>

 

 

11.  指令缩写

都用指令缩写 (用 : 表示 v-bind: 和用 @ 表示 v-on:)

 

//❌bad
<input
 v-bind:value="newTodoText"
 :placeholder="newTodoInstructions"
  v-on:focus="onFocus"/>

//✔good
<input
  :value="newTodoText"
 @input="onInput"
 @focus="onFocus"/>

 

 

12.  清理空方法和空生命周期函数

及时清理项目中的空方法和模板插件生成的空的生命周期函数,保持代码整洁

 

//❌bad
<template>
  <!-- 加好友弹窗 -->
  <div class="mask">
    <div class="erweima-box">
      <div class="code">
        <img :src="codeImg" alt="">
      </div>
      <div class="text">添加企业成员微信参与活动</div>
      <div class="button" >
        <span @click="hasAdd">
          我已添加企业微信
        </span>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'addFriend',
  props: ['codeImg'],
  data() {
    return {
    }
  },
  watch: {

  },
  mounted() {

  },
  methods: {
    hasAdd() {
      this.$emit('hasAddFriend')
    },
toDetail(){
}
  }
}
</script>

//✔good
<template>
  <!-- 加好友弹窗 -->
  <div class="mask">
    <div class="erweima-box">
      <div class="code">
        <img :src="codeImg" alt="">
      </div>
      <div class="text">添加企业成员微信参与活动</div>
      <div class="button" >
        <span @click="hasAdd">
          我已添加企业微信
        </span>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'addFriend',
  props: ['codeImg'],
methods: {
    hasAdd() {
      this.$emit('hasAddFriend')
    }
  }
}
</script>

 

13.  组件开发规范

每个 Vue 组件的代码建议不要超出 400 行,如果超出建议拆分组件

组件一般情况下是可以拆成基础/ui部分和业务部分,基础组件一般是承载呈现,基础功能,不和业务耦合部分。

业务组件一般包含业务功能业务特殊数据等等

13.1.  UI组件/基础组件

开发的时候注意可拓展性,支持数据传参进行渲染,支持插槽slot

设置有mixin,mixin中放了基础信息和方法

13.2.  容器组件

和当前业务耦合性比较高,由多个基础组件组成,可承载当前页的业务接口请求和数据(vuex)

13.3.  组件存放位置

(1)ui组件存放在src/components/ 中

包含xxx.vue和 xxmixin.js 和 Today I Learned for programmers - Tiloid

 

xxx.vue // 表示ui部分
xxmixin.js // 表示js部分
readme.md // 中描述组件的基本信息

 

 

名词

含义

案例

@name

组件名称

筛选下拉框

@version

版本

v1.0.0

@updateTime

更新日期

2018.09.18

@describe

使用场景描述

某某场景下

@props

参数

['data']

@author

作者

dd

 

引用组件的时候 直接引入 mixinElementFilter.js 即可。在引用组件的页面可以对mixin里面的方法进行重构 

(2)业务组件就放在业务模块部分即可

13.4.  组件通讯

避免数据的分发源混乱,不建议使用eventBus控制数据,应使用props来和$emit来数据分发和传送

同级组件的通讯一般会有一个中间容器组件作为桥梁容器组件作为数据的接受和分发点

13.5.  组件的挂在和销毁

(1)  通过v-if控制组件的挂在和销毁

 

<testcomponent v-if='componentActive'> </testcomponent>

 

(1)  通过is控制组件的挂在和销毁

 

<component is='componentName'> </component>

 

 

13.6.  跨项目组件共用

公共组件存放位置中 定时抽取共用次数多的组件 将他放在http://npm.idc7x24.cn/中,供下载引用

14.  谨慎使用

14.1.  没有在 v-if/v-if-else/v-else 中使用 key

如果一组 v-if + v-else 的元素类型相同,最好使用 key (比如两个 <div> 元素)。

 

//❌bad
<div v-if="error">
 错误:{{ error }}
</div>
<div v-else>
 {{ results }}
</div>

//✔good
<div
 v-if="error"
 key="search-status">
 错误:{{ error }}
</div>
<div
 v-else
 key="search-results">
 {{ results }}
</div>

 

 

14.2.  scoped 中的元素选择器

元素选择器应该避免在 scoped 中出现。

在 scoped 样式中,类选择器比元素选择器更好,因为大量使用元素选择器是很慢的。

 

//❌bad
<template>
 <button>X</button>
</template>
 
<style scoped>
button {
 red;
}
</style>

//✔good
<template>
 <button class="btn btn-close">X</button>
</template>
 
<style scoped>
.btn-close {
 background-color: red;
}
</style>

 

 

14.3.  隐性的父子组件通信

应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop。

 

//❌bad
Vue.component('TodoItem', {
 props: {
     todo: {
         type: Object,
         required: true
     }
 },
 methods: {
     removeTodo () {
         const vm = this
         vm.$parent.todos = vm.$parent.todos.filter(function (todo) {
             return todo.id !== vm.todo.id
         })
     }
 },
 template: `
 <span>
     {{ todo.text }}
     <button @click="removeTodo">X</button>
 </span>
 `
})


//✔good
Vue.component('TodoItem', {
 props: {
     todo: {
         type: Object,
         required: true
     }
 },
 template: `
 <input
     :value="todo.text"
     @input="$emit('input', $event.target.value)">
 `
})

 

 

五、  小程序开发规范

1.  目录概述

1.1.  组件文件

所有组件相关文件统一放在components目录下。

 

1.2.  图片文件

项目图片文件放置于根目录的images文件夹下,组件独有的图片放在当前组件images目录下

 

1.3.  模型文件

模型文件主要用于编写各类业务模型。项目模型文件放置于根目录的models文件夹下,组件相关模型放置于components目录下的models文件夹中。

 

1.4.  行为文件

行为文件放在所引用的组件目录下。

 

2.  wxml规范

2.1.  WXML规范

wxml标签可以单独出现的情况,尽量单独出现,如<input />。

控制每行HTML的代码数量在50个字符以内,方便阅读浏览,多余的代码进行换行处理,标签所带属性每个属性间进行换行。

 

<c-music
  wx:if="{{classic.type===200}}"
  img="{{classic.img}}"
  content="{{classic.content}}">
</c-music>

 

合理展现分离内容,不要使用内联样式。

 

<image class="tag" style="display: flex"></image>

 

 

2.2.  注释规范

除组件外的其他块级元素,均需注释出其功能,并在其上下空出一行与其他代码进行区分。

 

<view>...</view>

//导航栏
<view>...</view>

<view>...</view>

 

 

3.  wxss规范

3.1.  书写规范

CSS代码需有明显的代码缩进。每一个样式类之间空出一行。

 

.c-tag{
width: 100%;
}

.c-container{
width: 100%;
}

 

尽量使用简写属性,并且同一属性放置在一起,避免散乱。

 

/**使用简写属性**/
.c-image{
margin: 0 auto;
}

/**同一属性放在一块**/
.c-tag{
margin-left: 10rpx;
margin-right: 10rpx
}

 

推荐采用flex进行布局,谨慎使用float以及vertical-align。

 

.container{
disaplay: flex;
flex-dirextion: row
}

 

 

3.2.  注释规范

成组的wxss规则之间用块状注释。请勿在代码后面直接注释。

 

/** 修改button默认的点击态样式类**/
.button-hover {
background-color: red;
}

 

 

4.  js规范

遵循第二章基本的js书写规范

4.1.  回调函数规范

回调函数统一使用Promise函数的方式进行编写,回调成功的参数统一为res,错误参数为err。

 

// promise 处理回调
let back = new Promise((resolve, reject) => {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

back.then((res) => {
    console.log('成功回调!', res);
}).catch((err) => {
    console.log('失败回调!', error);
});

 

私有函数以及回调函数统一放置在生命周期函数后。

对于加了es6支持的项目优先使用 async/await写法

 

删除js文件中未用到的生命周期函数,保持代码的整洁。

 

Pages({
  data:{

},

onLoad:function(event){

},

_self:function(){

}
})

 

 

每个函数之间用一个空行分离结构。

 

4.2.  数据绑定变量定义规范

所有涉及到数据绑定的变量均需在data中初始化。禁止在不定义的情况下直接setData。

 

Pages({
  data:{
     id : null
},

onLoad:function(event){
let id = event.target.dataset.id
this.data.id = id
}
})

 

点击事件规范

点击事件函数命名方式为 on + 事件名 或者业务名。

 

onLike: function(event){

}

 

 

4.3.  组件名命名规范

组件在使用时命名以 “c-”为开头的组件名,若组件名称为多个单词名拼接而成,采用 ' - ' 连接。

 

<c-movies />

 

 

4.4.  组件触发事件规范

组件点击触发事件建议用冒号分隔开

 

<c-component-tag-name bind:myevent="onMyEvent" />

 

 

4.5.  externalClasses命名规范

命名格式采用如下形式:c-class-{name},name可自行定义。

 

4.6.  组件样式规范

命名必须以 c- 开头,不允许使用内联样式以及id样式

 

.c-container{
disaplay: flex;
flex-dirextion: row
}

 

 

4.7.  标点规范

JS语句需以分号结束

JS中一致使用单引号' ' , 不使用双引号。

WXML、CSS、JSON中均应使用双引号。

CSS属性中冒号中后面用一个空格分隔开。

执行一致的缩进(4个空格)

 

 

六、  移动端开发/优化规范

1.  click 的 300ms 延迟响应

click 的 300ms 延迟是由双击缩放所导致的,由于用户可以进行双击缩放或者双击滚动的操作,当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。因此,移动端浏览器就等待 300 毫秒,以判断用户是否再次点击了屏幕。

 

随着响应式网页逐渐增多,用户使用双击缩放机会减少,这 300ms 的延迟就更不可接受了。浏览器开发商也随之提供相应的解决方案。这些方案在5 Ways to Prevent the 300ms Click Delay on Mobile Devices 中,被提及的包括“禁用缩放”和“width=device-width”等方案,但这些方案并不完美,需要针对某些版本浏览器,又或仅在 Android 的浏览器上使用。

 

所以这时候就需要一个更简单通用的解决方案,其中 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库 FastClick 就是很好的选择。FastClick 在检测到 touchend 事件的时候,会通过 DOM 自定义事件立即触发一个模拟 click 事件,并把浏览器在 300 毫秒之后真正触发的 click 事件阻止掉。

 

FastClick 的使用方法非常简单,在 window load 事件之后,在 <body> 上调用FastClick.attach() 即可。

 

2.  禁止html缓存

如果没有禁止html缓存则每次发版用户的html文件更新都会延迟,那么就有概率出现旧index.html中引入的js或css文件找不到而导致页面白板。所以需要通过配置禁止缓存html文件。基于现在的自建nginx服务器的发版方式,我们可以再nginx.conf 文件中增加请求头来禁止文件缓存,配置如下:

 

server {
    listen       80;
    server_name  localhost;

    location / {
      root   /usr/share/nginx/html;
      index  index.html index.htm;
      try_files $uri $uri/ /index.html;
       ## 配置页面不缓存html和htm结尾的文件
      if ($request_filename ~* .*\.(?:htm|html)$) {
         add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
      }
    }
}

 

 

3.  快速回弹滚动

快速回弹滚动在手机浏览器上的发展历史:

早期的时候,移动端的浏览器都不支持非 body 元素的滚动条,所以一般都借助 iScroll;

Android 3.0 / iOS 解决了非 body 元素的滚动问题,但滚动条不可见,同时 iOS 上只能通过2个手指进行滚动;

Android 4.0 解决了滚动条不可见及增加了快速回弹滚动效果,不过随后这个特性又被移除;

iOS从5.0开始解决了滚动条不可见及增加了快速回弹滚动效果

如果想要为某个元素拥有 Native 般的滚动效果,可以这样操作:

 

.element {
    overflow: auto; / auto | scroll /
    -webkit-overflow-scrolling: touch;
}

 

除了 iScroll 之外,还有一个更加强大的滚动插件 Swiper,支持 3D 和内置滚动条等。

 

3.1.  关于ios局部滚动卡顿的问题

ios内置的浏览器在使用了-webkit-overflow-scrolling:touch之后,页面偶尔会卡住不动,这是由于局部滚动出界的情况,当我们的局部滚动条在最顶端的时候如果不等局部滚动的回弹效果执行完毕继续触发向下活向上滚动的时候发现整个局部滚动卡住转而触发了整个页面的全局滚动,造成的表象就是整个页面卡住。如果等带局部滚动回弹动画执行完毕后再拖拽页面则局部滚动正常。

解决这个问题目前通常可以有三种方案:

1.  尽量不要使用局部滚动,如果只是简单的菜单列表的布局可以灵活运用 fixed 和 absolute 处理菜单布局,列表使用全局滚动的方式

2.  如果必须要使用局部滚动,那么可以尝试禁止全局滚动在body上增加 overflow:hidden 属性,当然是在加这个属性不影响页面布局的前提下

3.  方案1和2采用的都是一些兼容的方式,是在默认IOS有这种滚动问题的情况下采用绕开或者适配的折中方案,但是都有局限性,我们也可以放弃IOS默认滚动,采用自己处理滚动的方式来实现页面效果,有一定的开发成本。社区成熟方案有: swiperbetter-scroll

 

4.  设备检测

代码引用自:https://github.com/binnng/device.js/blob/master/device.js

 

const WIN = window;
const LOC = WIN["location"];
const NA = WIN.navigator;
const UA = NA.userAgent.toLowerCase();

function test(needle) {
return needle.test(UA);
}

const IsTouch = "ontouchend" in WIN;
const IsAndroid = test(/android|htc/) || /linux/i.test(NA.platform + "");
const IsIPad = !IsAndroid && test(/ipad/);
const IsIPhone = !IsAndroid && test(/ipod|iphone/);
const IsIOS = IsIPad || IsIPhone;
const IsWinPhone = test(/windows phone/);
const IsWebapp = !!NA["standalone"];
const IsXiaoMi = IsAndroid && test(/mi\s+/);
const IsUC = test(/ucbrowser/);
const IsWeixin = test(/micromessenger/);
const IsBaiduBrowser = test(/baidubrowser/);
const IsChrome = !!WIN["chrome"];
const IsBaiduBox = test(/baiduboxapp/);
const IsPC = !IsAndroid && !IsIOS && !IsWinPhone;
const IsHTC = IsAndroid && test(/htc\s+/);
const IsBaiduWallet = test(/baiduwallet/);

 

 

5.  获取滚动条值

PC 端滚动条的值是通过 document.scrollTop 和 document.scrollLeft 获得,但在 iOS 中并没有滚动条的概念,所以仅能通过 windows.scroll 获取,同时也能兼容 Android 。

 

6.  清除输入框内阴影

在 iOS 上,输入框默认有内部阴影,但无法使用 box-shadow 来清除,如果不需要阴影,可以这样操作:

 

7.  Meta 相关

页面窗口自动调整到设备宽度,并禁止用户缩放页面。

 

8.  电话号码识别

iOS Safari ( Android 或其他浏览器不会) 会自动识别看起来像电话号码的数字,将其处理为电话号码链接,比如:

7位数字,形如:1234567

带括号及加号的数字,形如:(+86)123456789

双连接线的数字,形如:00-00-00111

11位数字,形如:13800138000

 

9.  邮箱地址的识别

在 Android ( iOS 不会)上,浏览器会自动识别看起来像邮箱地址的字符串,不论有你没有加上邮箱链接,当你在这个字符串上长按,会弹出发邮件的提示。

 

七、  前端安全规范

1.  跨站脚本攻击(xss)

原理:Cross Site Scripting, 安全专家们通常将其缩写成XSS,原本应当是css,但为了和层叠样式表(Cascading Style Sheet,CSS )有所区分,故称XSS。XSS攻击其核心都是利用了脚本注入。

XSS的三个类型分别是:

● 反射性XSS(非持久性XSS)

前端考虑过滤一些非法字符 后端验证XSS漏洞可以利用:请求参数未请求接口前进行过滤一些非法字符。比如自定义Filter过滤器。

 

● 存储型XSS

存储型 XSS 一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。 会存储到数据库,页面刷新后会弹出。

 

● DOM XSS

因为其形成是通过修改页面的"DOM节点"形成的XSS。例如:通过修改DOM节点上的绑定方法,用户无意间通过点击、输入等行为执行这些方法获取到用户的相关信息

 

区别:

存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。

反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。

由于需要用户主动打开恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。

POST 的内容也可以触发反射型 XSS,只不过其触发条件比较苛刻(需要构造表单提交页面,并引导用户点击),所以非常少见。

 

2.  XSS的防御方式

2.1.  HttpOnly

原理:浏览器禁止页面的Javascript访问带有HttpOnly属性的cookie。(实质解决的是:XSS后的cookie劫持攻击)如今已成为一种“标准”的做法。

 

解决方案:JavaEE给Cookie添加HttpOnly的方式为: response.setHeader("Set-Cookie","cookiename=value; Path=/;Domain=domainvalue; Max-Age=seconds;HTTPOnly");

 

2.2.  输入检查(XSS Filter)

原理:让一些基于特殊字符的攻击失效。(常见的Web漏洞如XSS、SQLInjection等,都要求攻击者构造一 些特殊字符)* 输入检查的逻辑,必须在服务端实现,因为客户端的检查也是很容易被攻击者绕过, 现有的普遍做法是两端都做同样的检查,客户端的检查可以阻挡大部分误操作的正常用户, 从而节约服务器的资源。解决方案:检查是否包含"JavaScript","<script></script>"等敏感字符。 以及对字符串中的<>:"&/'等特殊字符做处理。

 

2.3.  输出检查

原理:一般来说除了富文本输出之外,在变量输出到HTML页面时,使用编码或转义的方式来防御XSS攻击解决方案:*   针对HTML代码的编码方式:HtmlEncode*   PHP:htmlentities()和htmlspecialchars()两个函数*   Javascript:JavascriptEncode(需要使用""对特殊字符进行转义,同时要求输出的变量必须在引号内部)*   在URL的path(路径)或者search(参数)中输出,使用URLEncode。

 

3.  跨站点请求伪造

● 用户登录受信任网站A,并且在本地生成Cookie

● 在不登出网站A的情况下,访问有害网站B

 

 

4.  点击劫持

点击劫持是指在一个Web页面中隐藏了一个透明的iframe,用外层假页面诱导用户点击,实际上是在隐藏的frame上触发了点击事件进行一些用户不知情的操作。

 

● 禁止iframe的嵌套

 

if(window.top.location !== window.loaction){
window.top.location === window.self.location
}

 

● X-Frame-Options HTTP响应头是用来给浏览器指示允许一个页面能否在<frame>、<iframe>、<object>中展现的标记

 

5.  本地存储数据问题

当服务端设置 'Access-Control-Allow-Origin' 时使用了通配符 "*",允许来自任意域的跨域请求,这是极其危险的。

把一些个人信息不经加密直接存到本地或者cokie,这样是非常不安全的,黑客们可以很容易就拿到用户的信息,所有在放到cookie中的信息或者localStorage里的信息要进行加密,加密可以自己定义一些加密方法或者网上寻找一些加密的插件,或者用base64进行多次加密然后再多次解码,这样就比较安全了。

 

 

6.  跨域问题处理

当服务端设置 'Access-Control-Allow-Origin' 时使用了通配符 "*",允许来自任意域的跨域请求,这是极其危险

 

7.  HTTPS加密传输数据

当其中的某一步被黑客拦截的时候,如果信息没有加密,就会很容易被盗取。

 

八、  注释规范

注释是为了辅助理解代码添加的说明信息,注释与删除代码是两回事,对于要删除的废代码直接删除,删除后做好git提交备注,不要用注释的方式留在源码里面,保持源码整洁

 

注释不是越多越好,如果规范的代码命名能做到自我解释则不需要写注释。

注释是需要维护的,如果代码有变更需要维护注释信息,错误的注释比没有注释危害更大!

1.  单行注释

 

// 这个函数的执行条件,执行结果大概说明
dosomthing()

 

2.  多行注释

 

/*
* xxxx  描述较多的时候可以使用多行注释
* xxxx
*/
dosomthing();

 

3.  函数(方法)注释 参考jsdoc

JSDoc 是一个根据 JavaScript 文件中注释信息,生成 JavaScript 应用程序或模块的API文档的工具。你可以使用 JSDoc 标记如:命名空间,类,方法,方法参数等。从而使开发者能够轻易地阅读代码,掌握代码定义的类和其属性和方法,从而降低维护成本,和提高开发效率。

 

注释名

语法

含义

示例

@param

@param 参数名 {参数类型} 描述信息

描述参数的信息

@param name {String} 传入名称

@return @return {返回类型} 描述信息

描述返回值的信息

@return {Boolean}

true:可执行;false:不可执行

@author

@author 作者信息 [附属信息:如邮箱、日期]

描述此函数作者的信息

@author 张三 2015/07/21

@version

@version XX.XX.XX

描述此函数的版本号

@version 1.0.3

@example

@example 示例代码

演示函数的使用

@example setTitle(‘测试’)

 

例如:

 

/**
* Book类,代表一个书本.
* @constructor
* @param {string} title - 书本的标题.
* @param {string} author - 书本的作者.
*/
functionBook(title, author){
  this.title=title;
  this.author=author;
}
Book.prototype={
/**
    * 获取书本的标题
    * @returns {string|*} 返回当前的书本名称
    */
getTitle:function(){
        returnthis.title;   
},
/**
    * 设置书本的页数
    * @param pageNum {number} 页数
    */
setPageNum:function(pageNum){
      this.pageNum=pageNum;   
}
};

 

 

九、  CodeReview

1.  规则,链接

所有影响到以往流程的功能需求更改发版前都需要codeReview

2.  执行者

初级程序员可由中级程序员的执行codeReview

中级程序员可由高级程序员执行codeReview

以此类推

3.  反馈

每次codereView都需要有反馈,要对本次codeReview负责

反馈内容基本如

功能:本次主要是修改了什么功能或者bug

模块:本次发版影响的模块

代码问题:codereview过程中发现的代码问题,比如代码性能,写法,代码风格等等

业务问题:比如发现了某某影响到其他模块的逻辑问题,如果没有发现就写。无 

 

十、  git规范具体可以参考(销售云git使用规范

1.  分支命名

master/prod : master/prod  分支就叫 prod 分支,线上环境正在使用的,每一次更新master都需要打tag;

 

beta/uat : beta/uat 分支预发布分支,上线前需要发布预发布环境进行测试,测试通过合并到master分支;

 

pre/test : pre/test 分支就供测试环境使用的分支;

 

dev : dev  分支就叫开发分支;

uat :uat   验收测试分支

prod-canary: prod-canary 灰度分支

 

dev-version dev分支 dev-V1.1.2 这种命名规范来。version 表示当前迭代的版本号(产品拉群名称、WBS、后端分支、wayne备注都和version相同);

bugfix-tagversion(name) :补丁分支 bugfix-tagversion ,name表示本次热修复的内容标题4-6字为益处;

 

斜杠 的方式 在source-tree中有归类的作用(其他团队可以使用这个方法归类)

2.  提交代码规范

 

注意:代码提交除了要遵循本章节的提交规范外还需要注意:

1.  当日代码当日提交!不要把所有的代码做一次提交,完成一个功能点就提交一次,6~7次最为合适

2.  提交代码内容需要备注清楚,且与当日日报内容相对应!

 

2.1.  使用模版 commitizen

 Commitizen for multi-repo projects部分

在完善中,会继承自动检测代码,可选输入发版提交版本基本信息等等

安装相关包

 

npm i kz-commit --save-dev
npm i husky --save-dev
npm i lint-staged --save-dev

 

配置package.json

 

{
"scripts": {
        "kz-ci": "git add . && ./node_modules/.bin/kz-commit"
      },
      "husky": {
        "hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
          "pre-commit": "lint-staged"
        }
      },
      "lint-staged": {
        "src/*.js": [
          "eslint --fix",
"git add"        
]
      }
}

 

 

其中 lint-staged 对应的 eslint 校验规则会写在项目根目录下的.eslintrc.js(如果没有就自己创建)

然后再写上一些不必要校验的选项,毕竟不是每个选项都需要校验

比如

具体语法 google eslintrc

 

module.exports = {
  parser: 'babel-eslint',
  env: {
    browser: true,
  },
  extends: 'eslint:recommended',
  // add your custom rules here
  rules: {
    // allow paren-less arrow functions
    'arrow-parens': 0,
    // allow async-await
    'generator-star-spacing': 0,
    'camelcase': 0,
    'no-undef': 0,
    'no-console': 0,
    'no-case-declarations': 0
  }
}

 

另外 lint-staged配置中的 src/*.js 表示当前目录下需要校验的js文件

 

2.2.  commit的时候备注

提交message标准格式如下:

 

<type>[scope]: <subject>[#bugId]

[body]

[footer]

 

● type

必填,规范名称。主要标识修改类型

规范名

描述

feat

新增feature

fix

修复bug

docs

仅文档更改,比如README,CHANGELOG,CONTRIBUTE等

style

不会影响代码含义的更改(空格,格式,缺少分号等)

revert

版本回滚

refactor

代码重构,没有新增功能或者修复bug

perf

代码更改可提高性能

chore

其他不会修改 src 或测试文件的更改(比如文档修改,构建流程)

merge

合并分支

test

测试用例,包括单元测试和、集成测试等

 

● scope

可选,对于开源项目可以是指定提交更改位置的任何内容,例如菜单,侧边栏等。对于公司以迭代周期维护的项目这里填对应迭代版本号,如果是紧急修复线上问题可以定义为 hotfix${date}

● subject

必填,提交标题,包含对变更的简洁描述

● body

可选提交描述,详细描述修改思路和提交内容,body 应包括改变的动机,并将其与以前的行为进行对比。也就是说,描述为什么修改,做了什么样的修改,以及开发的思路等,是 commit 的详细描述。

● footer

可选,主要增加一些辅助信息,入修改bug的禅道地址,或者修改bug参考的博客、github、stackoverflow地址等

样例

 

fix(v2.9.1.10):区域建群列表搜索功能fix #19999)

 

3.  发版规范

3.1.  版本开发

根据新的版本迭代,从prod创建一个对应的开发分支,一般这个分支都用当前版本号命名。不直接在这个分支上开发,这里简称为dev分支。每个开发人员都从dev分支创建属于自己的分支,在个人分支上开发,开发完成后将代码合并到这个dev分支上。

 

3.2.  版本提测

目前我们只有测试环境和正式环境两种环境。所以dev分支直接发布到测试环境,在此分支上修改bug。

 

根据测试的各个阶段可以分别有不通的发布环境,根据项目的需求初始化的时候可以从prod分支新建出各个测试版本的分支,入pre、uat、canary

 

1.  pre,预览分支主要是第一轮测试问题解决和修复分支,发版的时候可以由迭代开发分支merge到pre分支打包发布到pre环境

2.  uat,用户验收测试,此时大部分逻辑bug应该都被处理调了,ui和产品会介入做交互体验测试,uat的发版和pre类似,也是从迭代开发分支merge并到uat分支,然后打包发布到uat环境

3.  canary,灰度测试,此时逻辑和用户体验问题都已修复,主要是连接生产数据库测试因为环境和数据的差异而引发的问题,也是从迭代开发分支merge并到canary分支,然后打包发布到canary环境

 

3.3.  版本上线

上线前,从prod拉个待上线分支简称为release分支,并对prod分支打好tag。

如果项目有3.2节中提到的灰度测试环节,则可以直接将canary分支合并到release分支,如果没有则将迭代dev分支代码合并上去。合并完成,需要发一次测试环境,让测试最后确认一下,以免版本出问题。

 

3.4.  修改线上bug

从prod建立一个分支去修改bug,这里简称为hotfix分支,修改完毕提测,测试结束上线,上线完代码合并prod和dev分支。

 

3.5.  上线之后

release分支版本发布正式后,要完全合并到prod,保证prod代码就是正式环境的代码。

 

十一、  格式化工具

● Eslint

● Beautify

● Prettier - Code formatter

confluence链接

十二、  公共模块

1.  开发自定义公共模块

在项目中,我们会封装一些全局的公共组件、方法等进行复用,各模块间各司其职。下面将常见的公共模块进行说明。

 

 

├── ...
└── src
    ├── assets
    ├── api
    ├── directives
    ├── mixins
    ├── components
    ├── base
    ├── styles
    └── utils

 

assets 全局静态资源,一般存放图片、字体等文件。

api 公用接口地址

components 全局公共组件,存放多个页面/组件中会多次使用的 UI 组件。

directives 全局指令

mixins存放可复用的逻辑处理。

base 全局公共请求。

styles 全局公共样式。

utils 全局公共方法。

 

2.  使用第三方工具库

前端项目中已经引入的常用工具库如:

1.  lodash.js

2.  moment.js

3.  vant

4.  swiper

5.  modesign

 

对于前端常用的工具库要通读官方文档,项目中能借用它们已有能力实现的尽量采用,不要重复造轮子,常用的第三方开源库都是经过市场检验的,出bug的概率也相对较小

 

3.  测试

我们开发的公共模块通常是作为工具提供给业务使用,一般不会有测试人员来单独对公共库做测试,所以贡献的公共库必须有单元测试保障代码质量,关键的逻辑分支都要覆盖到。

 

十三、  代码贡献规范

对于日常项目中的组件,或者独立的功能,自己写的demo都可以上传,上传代码必须符合以下规范:

1.  vue组件贡献

贡献者可以直接上传一个vue文件,遵循第四章vue2开发规范,在此基础上还需要注意一下几点:

1.1.  必要部分

vue组件必须是单文件组件,上传一个.vue文件,此文件必须要包含<template><script><style>这三个部分。也就是说,这个组件的html,js和css都必须要有,组件与业务逻辑需要完全解耦,能让别人直接放到项目里就能使用。

 

1.2.  依赖注入说明

如果在main.js中有使用到外部第三方库,需要标注清楚是什么库,最好将插件文档地址一同注释,从而方便使用者在本地项目中。

 

1.3.  子组件或方法引入说明

一般情况单文件组建中会引入很多公共组件或公共方法,所以在上传.vue文件的时候,请务必仔细检查当前文件的import,可以不用修改路径(这样比较麻烦),但是必须要把引入的文件给带上,此处注意检查下关系,如果引入的文件还有引入,那也需要带上,然后最好在注释中说明清楚,让阅读者自己修改引入文件的位置。

当然不建议上传这种与项目本体有很多耦合的单文件组件,尽量上传单一逻辑、可复用的组件。

 

1.4.  参数注释说明

上传的单文件组件视为子组件,会在阅读者的项目中被引入,请遵循简单的父子组件通信流程,子组件中请定义好props,视为父组件传入的参数,请在props中注释好参数的意义,props中的参数,请定义好type和default。

 

params: {
   type: Boolean,//参数类型
default: false,//参数默认值
}

 

 

子组件中如有抛出逻辑,请标注清抛出事件的名称,以及抛出的值是什么,最好有个简单的数据结构注释。

 

this.$emit("change", val);
//change是子组件抛出的自定义事件名称
//val 是值

 

 

1.  js贡献

日常工作中,会有很多逻辑单一且能复用的js方法,例如防抖函数、时间戳转化函数等等,每个项目中都许多此类的方法,这些与项目本体解耦的方法欢迎贡献上传,但是上传时,请将方法参数说明等备注清楚,方便阅读者使用,如以下示例:

这是一个公共的防抖函数,在任意项目中均可以使用,请写清楚备注,即以下代码中的注释部分

 

/*
func:
类型:function
作用:传入需执行防抖的业务逻辑函数

delay:
类型:int
作用:执行防抖延迟毫秒数
*/
function debounce(func, delay) {
let timeout;
return function(e) {
clearTimeout(timeout);
let context = this, args = arguments
        timeout = setTimeout(function(){
func.apply(context, args);
},delay)
};
};

//以下是函数使用方法
//防抖函数初始化,传入必要参数,首参就是项目里要执行的逻辑。
//防抖函数初始化后会返回一个函数,请用变量接收。
const validate = debounce(function(e) {
    console.log("change", e.target.value, new Date-0)
}, 380);

// 绑定监听,监听的就是防抖函数返回的值。
document.querySelector("input").addEventListener('input', validate);

 

 

2.  css片段贡献

css方面其实有很多东西可以贡献,平时项目中的一些css小技巧,平时自己研究的一些c3动画等。

css片段贡献时,请注明片段意义/解决的问题,注释清楚代码的含义,例如以下代码片段:

 

标题:

移动端H5适配iphoneX底部栏,css快速解决方案

 

代码:

 

//meta标签放入viewport-fit=cover
<meta name="viewport" content="width=device-width...... viewport-fit=cover">

<style>
//底部栏加入以下样式
//safe-area-inset-bottom :安全距离底部边界的距离
@supports (bottom: env(safe-area-inset-bottom)){
    body,
    .footer{
        padding-bottom: constant(safe-area-inset-bottom);
        padding-bottom: env(safe-area-inset-bottom);
    }
}
</style>

 

 

3.  整个demo项目贡献

如果要将整个项目作为demo上传贡献,请务必做好项目说明,以便于阅读者了解项目和使用项目。

3.1.  项目简介

第一步请简短说明demo的作用,告知阅读者此demo能做些什么事情,例如:

vue-element-admin 是一个后台前端解决方案,它基于 vue 和 element-ui实现。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。

 

3.2.  功能列表说明

简单描述下功能列表,如果项目比较简单,可以省略这部,例如:

- 登录 / 注销

- 权限验证

  - 页面权限

  - 指令权限

  - 权限配置

  - 二步登录

- 多环境发布

  - dev sit stage prod

 

3.3.  安装指南

vue项目需要安装依赖和命令启动,这基本都是相同的,但是此处最好还是备注一下,例如:

 

#安装依赖
npm install

#本地开发 启动项目
npm run dev

#构建测试环境
npm run build:stage

#构建生产环境
npm run build:prod
#启动完成后会自动打开浏览器访问 http://localhost:9527, 你看到下面的页面就代表操作成功了。

 

 

十四、  开发工具&辅助插件

统一的开发工具能减少兼容性问题和沟通成本,公司指定的开发工具为:

● 代码编辑工具:VSCode

● 版本控制工具:Git

● 代码提交工具:SourceTree

● 代码编译和包管理:node & npm

● 调试工具:chrome devtools、vue devtools、微信开发者工具(小程序)

 

下面介绍一些 VSCode 辅助插件:

1.  CodeSpellChecker

Code Spell Checker 插件可以实现和 idea 自带的单词拼写检查一样的效果

1.1.  在扩展中搜索并安装 Code Spell Checker

1.2.  配置文件识别

1.  打开 Code Spell Checker 的扩展配置

2.  在扩展配置中检查 Enabled Language Ids 配置项目

3.  检查配置项下是否有vue、javascript、typescript、scss选项,如果没有则点下面的Add Item 按钮进行添加,新版的插件应该都默认支持了

4.  打开vue文件即可看到错误的或者不规范的单词都被标上了波浪线,如下图所示,插件准确识别出我们的 valdiator 拼错了,应该是 validator

5.  重构之旅,下图中 Code Spell Checker 为我们标出了四处错误:

 

mubiaoItem

命名不规范,拼音不是标准单词,改为 targetItem

groupshujuyuan

命名不规范,拼音不是标准单词,改为 groupDataSource

valdiator

单词拼错,纠正为 validator

labelshujuyuan

命名不规范,拼音不是标准单词,改为 labelDataSource

重构结果:

 

 

2.  ESLint

ESLint 可以根据我们配置的 exlintrc.js 快速识别文件中不符合规范的地方,并一键修复

 

2.1.  在扩展中搜索安装 ESLint

 

2.2.  文件中识别eslint错误并快速修复

点击Quick Fix 按钮:

选择 Fix all auto-fixable problems 一建修复整个文件的eslint问题

 

3.  Commit Message Editor

Commit Message Editor 是一款辅助生成git提交规范的插件,能够针对第十章的 commitlint规范快速生成标准的提交文案

3.1.  在扩展中安装 Commit Message Editor

安装完扩展以后在提交文件的时候就多出一个编辑提交信息的小图标:

 

3.2.  编辑commit信息

点击上图中的编辑提交信息按钮

 

点击save按钮就可以看到生成的提交信息:

 

提交成功后在Git Graph中查看刚刚提交的内容:

 

 

4.  GitHub Colipot使用(前端)

GitHub Copilot是一个人工智能配对程序员,可以帮助你更快、更少地编写代码。它从注释和代码中提取上下文,以立即建议单个行和整个函数。GitHub Copilot由GitHub、OpenAI和微软开发的生成式AI模型提供支持。它可以作为Visual Studio Code、Visual Studio、Neovim和JetBrains集成开发环境(ide)套件的扩展,本文主要介绍在vscode中的使用场景

 

打开vscode,点击左边的扩展栏,搜索 “github copilot”,点击install安装扩展,重启vscode

copilot功能非常丰富且copilot的使用也不是本文档所要阐述的主要内容,大家可以移步 

GitHub Colipot使用(前端) 详细了解

posted @ 2023-12-19 23:15  PeterWolf  阅读(8)  评论(0)    收藏  举报