code-review以前端代碼規範及優化

所謂無規矩不成方圓,前端時間在團隊 code-review 中發現,不一樣時期不一樣開發人員寫的代碼可謂五花八門。所以咱們提出了一些相關代碼方面的規範,但願往後能造成團隊的編碼規範。javascript

制定開發規範的目的css

  • 統一編碼風格,規範,提升團隊協做效率
  • 在團隊協做中輸出可讀性強,易維護,風格一致的代碼

本文在gitthub作了收錄:github.com/Michael-lzg…html

HTML 篇

啓用標準模式

使用 HTML5 的 doctype 來啓用標準模式前端

<!DOCTYPE html>
複製代碼

統一使用 UTF-8 編碼

<meta charset="utf-8" />
複製代碼

優先使用 IE 最新版本和 Chrome

<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
複製代碼

移動設備添加 viewport

<meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no" />
複製代碼

自閉合標籤無需閉合

例如: imginputbrhrvue

<img src="https://xxx.png" alt="Google" />
<br />
<input type="text" name="title" />
複製代碼

使用語義化標籤

html 的標籤能使用語義化的,儘可能使用語義化標籤,避免一個頁面都是 div 或者 p 標籤java

<!-- bad -->
<div>
  <p></p>
</div>

<!-- good -->
<header></header>
<footer></footer>
複製代碼

屬性順序要求

HTML 屬性應該按照特定的順序出現以保證易讀性。webpack

id
class
name
data-xxx
src, for, type, href
title, alt
aria-xxx, role
複製代碼

CSS 篇

BEM 命名原則

  • block:模塊,名字單詞間用 - 鏈接
  • element:元素,模塊的子元素,以 __ 與 block 鏈接
  • modifier:修飾,模塊的變體,定義特殊模塊,以 -- 與 block 鏈接
/* 舉個例子 */
.block__element {
}
.block--modifier {
}
複製代碼

有效使用 css 選擇器

選擇器嵌套應少於 3 級git

/* bad */
.page .header .login #username input {
}

/* good */
#username input {
}
複製代碼

有效使用 css 選擇器,因遵循如下原則es6

  • 保持簡單,不要使用嵌套過多過於複雜的選擇器。
  • 通配符和屬性選擇器效率最低,須要匹配的元素最多,儘可能避免使用。
  • 不要使用類選擇器和 ID 選擇器修飾元素標籤。
  • 不要爲了追求速度而放棄可讀性與可維護性
  • 避免使用 CSS 表達式

慎重選擇高消耗的樣式

高消耗屬性在繪製前須要瀏覽器進行大量計算:github

box-shadows
border-radius
transparency
transforms
CSS filters(性能殺手)
複製代碼

避免重繪重排

當發生重排的時候,瀏覽器須要從新計算佈局位置與大小,不利於性能優化。

常見引發重繪重排屬性和方法

  • 添加或者刪除可見的 DOM 元素;
  • 元素尺寸改變——邊距、填充、邊框、寬度和高度
  • 內容變化,好比用戶在 input 框中輸入文字
  • 瀏覽器窗口尺寸改變——resize 事件發生時
  • 計算 offsetWidthoffsetHeight 屬性
  • 設置 style 屬性的值

減小重繪重排的方法

  • 使用 transform 替代 top
  • 使用 visibility 替換 display: none ,由於前者只會引發重繪,後者會引起迴流(改變了佈局)
  • 不要把節點的屬性值放在一個循環裏當成循環裏的變量。
  • 不要使用 table 佈局,可能很小的一個小改動會形成整個 table 的從新佈局
  • 動畫實現的速度的選擇,動畫速度越快,迴流次數越多,也能夠選擇使用 requestAnimationFrame
  • CSS 選擇符從右往左匹配查找,避免節點層級過多

Javascript 篇

關於命名

普通命名採用小駝峯式命名

let userName = 'jack'
複製代碼

命名是複數的時候須要加 s,好比說我想聲明一個數組,表示不少人的名字

let names = new Array()
複製代碼

每一個常量都需命名,這樣更利於別人讀懂含義

// good
const COL_NUM = 10
let row = Math.ceil(num / COL_NUM)

// bad
let row = Math.ceil(num / 10)
複製代碼

命名須要符合語義化,若是函數命名,能夠採用加上動詞前綴:

  • can 判斷是否可執行某個動做
  • has 判斷是否含有某個值
  • is 判斷是否爲某個值
  • get 獲取某個值
  • set 設置某個值
//是否可閱讀
function canRead(){
   return true;
}
//獲取姓名
function getName{
   return this.name
}
複製代碼

