项目源自 github 的项目 frontend-guidelines ,对它做了一个翻译。
HTML文档 语义 HTML5为我们提供了许多用于精确描述内容的语义元素,用来确保你可以从其丰富的词汇中获益。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div id ="main" > <div class ="article" > <div class ="header" > <h1 > Blog post</h1 > <p > Published: <span > 21st Feb, 2015</span > </p > </div > <p > …</p > </div > </div > <main > <article > <header > <h1 > Blog post</h1 > <p > Published: <time datetime ="2015-02-21" > 21st Feb, 2015</time > </p > </header > <p > …</p > </article > </main >
确保你理解了正在使用元素的语义,用错了比不用还糟糕。
1 2 3 4 5 6 7 8 9 10 11 <h1 > <figure > <img alt =Company src =logo.png > </figure > </h1 > <h1 > <img alt =Company src =logo.png > </h1 >
简明 保持代码简要,不要按照 xhtml 的方法瞎搞。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <!doctype html > <html lang =en > <head > <meta http-equiv =Content-Type content ="text/html; charset=utf-8" /> <title > Contact</title > <link rel =stylesheet href =style.css type =text/css /> </head > <body > <h1 > Contact me</h1 > <label > Email address: <input type =email placeholder =you@email.com required =required /> </label > <script src =main.js type =text/javascript > </script > </body > </html > <!doctype html > <html lang =en > <meta charset =utf-8 > <title > Contact</title > <link rel =stylesheet href =style.css > <h1 > Contact me</h1 > <label > Email address: <input type =email placeholder =you@email.com required > </label > <script src =main.js > </script > </html >
易用性 易用性不该忘了,虽然你不需要变成 WCAG [无障碍网页内容指引,Web Content Accessibility Guidelines]专家,但你可以通过一些小改进获得巨大的效益:
学习使用 alt
元素属性
确保 link 和 button 用了正确标签 (别有 <div class=button>
这种暴行)
不依赖于颜色来传达信息
明确标注表单控件
1 2 3 4 5 <h1 > <img alt ="Logo" src ="logo.png" > </h1 > <h1 > <img alt ="My Company, Inc." src ="logo.png" > </h1 >
语言 虽然你不一定需要指定语言和字符编码,但是建议你document的基本声明一下(即使 header 里面已经声明了)。
字符编码推荐 UTF-8。
中文编码参考资料
1 2 3 4 5 6 7 8 9 10 <!doctype html > <title > Hello, world.</title > <!doctype html > <html lang =en > <meta charset =utf-8 > <title > Hello, world.</title > </html >
性能 除非找了个牛逼的理由把JS脚本放在正文前面,否则应该避免这种阻塞渲染的做法。
如果 CSS 非常重,可以把初始化的时候需要的样式隔离,后续需要的样式放到另一个CSS 文件里延时加载。
两个请求肯定不如一个快,但是更重要的是用户感知的速度。
1 2 3 4 5 6 7 8 9 10 11 12 13 <!doctype html > <meta charset =utf-8 > <script src =analytics.js > </script > <title > Hello, world.</title > <p > ...</p > <!doctype html > <meta charset =utf-8 > <title > Hello, world.</title > <p > ...</p > <script src =analytics.js > </script >
CSS样式 分号 因为分号是分隔符,所以应该始终以分号结尾。
1 2 3 4 5 6 7 8 9 div { color : red } div { color : red; }
Box model 理想情况下,整个文档的Box model应该相同。 全局* {box-sizing:border-box; }
就行,能不改特定元素的默认框模型 就不要改。
1 2 3 4 5 6 7 8 9 10 11 div { width : 100% ; padding : 10px ; box-sizing : border-box; } div { padding : 10px ; }
Flow 能不改元素的默认行为就不要改,让元素在document里尽可能多地流动。
例如,删除图像下方的空白区域,不应该更改其默认display:
1 2 3 4 5 6 7 8 9 img { display : block; } img { vertical-align : middle; }
同理,能不让元素脱离文档流就不要脱离。
1 2 3 4 5 6 7 8 9 10 11 12 div { width : 100px ; position : absolute; right : 0 ; } div { width : 100px ; margin-left : auto; }
改变位置 有很多方法可以在CSS中定位元素,但是用下面这些属性/值的时候尽可能严格要求自己。 按优先顺序:
1 2 3 4 5 6 display: block; display: flex; position: relative; position: sticky; position: absolute; position: fixed;
选择器 最小化与DOM紧密耦合的选择器。 选择器超过3个结构伪类、后代或同级组合器时,为要匹配的元素添加一个类。
1 2 3 4 5 div :first-of-type :last-child > p ~ *div :first-of-type .info
避免重载不必要的选择器
1 2 3 4 5 6 7 8 9 img [src$=svg] , ul > li :first-child { opacity : 0 ; } [src$=svg] , ul > :first-child { opacity : 0 ; }
准确选择 不要搞得值和选择器难以重构,尽量少用id
‘,尽量不用!important
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .bar { color : green !important ; } .foo { color : red; } .foo .bar { color : green; } .foo { color : red; }
覆盖 覆盖样式会让调试选择器变难,避免这样做。
1 2 3 4 5 6 7 8 9 10 11 12 li { visibility : hidden; } li :first-child { visibility : visible; } li + li { visibility : hidden; }
继承 不要重复可以继承的样式声明。
1 2 3 4 5 6 7 8 9 div h1 , div p { text-shadow : 0 1px 0 #fff ; } div { text-shadow : 0 1px 0 #fff ; }
简洁性 保持代码精简,使用速记属性,避免用多个不必要的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 div { transition : all 1s ; top : 50% ; margin-top : -10px ; padding-top : 5px ; padding-right : 10px ; padding-bottom : 20px ; padding-left : 10px ; } div { transition : 1s ; top : calc (50% - 10px ); padding : 5px 10px 20px ; }
语言 能用英文的不用数字。
1 2 3 4 5 6 7 8 9 :nth-child(2n + 1) { transform : rotate (360deg ); } :nth-child(odd) { transform : rotate (1turn ); }
浏览器引擎前缀 积极的去掉过时前缀。如果需要用前缀要房子标准属性之前。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 div { transform : scale (2 ); -webkit-transform : scale (2 ); -moz-transform : scale (2 ); -ms-transform : scale (2 ); transition : 1s ; -webkit-transition : 1s ; -moz-transition : 1s ; -ms-transition : 1s ; } div { -webkit-transform : scale (2 ); transform : scale (2 ); transition : 1s ; }
动画 transition比animation好。可以使用opacity
和 transform
的时候避免使用animation。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 div :hover { animation : move 1s forwards; } @keyframes move { 100% { margin-left : 100px ; } } div :hover { transition : 1s ; transform : translateX (100px ); }
单位 尽量使用无单位的数字,如果用相对单位rem
更好,秒比毫秒好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 div { margin : 0px ; font-size : .9em ; line-height : 22px ; transition : 500ms ; } div { margin : 0 ; font-size : .9rem ; line-height : 1.5 ; transition : .5s ; }
颜色 用透明度就用rgba
,否则用十六机制数比较好。
1 2 3 4 5 6 7 8 9 div { color : hsl (103 , 54% , 43% ); } div { color : #5a3 ; }
画图 CSS 可以复现的不要用http 请求资源来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 div ::before { content : url (white-circle.svg); } div ::before { content : "" ; display : block; width : 20px ; height : 20px ; border-radius : 50% ; background : #fff ; }
Hacks 别用
1 2 3 4 5 6 7 8 9 10 11 div { // position: relative; transform : translateZ (0); } div { will-change : transform; }
JavaScript 性能 可读性,正确性和表现力比性能重要多了。
JS 基本上不会成为你的性能瓶颈。
优化图像压缩,网络访问和DOM回流等功能效果好多了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const arr = [1 , 2 , 3 , 4 ];const len = arr.length;var i = -1 ;var result = [];while (++i < len) { var n = arr[i]; if (n % 2 > 0 ) continue ; result.push(n * n); } const arr = [1 , 2 , 3 , 4 ];const isEven = n => n % 2 == 0 ;const square = n => n * n;const result = arr.filter(isEven).map(square);
无状态 保持函数的干净。
理想情况下,所有功能都不会产生副作用、不使用外部数据,并返回新对象、不突变现有的对象。
1 2 3 4 5 6 7 const merge = (target, ...sources ) => Object .assign(target, ...sources);merge({ foo : "foo" }, { bar : "bar" }); const merge = (...sources ) => Object .assign({}, ...sources);merge({ foo : "foo" }, { bar : "bar" });
原生函数 尽量使用原生函数
1 2 3 4 5 6 7 const toArray = obj => [].slice.call(obj);const toArray = (() => Array .from ? Array .from : obj => [].slice.call(obj) )();
隐式声明 隐式声明有意义则拥抱,否则避免。不要cargo-cult ,不要相信约翰布鲁姆教。
1 2 3 4 5 if (x === undefined || x === null ) { ... }if (x == undefined ) { ... }
循环 不要使用循环去艹可变对象。使用array.prototype
方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const sum = arr => { var sum = 0 ; var i = -1 ; for (;arr[++i];) { sum += arr[i]; } return sum; }; sum([1 , 2 , 3 ]); const sum = arr => arr.reduce((x, y ) => x + y); sum([1 , 2 , 3 ]);
如果不行,用 array.prototype
很丑 or 很有争议,用递归。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const createDivs = howMany => { while (howMany--) { document .body.insertAdjacentHTML("beforeend" , "<div></div>" ); } }; createDivs(5 ); const createDivs = howMany => [...Array(howMany)].forEach(() => document .body.insertAdjacentHTML("beforeend" , "<div></div>" ) ); createDivs(5 ); const createDivs = howMany => { if (!howMany) return ; document .body.insertAdjacentHTML("beforeend" , "<div></div>" ); return createDivs(howMany - 1 ); }; createDivs(5 );
参数 忘掉 arguments
对象。Rest参数比一个 option 对象要好:
它有名字,能更好的帮助你知道它预期的意义。
数组比较好用
1 2 3 4 5 6 const sortNumbers = () => Array .prototype.slice.call(arguments ).sort(); const sortNumbers = (...numbers ) => numbers.sort();
Apply 忘了apply()
,用...
展开的参数。
1 2 3 4 5 6 7 8 const greet = (first, last ) => `Hi ${first} ${last} ` ;const person = ["John" , "Doe" ];greet.apply(null , person); greet(...person);
Bind 有更好习惯的用法时,不要用bind()
。
1 2 3 4 5 ["foo" , "bar" ].forEach(func.bind(this )); ["foo" , "bar" ].forEach(func, this );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const person = { first: "John" , last: "Doe" , greet() { const full = function ( ) { return `${this .first} ${this .last} ` ; }.bind(this ); return `Hello ${full()} ` ; } } const person = { first: "John" , last: "Doe" , greet() { const full = () => `${this .first} ${this .last} ` ; return `Hello ${full()} ` ; } }
高阶函数 避免不必要的函数嵌套。
1 2 3 4 5 [1 , 2 , 3 ].map(num => String (num)); [1 , 2 , 3 ].map(String );
Composition 避免多层函数嵌套,用composition更好。
1 2 3 4 5 6 7 8 9 10 const plus1 = a => a + 1 ;const mult2 = a => a * 2 ;mult2(plus1(5 )); const pipeline = (...funcs ) => val => funcs.reduce((a, b ) => b(a), val);const addThenMult = pipeline(plus1, mult2);addThenMult(5 );
缓存 缓存昂贵的操作,包括不限于功能测试、大数据结构等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const contains = (arr, value ) => Array .prototype.includes ? arr.includes(value) : arr.some(el => el === value); contains(["foo" , "bar" ], "baz" ); const contains = (() => Array .prototype.includes ? (arr, value ) => arr.includes(value) : (arr, value ) => arr.some(el => el === value) )(); contains(["foo" , "bar" ], "baz" );
声明变量 const
> let
> var
1 2 3 4 5 6 7 var me = new Map ();me.set("name" , "Ben" ).set("country" , "Belgium" ); const me = new Map ();me.set("name" , "Ben" ).set("country" , "Belgium" );
条件语句 IIFE 比一堆的 if else 和 switch 要好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var grade;if (result < 50 ) grade = "bad" ; else if (result < 90 ) grade = "good" ; else grade = "excellent" ; const grade = (() => { if (result < 50 ) return "bad" ; if (result < 90 ) return "good" ; return "excellent" ; })();
对象迭代 避免 for...in
,尽可能函数式编程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const shared = { foo : "foo" };const obj = Object .create(shared, { bar: { value: "bar" , enumerable: true } }); for (var prop in obj) { if (obj.hasOwnProperty(prop)) console .log(prop); } Object .keys(obj).forEach(prop => console .log(prop));
对象当字典 Maps 比 objects 更强大,有疑问的时候用Map
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const me = { name: "Ben" , age: 30 }; var meSize = Object .keys(me).length;meSize; me.country = "Belgium" ; meSize++; meSize; const me = new Map ();me.set("name" , "Ben" ); me.set("age" , 30 ); me.size; me.set("country" , "Belgium" ); me.size;
Curry 咖喱是许多开发人员的强大但外来的范式。 不要滥用它,因为适用它的地方很少。
1 2 3 4 5 6 7 const sum = a => b => a + b;sum(5 )(3 ); const sum = (a, b ) => a + b;sum(5 , 3 );
可读性 不要使用看似聪明的技巧来模糊你的代码的意图。
1 2 3 4 5 foo || doSomething(); if (!foo) doSomething();
1 2 3 4 5 void function ( ) { }();(function ( ) { }());
1 2 3 4 5 const n = ~~3.14 ;const n = Math .floor(3.14 );
代码复用 别怕造一堆的可复用的小的函数。
1 2 3 4 5 6 7 arr[arr.length - 1 ]; const first = arr => arr[0 ];const last = arr => first(arr.slice(-1 ));last(arr);
1 2 3 4 5 6 7 const product = (a, b ) => a * b;const triple = n => n * 3 ;const product = (a, b ) => a * b;const triple = product.bind(null , 3 );
依赖 尽量最小化依赖,三方代码是你无法掌控的,不要为了一些容易实现的功能区引用一个完整的大库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var _ = require ("underscore" );_.compact(["foo" , 0 ])); _.unique(["foo" , "foo" ]); _.union(["foo" ], ["bar" ], ["foo" ]); const compact = arr => arr.filter(el => el);const unique = arr => [...Set(arr)];const union = (...arr ) => unique([].concat(...arr));compact(["foo" , 0 ]); unique(["foo" , "foo" ]); union(["foo" ], ["bar" ], ["foo" ]);