所谓无规矩不成方圆,前端时间在团队 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开发经常使用技巧(干货满满哦!)