關於字符串

統一使用單引號而不是雙引號

// bad
const name = 'jack'

// good
const name = 'jack'
複製代碼

用字符串模板而不是 '+' 來拼接字符串

function sayHi(name) {
  return 'How are you, ' + name + '?'
}

// good
function sayHi(name) {
  return `How are you, ${name}?`
}
複製代碼

關於數組

用字面量賦值

// bad
const items = new Array()

// good
const items = []
複製代碼

用擴展運算符作數組淺拷貝

// bad
let arr = [1, 2, 3]
const len = arr.length
const copyArr = []

for (let i = 0; i < len; i += 1) {
  copyArr[i] = arr[i]
}

// good
const copyArr = [...arr]
複製代碼

用 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)
複製代碼

使用數組解構

const arr = [1, 2, 3, 4]

// bad
const first = arr[0]
const second = arr[1]

// good
const [first, second] = arr
複製代碼

關於對象

建立對象和數組推薦使用字面量,由於這不只是性能最優也有助於節省代碼量。

// good
let obj = {
  name: 'Tom',
  age: 15,
  sex: '男',
}

// bad
let obj = {}
obj.name = 'Tom'
obj.age = 15
obj.sex = '男'
複製代碼

ES6 使用屬性值縮寫

const lukeSkywalker = 'Luke Skywalker'

// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
}

// good
const obj = {
  lukeSkywalker,
}
複製代碼

將屬性的縮寫放在對象聲明的開頭

const anakinSkywalker = 'Anakin Skywalker'
const lukeSkywalker = 'Luke Skywalker'

// bad
const obj = {
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  lukeSkywalker,
  episodeThree: 3,
  mayTheFourth: 4,
  anakinSkywalker,
}

// good
const obj = {
  lukeSkywalker,
  anakinSkywalker,
  episodeOne: 1,
  twoJediWalkIntoACantina: 2,
  episodeThree: 3,
  mayTheFourth: 4,
}
複製代碼

對象淺拷貝時,更推薦使用擴展運算符 ...,而不是 Object.assign。解構賦值獲取對象指定的幾個屬性時,推薦用 rest 運算符,也是 ...。

// very bad
const original = { a: 1, b: 2 }
const copy = Object.assign(original, { c: 3 })
delete copy.a // 改變了 original

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

// 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 }
複製代碼

關於函數

函數參數使用默認值替代使用條件語句進行賦值。

// good
function createMicrobrewery(name = 'Jack') {
   ...
}

// bad
function createMicrobrewery(name) {
  const userNameName = name || 'Jack'
   ...
}
複製代碼

函數參數使用結構語法,函數參數越少越好,若是參數超過兩個,要使用 ES6 的解構語法,不用考慮參數的順序。

// good
function createMenu({ title, body, buttonText, cancellable }) {
   ...
}

createMenu({
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true,
})

// bad
function createMenu(title, body, buttonText, cancellable) {
  // ...
}
複製代碼

優先使用 rest 語法...,而不是 arguments

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

// good
function concatenateAll(...args) {
  return args.join('')
}
複製代碼

把默認參數賦值放在最後

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

// good
function handleThings(name, opts = {}) {
  // ...
}
複製代碼

儘可能使用箭頭函數

// 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
  })
複製代碼

關於模塊

在非標準模塊系統上使用(import/export)

// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide')
module.exports = AirbnbStyleGuide.es6

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide'
export default AirbnbStyleGuide.es6

// best
import { es6 } from './AirbnbStyleGuide'
export default es6
複製代碼

一個入口只 import 一次

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

// good
import foo, { named1, named2 } from 'foo'
複製代碼

在只有一個導出的模塊裏,用 export default 更好

// bad
export function foo() {}

