所謂無規矩不成方圓,前端時間在團隊 code-review
中發現,不一樣時期不一樣開發人員寫的代碼可謂五花八門。所以咱們提出了一些相關代碼方面的規範,但願往後能造成團隊的編碼規範。css
制定開發規範的目的html
本文在gitthub作了收錄:https://github.com/Michael-lz...前端
使用 HTML5 的 doctype
來啓用標準模式vue
<!DOCTYPE html>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no" />
例如: img
, input
, br
, hr
等webpack
<img src="https://xxx.png" alt="Google" /> <br /> <input type="text" name="title" />
html 的標籤能使用語義化的,儘可能使用語義化標籤,避免一個頁面都是 div 或者 p 標籤git
<!-- bad --> <div> <p></p> </div> <!-- good --> <header></header> <footer></footer>
HTML 屬性應該按照特定的順序出現以保證易讀性。es6
id class name data-xxx src, for, type, href title, alt aria-xxx, role
/* 舉個例子 */ .block__element { } .block--modifier { }
選擇器嵌套應少於 3 級github
/* bad */ .page .header .login #username input { } /* good */ #username input { }
有效使用 css 選擇器,因遵循如下原則web
高消耗屬性在繪製前須要瀏覽器進行大量計算:chrome
box-shadows border-radius transparency transforms CSS filters(性能殺手)
當發生重排的時候,瀏覽器須要從新計算佈局位置與大小,不利於性能優化。
常見引發重繪重排屬性和方法
DOM
元素;input
框中輸入文字resize
事件發生時offsetWidth
和 offsetHeight
屬性style
屬性的值減小重繪重排的方法
transform
替代 top
visibility
替換 display: none
,由於前者只會引發重繪,後者會引起迴流(改變了佈局)table
佈局,可能很小的一個小改動會形成整個 table
的從新佈局requestAnimationFrame
普通命名採用小駝峯式命名
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)
命名須要符合語義化,若是函數命名,能夠採用加上動詞前綴:
//是否可閱讀 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 循環過程當中,數組的長度,使用一個變量來接收,這樣有利於代碼執行效率獲得提升,而不是每走一次循環,都得從新計算數組長度
// bad for(var i=0;i<arr.length,i++){ } // good for(var i=0;i<arr.length,i++){ }
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 } } }
在列表數據進行遍歷渲染時,須要爲每一項 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-for
比 v-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>
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 組件,若是咱們直接引入整個組件庫,會致使項目的體積太大,咱們能夠藉助 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)
雪碧圖,CSS Sprites
,國內也叫 CSS 精靈,是一種 CSS 圖像合成技術,主要用於小圖片顯示。
雪碧圖的優勢是把諸多小圖片合成一張大圖,利用backround-position
屬性值來肯定圖片呈現的位置,這樣就能減小 http 請求,到達性能優化的效果。
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>
總結18個webpack插件,總會有你想要的!
搭建一個 vue-cli4+webpack 移動端框架(開箱即用)
從零構建到優化一個相似vue-cli的腳手架
封裝一個toast和dialog組件併發布到npm
從零開始構建一個webpack項目
總結幾個webpack打包優化的方法
總結vue知識體系之高級應用篇
總結vue知識體系之實用技巧
總結vue知識體系之基礎入門篇
總結移動端H5開發經常使用技巧(乾貨滿滿哦!)