前端指南 - HTML/CSS/Javascript
无规矩不成方圆,一起来看看大神的经验。前端指南 - HTML/CSS/Javascript
本页目录3 节

无规矩不成方圆,一起来看看大神的经验。
- 原文来源:GitHub
HTML
语义
HTML5 为我们提供了许多语义元素,旨在精确描述内容。确保你受益于它丰富的词汇。
<!-- bad --><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>
<!-- good --><main> <article> <header> <h1>Blog post</h1> <p>Published: <time datetime=2015-02-21>21st Feb, 2015</time></p> </header> <p>…</p> </article></main>确保您了解正在使用的元素的语义。用错误的方式使用语义元素比保持中立更糟糕。
<!-- bad --><h1> <figure> <img alt=Company src=logo.png> </figure></h1>
<!-- good --><h1> <img alt=Company src=logo.png></h1>简洁
保持代码简洁。忘记你旧的 XHTML 习惯。
<!-- bad --><!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>
<!-- good --><!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 专家来改进您的网站,您可以立即开始修复能带来巨大变化的小事情,例如:
- 学习正确使用属性
alt - 确保您的链接和按钮被标记为此类(无暴行)
<div class=button> - 不完全依靠颜色来传达信息
- 明确标记表单控件
<!-- bad --><h1><img alt=Logo src=logo.png></h1>
<!-- good --><h1><img alt=Company src=logo.png></h1>语言和字符编码
虽然定义语言是可选的,但建议始终在根元素上声明它。
HTML 标准要求页面使用 UTF-8 字符编码。必须声明它,虽然可以在内容类型 HTTP 标头中声明它,但建议始终在文档级别声明它。
<!-- bad --><!doctype html><title>Hello, world.</title>
<!-- good --><!doctype html><html lang=en> <meta charset=utf-8> <title>Hello, world.</title></html>性能
除非在内容之前加载脚本有正当理由,否则不要阻止页面的呈现。如果样式表很重,请隔离最初绝对必需的样式,并推迟在单独的样式表中加载辅助声明。两个 HTTP 请求明显慢于一个请求,但速度感知是最重要的因素。
<!-- bad --><!doctype html><meta charset=utf-8><script src=analytics.js></script><title>Hello, world.</title><p>...</p>
<!-- good --><!doctype html><meta charset=utf-8><title>Hello, world.</title><p>...</p><script src=analytics.js></script>CSS
分号
虽然分号在技术上是 CSS 中的分隔符,但始终将其视为终止符。
/* bad */div { color: red}
/* good */div { color: red;}箱模型
理想情况下,整个文档的框模型应相同。全局是好的,但如果可以避免,则不要更改特定元素上的默认框模型。* { box-sizing: border-box; }
/* bad */div { width: 100%; padding: 10px; box-sizing: border-box;}
/* good */div { padding: 10px;}Flow
如果可以避免元素的默认行为,请不要更改它。尽可能多地将元素保留在自然文档流中。例如,删除图像下方的空白不应使您更改其默认显示:
/* bad */img { display: block;}
/* good */img { vertical-align: middle;}同样,如果可以避免元素,请不要从流中取下元素。
/* bad */div { width: 100px; position: absolute; right: 0;}
/* good */div { width: 100px; margin-left: auto;}定位
有许多方法可以定位 CSS 中的元素。青睐现代布局规范(如 Flexbox 和 Grid),并避免从普通文档流中删除元素,例如使用 。position: absolute
选择
最小化与 DOM 紧密耦合的选择器。考虑在选择器超过 3 个结构伪类、后代或同级组合器时,将类添加到要匹配的元素。
/* bad */div:first-of-type :last-child > p ~ *
/* good */div:first-of-type .info避免在不需要时使选择器过载。
/* bad */img[src$=svg], ul > li:first-child { opacity: 0;}
/* good */[src$=svg], ul > :first-child { opacity: 0;}特 异性
不要使值和选择器难以覆盖。尽量减少 的 和 的 使用。id!important
/* bad */.bar { color: green !important;}.foo { color: red;}
/* good */.foo.bar { color: green;}.foo { color: red;}重写
重写样式使选择器和调试更加困难。尽可能避免。
/* bad */li { visibility: hidden;}li:first-child { visibility: visible;}
/* good */li + li { visibility: hidden;}继承
不要复制可以继承的样式声明。
/* bad */div h1, div p { text-shadow: 0 1px 0 #fff;}
/* good */div { text-shadow: 0 1px 0 #fff;}简洁
保持代码简洁。使用速记属性,避免在不需要时使用多个属性。
/* bad */div { transition: all 1s; top: 50%; margin-top: -10px; padding-top: 5px; padding-right: 10px; padding-bottom: 20px; padding-left: 10px;}
/* good */div { transition: 1s; top: calc(50% - 10px); padding: 5px 10px 20px;}语言
比起数学,更喜欢英语。
/* bad */:nth-child(2n + 1) { transform: rotate(360deg);}
/* good */:nth-child(odd) { transform: rotate(1turn);}供应商前缀
积极终止过时的供应商前缀。如果需要使用它们,请在标准属性之前插入它们。
/* bad */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;}
/* good */div { -webkit-transform: scale(2); transform: scale(2); transition: 1s;}动画
有利于转换而不是动画。避免对 和 以外的其他属性进行动画处理。opacitytransform
/* bad */div:hover { animation: move 1s forwards;}@keyframes move { 100% { margin-left: 100px; }}
/* good */div:hover { transition: 1s; transform: translateX(100px);}单位
可以时使用无单位值。如果您使用相对单位,则有利。首选秒数而不是毫秒。rem
/* bad */div { margin: 0px; font-size: .9em; line-height: 22px; transition: 500ms;}
/* good */div { margin: 0; font-size: .9rem; line-height: 1.5; transition: .5s;}颜色
如果需要透明度,请使用 。否则,始终使用十六进制格式。rgba
/* bad */div { color: hsl(103, 54%, 43%);}
/* good */div { color: #5a3;}绘图
当资源易于使用 CSS 进行复制时,请避免 HTTP 请求。
/* bad */div::before { content: url(white-circle.svg);}
/* good */div::before { content: ""; display: block; width: 20px; height: 20px; border-radius: 50%; background: #fff;}黑客
不要使用它们。
/* bad */div { // position: relative; transform: translateZ(0);}
/* good */div { /* position: relative; */ will-change: transform;}JavaScript
性能
比起性能,更有利于可读性、正确性和表达性。JavaScript 基本上永远不会成为您的性能瓶颈。而是优化图像压缩、网络访问和 DOM 回流等内容。如果您只记住本文档中的一个准则,请选择此准则。
// bad (albeit way faster)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);}
// goodconst arr = [1, 2, 3, 4];const isEven = n => n % 2 == 0;const square = n => n * n;
const result = arr.filter(isEven).map(square);无 国籍
尽量保持功能纯净。理想情况下,所有函数都不应产生副作用,不使用外部数据并返回新对象,而不是突变现有对象。
// badconst merge = (target, ...sources) => Object.assign(target, ...sources);merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" }
// goodconst merge = (...sources) => Object.assign({}, ...sources);merge({ foo: "foo" }, { bar: "bar" }); // => { foo: "foo", bar: "bar" }当地人
尽可能依赖本机方法。
// badconst toArray = obj => [].slice.call(obj);
// goodconst toArray = (() => Array.from ? Array.from : obj => [].slice.call(obj))();强制
在有意义的时候接受隐含的胁迫。否则避免。不要货物崇拜。
// badif (x === undefined || x === null) { ... }
// goodif (x == undefined) { ... }循环
不要使用循环,因为它们会强制您使用可变对象。依赖于方法。array.prototype
// badconst sum = arr => { var sum = 0; var i = -1; for (;arr[++i];) { sum += arr[i]; } return sum;};
sum([1, 2, 3]); // => 6
// goodconst sum = arr => arr.reduce((x, y) => x + y);
sum([1, 2, 3]); // => 6如果你不能,或者使用方法可以说是滥用,使用递归。array.prototype
// badconst createDivs = howMany => { while (howMany--) { document.body.insertAdjacentHTML("beforeend", "<div></div>"); }};createDivs(5);
// badconst createDivs = howMany => [...Array(howMany)].forEach(() => document.body.insertAdjacentHTML("beforeend", "<div></div>") );createDivs(5);
// goodconst createDivs = howMany => { if (!howMany) return; document.body.insertAdjacentHTML("beforeend", "<div></div>"); return createDivs(howMany - 1);};createDivs(5);下面是一个通用循环函数,使递归更易于使用。
参数
忘记对象。其余参数始终是一个更好的选项,因为:arguments
- 它的名字,所以它给你一个更好的想法,函数期待的参数
- 它是一个真正的数组,这使得它更容易使用。
// badconst sortNumbers = () => Array.prototype.slice.call(arguments).sort();
// goodconst sortNumbers = (...numbers) => numbers.sort();应用
忘记 。改用点差运算符。apply()
const greet = (first, last) => `Hi ${first} ${last}`;const person = ["John", "Doe"];
// badgreet.apply(null, person);
// goodgreet(...person);绑定
当有一个更惯用的方法时,不要这样做。bind()
// bad["foo", "bar"].forEach(func.bind(this));
// good["foo", "bar"].forEach(func, this);// badconst person = { first: "John", last: "Doe", greet() { const full = function() { return `${this.first} ${this.last}`; }.bind(this); return `Hello ${full()}`; }}
// goodconst person = { first: "John", last: "Doe", greet() { const full = () => `${this.first} ${this.last}`; return `Hello ${full()}`; }}高阶函数
不必嵌套函数。
// bad[1, 2, 3].map(num => String(num));
// good[1, 2, 3].map(String);组成
避免多次嵌套函数调用。改用合成。
const plus1 = a => a + 1;const mult2 = a => a * 2;
// badmult2(plus1(5)); // => 12
// goodconst pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val);const addThenMult = pipeline(plus1, mult2);addThenMult(5); // => 12缓存
缓存功能测试、大型数据结构和任何昂贵的操作。
// badconst contains = (arr, value) => Array.prototype.includes ? arr.includes(value) : arr.some(el => el === value);contains(["foo", "bar"], "baz"); // => false
// goodconst contains = (() => Array.prototype.includes ? (arr, value) => arr.includes(value) : (arr, value) => arr.some(el => el === value))();contains(["foo", "bar"], "baz"); // => false变量
一遍又一遍地偏袒。constletletvar
// badvar me = new Map();me.set("name", "Ben").set("country", "Belgium");
// goodconst me = new Map();me.set("name", "Ben").set("country", "Belgium");条件
如果(否则)和切换语句,则支持 IIFE 并返回语句。
// badvar grade;if (result < 50) grade = "bad";else if (result < 90) grade = "good";else grade = "excellent";
// goodconst grade = (() => { if (result < 50) return "bad"; if (result < 90) return "good"; return "excellent";})();对象迭代
尽可能避免。for...in
const shared = { foo: "foo" };const obj = Object.create(shared, { bar: { value: "bar", enumerable: true }});
// badfor (var prop in obj) { if (obj.hasOwnProperty(prop)) console.log(prop);}
// goodObject.keys(obj).forEach(prop => console.log(prop));对象作为地图
虽然对象具有合法的用例,但地图通常是更好、更强大的选择。当有疑问时,请使用 。Map
// badconst me = { name: "Ben", age: 30};var meSize = Object.keys(me).length;meSize; // => 2me.country = "Belgium";meSize++;meSize; // => 3
// goodconst me = new Map();me.set("name", "Ben");me.set("age", 30);me.size; // => 2me.set("country", "Belgium");me.size; // => 3咖喱
对于许多开发人员来说,咖喱是一种强大但外国的模式。不要滥用它,因为它适当的用例是相当不寻常的。
// badconst sum = a => b => a + b;sum(5)(3); // => 8
// goodconst sum = (a, b) => a + b;sum(5, 3); // => 8可读性
不要使用看似聪明的技巧来混淆代码的意图。
// badfoo || doSomething();
// goodif (!foo) doSomething();// badvoid function() { /* IIFE */ }();
// good(function() { /* IIFE */ }());// badconst n = ~~3.14;
// goodconst n = Math.floor(3.14);代码重用
不要害怕创建大量小型、高度可组合和可重用的功能。
// badarr[arr.length - 1];
// goodconst first = arr => arr[0];const last = arr => first(arr.slice(-1));last(arr);// badconst product = (a, b) => a * b;const triple = n => n * 3;
// goodconst product = (a, b) => a * b;const triple = product.bind(null, 3);依赖
最小化依赖项。第三方是你不知道的代码。不要只加载整个库,只需几种方法即可轻松复制:
// badvar _ = require("underscore");_.compact(["foo", 0]));_.unique(["foo", "foo"]);_.union(["foo"], ["bar"], ["foo"]);
// goodconst compact = arr => arr.filter(el => el);const unique = arr => [...new Set(arr)];const union = (...arr) => unique([].concat(...arr));
compact(["foo", 0]);unique(["foo", "foo"]);union(["foo"], ["bar"], ["foo"]);