// good
export default function foo() {
複製代碼

for 循環

使用 for 循環過程當中,數組的長度,使用一個變量來接收,這樣有利於代碼執行效率獲得提升,而不是每走一次循環,都得從新計算數組長度

// bad
for(var i = 0; i < arr.length; i++){

}

// good
for(var i = 0; len = arr.length; i < len; i++){

}
複製代碼

Vue 篇

Prop 定義儘可能詳細。

prop 的定義應該儘可能詳細,至少須要指定其類型。

// bad
props: ['status']

// good
props: {
  status: String
}

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

v-for 遍歷必須添加 key

在列表數據進行遍歷渲染時,須要爲每一項 item 設置惟一 key 值,方便 Vue.js 內部機制精準找到該條列表數據。當 state 更新時,新的狀態值和舊的狀態值對比,較快地定位到 diff

<!-- 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>
複製代碼

v-if 和 v-for 不要用在同一個元素上。

v-forv-if 優先級高,若是每一次都須要遍歷整個數組,將會影響速度,尤爲是當之須要渲染很小一部分的時候。

<!-- 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>
複製代碼

組件的 data 必須是一個函數

JS 中的實例是經過構造函數來建立的,每一個構造函數能夠 new 出不少個實例,那麼每一個實例都會繼承原型上的方法或屬性。Vue 的 data 數據實際上是 Vue 原型上的屬性,數據存在於內存當中。

同一個組件被複用屢次,會建立多個實例。這些實例用的是同一個構造函數,若是 data 是一個對象的話。那麼全部組件都共享了同一個對象。爲了保證組件的數據獨立性,要求每一個組件必須經過 data 函數返回一個對象做爲組件的狀態,這樣每複用一次組件,就會返回一份新的 data。

// bad
Vue.component('some-comp', {
  data: {
    foo: 'bar',
  },
})

// good
Vue.component('some-comp', {
  data: function () {
    return {
      foo: 'bar',
    }
  },
})
複製代碼

組件模板應該書寫簡潔

組件模板應該只包含簡單的表達式,複雜的表達式則應該重構爲計算屬性或方法。

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

// good
// 在模板中
{{ normalizedFullName }}
// 複雜表達式已經移入一個計算屬性
computed: {
  normalizedFullName: function () {
    return this.fullName.split(' ').map(function (word) {
      return word[0].toUpperCase() + word.slice(1)
    }).join(' ')
  }
}
複製代碼

指令縮寫

<!-- bad -->
<input v-bind:value="newTodoText" :placeholder="newTodoInstructions" v-on:input="onInput" />
<!-- good -->
<input :value="newTodoText" :placeholder="newTodoInstructions" @input="onInput" />
複製代碼

組件名爲多個單詞

咱們開發過程當中自定義的組件的名稱須要爲多個單詞,這樣作能夠避免跟現有的以及將來的 HTML 元素相沖突,由於全部的 HTML 元素名稱都是單個單詞的。

// good
Vue.component('todo-item', {
  // ...
})
export default {
  name: 'TodoItem',
  // ...
}

// bad
Vue.component('todo', {
  // ...
})

export default {
  name: 'Todo',
  // ...
}
複製代碼

多個屬性進行分行

在 JavaScript 中,用多行分隔對象的多個屬性是很常見的最佳實踐,由於這樣更易讀。

<!-- good -->
<MyComponent foo="a" bar="b" baz="c" />

<!-- bad -->
<MyComponent foo="a" bar="b" baz="c" />
複製代碼

元素特性的順序

原生屬性放前面,指令其次,傳參和方法放最後

- class, id, ref
  - name, data-*, src, alt, for, type, href, value, max, min
  - title, placeholder, aria-*, role
  - required, readonly, disabled
  - v-model, v-for, key, v-if, v-show, v-bind,:
  - foo="a" bar="b" baz="c"
複製代碼

關於組件內樣式

爲組件樣式設置做用域

/* bad  */
<style>
.btn-close {
  background-color: red;
}
</style>

/* good  */
<style scoped>
.button-close {
  background-color: red;
}
</style>
複製代碼

若要改變第三方組件庫的樣式,須要加上頂級做用域。

/* bad */
.ivu-input {
  width: 254px !important;
}

/* good */
.customerForm .ivu-input {
  width: 254px !important;
}
/* .customerForm爲當前組件的頂級dom */
複製代碼

關於組件結構

組件結構遵循從上往下 template,script,style 的結構。

<template>
  <div></div>
</template>

<script> export default {} </script>

<style lang="scss" scoped></style>
複製代碼

script 部分各方法成員遵循如下順序放置。

- name
- components
- props
- data
- methods
- computed
- watch
- created
- mounted
- update
複製代碼

清除定時器或者事件監聽

因爲項目中有些頁面不免會碰到須要定時器或者事件監聽。可是在離開當前頁面的時候,定時器若是不及時合理地清除,會形成業務邏輯混亂甚至應用卡死的狀況,這個時就須要清除定時器事件監聽,即在頁面卸載(關閉)的生命週期函數裏,清除定時器。

methods:{
  resizeFun () {
    this.tableHeight = window.innerHeight - document.getElementById('table').offsetTop - 128
  },
  setTimer() {
    this.timer = setInterval(() => { })
  },
  clearTimer() {
		clearInterval(this.timer)
    this.timer = null
	}
},
mounted() {
  this.setTimer()
  window.addEventListener('resize', this.resizeFun)
},
beforeDestroy() {
  window.removeEventListener('resize', this.resizeFun)
  this.clearTimer()
}
複製代碼

路由懶加載

Vue 是單頁面應用,可能會有不少的路由引入 ,這樣使用 webpcak 打包後的文件很大,當進入首頁時,加載的資源過多,頁面會出現白屏的狀況,不利於用戶體驗。若是咱們能把不一樣路由對應的組件分割成不一樣的代碼塊,而後當路由被訪問的時候才加載對應的組件,這樣就更加高效了。

{
  path: '/Home',
  component: () => import('@/views/Home.vue')
}
複製代碼

職責單一

任什麼時候候儘可能是的一個函數就作一件事情,而不是將各類邏輯所有耦合在一塊兒,提升單個函數的複用性和可讀性。好比:每一個頁面都會在加載完成時進行數據的請求並展現到頁面。

// bad
methods: {
  getList1() {
    // to do ...
  },
  getList2() {
    // to do ...
  }
},
created() {
  this.getList1()
  this.getList2()
},

// good
methods: {
  // 將所有的請求行爲聚合在init函數中
  init() {
    this.getList1()
    this.getList2()
  },
  getList1() {
    // to do ...
  },
  getList2() {
    // to do ...
  }
},
created() {
  this.init();
},
複製代碼

第三方 UI 組件按需引入

咱們在項目中使用的第三方 UI 組件,若是咱們直接引入整個組件庫,會致使項目的體積太大,咱們能夠藉助 babel-plugin-component ,而後能夠只引入須要的組件,以達到減少項目體積的目的。如下爲項目中引入 vant 爲例:

一、首先,安裝 babel-plugin-component

npm install babel-plugin-component -D
複製代碼

二、修改 .babelrc

{
  "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
}
複製代碼

三、引入部分組件:

import Vue from 'vue'
import { Button } from 'vant'

Vue.use(Button)
複製代碼

圖片篇:

使用恰當的圖片格式。

  • jpg:適用於內容圖片多爲照片之類的。
  • png:適用於而飾圖片,一般更適合用無損壓縮。
  • gif: 基本上除了 gif 動畫外不要使用。
  • webP:大大減少圖片的體積,可是移動端有兼容性問題。

使用雪碧圖

雪碧圖,CSS Sprites,國內也叫 CSS 精靈,是一種 CSS 圖像合成技術,主要用於小圖片顯示。

雪碧圖的優勢是把諸多小圖片合成一張大圖,利用backround-position屬性值來肯定圖片呈現的位置,這樣就能減小 http 請求,到達性能優化的效果。

使用 iconfont

iconfont(字體圖標),即經過字體的方式展現圖標,多用於渲染圖標、簡單圖形、特殊字體等。

使用 iconfont 時,因爲只須要引入對應的字體文件便可,這種方法可有效減小 HTTP 請求次數,並且通常字體體積較小,因此請求傳輸數據量較少。與直接引入圖片不一樣,iconfont 能夠像使用字體同樣,設置大小、顏色及其餘樣式,且不存在失真的狀況。

圖片懶加載

圖片懶加載的原理就是暫時不設置圖片的 src 屬性,而是將圖片的 url 隱藏起來,好比先寫在 data-src 裏面,等某些事件觸發的時候(好比滾動到底部,點擊加載圖片)再將圖片真實的 url 放進 src 屬性裏面,從而實現圖片的延遲加載。

function lazyload() {
  var images = document.getElementsByTagName('img')
  var len = images.length
  var n = 0 //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷
  return function () {
    var seeHeight = document.documentElement.clientHeight
    for (var i = n; i < len; i++) {
      if (images[i].getBoundingClientRect().top < seeHeight) {
        //方法二: 當圖片的視口top出如今視口中
        if (images[i].getAttribute('src') === 'images/default.jpg') {
          images[i].src = images[i].getAttribute('data-src')
        }
        n = n + 1
      }
    }
  }
}
複製代碼

vue 項目能夠 vue-lazyload 插件實現圖片懶加載

main.js 中全局引入:

import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyLoad, {
  preLoad: 1,
  error: require('./assets/img/error.jpg'),
  loading: require('./assets/img/homePage_top.jpg'),
  attempt: 2,
})
複製代碼

頁面中使用

<li v-for="(item,index) in imgList">
  <img v-lazy="item" alt="" />
</li>
複製代碼

推薦文章

相關文章
相關標籤/搜索