前端开发规范
date
May 6, 2022
slug
frontend-rules
status
Published
tags
Javascript
HTML&CSS
Reactjs
Vue3.x
Vue2.x
Vuejs
HTTP
summary
参考前端开发规范手册,总结出适用于业务的开发规范🥱
type
Post
该规范由《前端开发规范手册》总结、提炼而来,已投入使用。
基本原则
- 结构、样式、行为分离
尽量确保文档和模板只包含
html
结构,样式都放到样式表里,行为都放到脚本里。- 缩进
统一两个空格缩进(总之缩进统一即可),不要使用
Tab
或者 Tab
、空格混搭。- 文件编码
- 在 HTML中指定编码
<meta charset="utf-8">
- 无需使用
@charset
指定样式表的编码,它默认为 UTF-8
使用不带
BOM
的 UTF-8 编码。- 一律使用小写字母
<!-- Recommended -->
<img src="google.png" alt="Google">
<!-- Not recommended -->
<A HREF="/">Home</A>
/* Recommended */
color: #e5e5e5;
/* Not recommended */
color: #E5E5E5;
- 省略外链资源 URL 协议部分
省略外链资源(图片及其它媒体资源)URL 中的
http
/ https
协议,使 URL 成为相对地址,避免 Mixed Content 问题,减小文件字节数。其他协议如:ftp
不省略。<!-- Recommended -->
<script src="//www.google.com/js/gweb/analytics/autotrack.js"></script>
<!-- Not recommended -->
<script src="http://www.google.com/js/gweb/analytics/autotrack.js"></script>
/* Recommended */
.example {
background: url(//www.google.com/images/example);
}
/* Not recommended */
.example {
background: url(http://www.google.com/images/example);
}
- 统一注释
- HTML 注释
- CSS 注释
- JavaScript 注释
- 单行注释
- 多行注释
- 函数/方法注释
- 文件注释
通过配置编辑器,可以提供快捷键来输出一致认可的注释模式。
<!-- 文章列表列表模块 -->
<div class="article-list">
<span>123<span>
</div>
<!--
@name: Tav-UI DropDown Template
@description: template
@author: i7eo([email protected])
-->
组件块和子组件块以及声明块之间使用一空行分隔,子组件块之间三空行分隔
/* ==========================================================================
组件块
============================================================================ */
/* 子组件块
============================================================================ */
.selector {
padding: 15px;
margin-bottom: 15px;
}
/* 子组件块
============================================================================ */
.selector-secondary {
display: block; /* 注释*/
}
.selector-three {
display: span;
}
必须独占一行。
//
后跟一个空格,缩进与下一行被注释说明的代码一致。避免使用
/* … */
这样的多行注释。有多行注释内容时,使用多个单行注释。1. 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。
2. 参数和返回值注释必须包含类型信息和说明
3. 当函数是内部函数,外部不可访问时,可以使用 @inner 标识;
/**
* 函数描述 *
* @param {string} p1 参数1的说明
* @param {string} p2 参数2的说明,比较长
* 那就换行了.
* @param {number=} p3 参数3的说明(可选)
* @return {Object} 返回值描述 */
function foo(p1, p2, p3) {
var p3 = p3 || 10;
return {
p1: p1,
p2: p2,
p3: p3
};
}
/**
* @fileoverview Description of file, its uses and information
* about its dependencies.
* @author [email protected] (Firstname Lastname)
* Copyright 2015 Meizu Inc. All Rights Reserved.
*/
- 代码验证
- 使用 W3C HTML Validator 来验证你的HTML代码有效性;
- 使用 W3C CSS Validator 来验证你的CSS代码有效性;
代码验证不是最终目的,真的目的在于让开发者在经过多次的这种验证过程后,能够深刻理解到怎样的语法或写法是非标准和不推荐的,即使在某些场景下被迫要使用非标准写法,也可以做到心中有数。
HTML
尽量遵循 HTML 标准和语义,但是不要以牺牲实用性为代价。任何时候都要尽量使用最少的标签并保持最小的复杂度。
通用约定
- 标签
- 自闭合(self-closing)标签,需闭合 ( 例如:
img
input
br
hr
等 )。必须闭合的原因是在 vue/react 中也推荐闭合,保持一致性。 - 可选的闭合标签(closing tag),需闭合 ( 例如:
</li>
或</body>
) - 尽量减少标签数量,不要无意义的嵌套元素,比如:
div
<img src="images/google.png" alt="Google" />
<input type="text" name="title" />
<ul>
<li>Style</li>
<li>Guide</li>
</ul>
<!-- Not recommended -->
<span class="avatar">
<img src="..." />
</span>
<!-- Recommended -->
<img class="avatar" src="..." />
- Class 与 ID
- class 应以功能或内容命名,不以表现形式命名
- class 与 id 单词字母小写,多个单词组成时,采用中划线
-
分隔 - 使用唯一的 id 作为 Javascript identifier, 同时避免创建无样式信息的 class
- 属性顺序
- id
- class
- name
- data-xxx
- src, for, type, href
- title, alt
- aria-xxx, role
HTML 属性应该按照特定的顺序出现以保证易读性,和 vue/react 推荐的 preitter 保持一致。
<a id="..." class="..." data-modal="toggle" href="###"></a>
<input class="form-control" type="text" />
<img src="..." alt="..." />
- 引号
这个没有特别限制,根据习惯配置即可。
- 嵌套
<li>
用于<ul>
或<ol>
下<dd>
,<dt>
用于<dl>
下<thead>
,<tbody>
,<tfoot>
,<tr>
,<td>
用于<table>
下- inline-Level 元素,仅可以包含文本或其它 inline-Level 元素
<a>
里不可以嵌套交互式元素<a>
、<button>
、<select>
等<p>
里不可以嵌套块级元素<div>
、<h1>~<h6>
、<p>
、<ul>/<ol>/<li>
、<dl>/<dt>/<dd>
、<form>
等
a
不允许嵌套 div
这种约束属于语义嵌套约束,与之区别的约束还有严格嵌套约束,比如 a
不允许嵌套 a
。严格嵌套约束在所有的浏览器下都不被允许;而语义嵌套约束,浏览器大多会容错处理,生成的文档树可能不太一样。
语义嵌套约束:
严格嵌套约束:
更多详情,参考WEB标准系列-HTML元素嵌套
需要说明的是,在 vue/react 中典型的嵌套组件
Table
的 dom 一般包含三个 <table>
,这里我的建议是只要保证浏览器正常渲染即可,B端项目语义嵌套要遵守,严格嵌套可不遵守;C端项目如果要考虑 SEO 不论是语义嵌套还是严格嵌套约束都必须遵守。- 布尔值属性
HTML5 规范中
disabled
、 checked
、 selected
等属性不用设置值。默认为 true
,可对比 vue/react 中 布尔类型的 prop。<input type="text" disabled>
<input type="checkbox" value="1" checked>
<select>
<option value="1" selected>1</option>
</select>
语义化
没有
CSS
的 HTML
是一个语义系统而不是 UI 系统。通常情况下,每个标签都是有语义的,所谓语义就是你的衣服分为外套, 裤子,裙子,内裤等,各自有对应的功能和含义。此外语义化的 HTML
结构,有助于机器(搜索引擎)理解,另一方面多人协作时,能迅速了解开发者意图。- 常见的语义化标签
- 示例
- 书的名称:
<h1>
- 书的每个章节标题:
<h2>
- 章节内的文章标题:
<h3>
- 小标题/副标题:
<h4>
<h5>
<h6>
- 章节的段落:
<p>
将你构建的页面当作一本书,将标签的语义对应的其功能和含义:
更多语义化的内容,参考 sofish 写的文章 这样去写你的 HTML
- 注意
在浏览器 SEO 规则中一个页面只允许出现一个
<h1>
HEAD
- 文档类型
为每个 HTML 页面的第一行添加标准模式(standard mode)的声明, 这样能够确保在每个浏览器中拥有一致的 表现
<!DOCTYPE html>
- 语言属性
为什么使用 lang=”zh-cmn-Hans” 而不是我们通常写的 lang=”zh-CN” 呢? 参考:
<!-- 中文 -->
<html lang="zh-Hans">
<!-- 简体中文 -->
<html lang="zh-cmn-Hans">
<!-- 繁体中文 -->
<html lang="zh-cmn-Hant">
<!-- English -->
<html lang="en">
- 字符编码
- 以无 BOM 的 utf-8 编码作为文件格式
- 指定字符编码的 meta 必须是 head 的第一个直接子元素
参考:
<html>
<head>
<meta charset="utf-8">
......
</head>
<body>
......
</body>
</html>
- IE 兼容模式
优先使用最新版本的IE 和 Chrome 内核
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
- SEO 优化
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- SEO -->
<title>Style Guide</title>
<meta name="keywords" content="your keywords">
<meta name="description" content="your description">
<meta name="author" content="author,email address">
</head>
- Viewport
viewport
: 一般指的是浏览器窗口内容区的大小,不包含工具条、选项卡等内容width
: 浏览器宽度,输出设备中的页面可见区域宽度device-width
: 设备分辨率宽度,输出设备的屏幕可见宽度initial-scale
: 初始缩放比例maximum-scale
: 最大缩放比例
为移动端设备优化,设置可见区域的宽度和初始缩放比例:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- IOS 图标
- apple-touch-icon 图片自动处理成圆角和高光等效果
- apple-touch-icon-precomposed 禁止系统自动添加效果,直接显示设计原图
<!-- iPhone 和 iTouch,默认 57x57 像素,必须有 -->
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-57x57- precomposed.png">
<!-- iPad,72x72 像素,可以没有,但推荐有 -->
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-72x72- precomposed.png" sizes="72x72">
<!-- Retina iPhone 和 Retina iTouch,114x114 像素,可以没有,但推荐有 -->
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-114x114- precomposed.png" sizes="114x114">
<!-- Retina iPad,144x144 像素,可以没有,但推荐有 -->
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-144x144- precomposed.png" sizes="144x144">
- favicon
- 在 Web Server 根目录放置 favicon.ico 文件
- 使用 link 指定 favicon
在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 favicon.ico 。为了保证 favicon 可访问,避免404,必须遵循以下两种方法之一:
<link rel="shortcut icon" href="path/to/favicon.ico">
- HEAD 模板
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>Style Guide</title>
<meta name="description" content="不超过150个字符">
<meta name="keywords" content="">
<meta name="author" content="name, [email protected]">
<!-- 为移动设备添加 viewport -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- iOS 图标 -->
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-57x57-
precomposed.png">
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
<link rel="shortcut icon" href="path/to/favicon.ico">
</head>
</html>
CSS
通用约定
这套约定是基于 RSCSS 规范而来,如果想了解 RSCSS 与 BEM 的区别,请参考:https://segmentfault.com/a/1190000039772466
- 代码组织
- 以组件为单位组织代码段
- 制定一致的注释规范
组件块和子组件块
以及声明块
之间使用一空行分隔,子组件块
之间三空行分隔- 如果使用了多个 CSS 文件,将其按照组件而非页面的形式分拆,因为页面会被重组,而组件只会被移动
良好的注释是非常重要的。请留出时间来描述组件(component)的工作方式、局限性和构建它们的方法。不要让你的团队其它成员 来猜测一段不通用或不明显的代码的目的。
提示:通过配置编辑器,可以提供快捷键来输出一致认可的注释模式。
/* ==========================================================================
组件块
============================================================================ */
/* 子组件块
============================================================================ */
.selector {
padding: 15px;
margin-bottom: 15px;
}
/* 子组件块
============================================================================ */
.selector-secondary {
display: block; /* 注释*/
}
.selector-three {
display: span;
}
- Class 和 ID
- 使用语义化、通用的命名方式
- 使用连字符 - 作为 ID、Class 名称界定符,不要驼峰命名法和下划线
- 避免选择器嵌套层级过多,尽量少于 3 级(chrome lighthouse 规范)
- 避免选择器和 Class、ID 叠加使用
出于性能考量,在没有必要的情况下避免元素选择器叠加 Class、ID 使用。
/* Not recommended */
.red {}
.box_green {}
.page .header .login #username input {}
ul#example {}
/* Recommended */
#nav {}
.box-video {}
#username input {}
#example {}
组件使用类名,方便样式覆盖。业务多用 ID。
- 声明块格式
- 选择器分组时,保持独立的选择器占用一行
- 声明块的左括号
{
前添加一个空格 - 声明块的右括号
}
应单独成行 - 声明语句中的
:
后应添加一个空格 - 声明语句应以分号
;
结尾 - 一般以逗号分隔的属性值,每个逗号后应添加一个空格
rgb()
、rgba()
、hsl()
、hsla()
或rect()
括号内的值,逗号分隔,但逗号后不添加一个空格- 对于属性值或颜色参数,省略小于 1 的小数前面的 0 (例如,
.5
代替0.5
;-.5px
代替0.5px
)十六进制值应该全部小写和尽量简写,例如,#fff
代替#ffffff
- 避免为 0 值指定单位,例如,用
margin: 0
代替margin: 0px
/* Not recommended */
.selector, .selector-secondary, .selector[type=text] {
padding:15px;
margin:0px 0px 15px;
background-color:rgba(0, 0, 0, 0.5);
box-shadow:0px 1px 2px #CCC,inset 0 1px 0 #FFFFFF
}
/* Recommended */
.selector,
.selector-secondary,
.selector[type="text"] {
padding: 15px;
margin-bottom: 15px;
background-color: rgba(0,0,0,.5);
box-shadow: 0 1px 2px #ccc, inset 0 1px 0 #fff;
}
- 声明顺序
- Positioning
- Box model
- Typographic
- Visual
相关属性应为一组,推荐的样式编写顺序
由于定位(positioning)可以从正常的文档流中移除元素,并且还能覆盖盒模型(box model)相关的样式,因 此排在首位。盒模型决定了组件的尺寸和位置,因此排在第二位。
其他属性只是影响组件的内部(inside)或者是不影响前两组属性,因此排在后面。
.declaration-order {
/* Positioning */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;
/* Box model */
display: block;
box-sizing: border-box;
width: 100px;
height: 100px;
padding: 10px;
border: 1px solid #e5e5e5;
border-radius: 3px;
margin: 10px;
float: right;
overflow: hidden;
/* Typographic */
font: normal 13px "Helvetica Neue", sans-serif;
line-height: 1.5;
text-align: center;
/* Visual */
background-color: #f5f5f5;
color: #fff;
opacity: .8;
/* Other */
cursor: pointer;
}
- 引号使用
根据项目配置来即可。
- 媒体查询(Media query)的位置
将媒体查询放在尽可能相关规则的附近。不要将他们打包放在一个单一样式文件中或者放在文档底部。如果你把他们分开了,将来只会被大家遗忘。
.element { ... }
.element-avatar { ... }
.element-selected { ... }
@media (max-width: 768px) {
.element { ...}
.element-avatar { ... }
.element-selected { ... }
}
- 不要使用
@import
- 使用多个
<link>
- 通过 Sass 或 Less 类似的 CSS 预处理器将多个 CSS 文件编译为一个文件
- 其他 CSS 文件合并工具
与
<link>
相比, @import
要慢很多,不光增加额外的请求数,还会导致不可预料的问题。替代办法:
参考:
- 链接的样式顺序
a:link -> a:visited -> a:hover -> a:active
- 无需添加浏览器厂商前缀
使用 Autoprefixer 自动添加浏览器厂商前缀,编写 CSS 时不需要添加浏览器前缀,直接使用标准的 CSS 编写。
模块组织
任何超过 1000 行的 CSS 代码,你都曾经历过这样的体验:
- 这个 class 到底是什么意思呢?
- 这个 class 在哪里被使用呢?
- 如果我创建一个
xxx
class,会造成冲突吗?
Reasonable System for CSS Stylesheet Structure 的目标就是解决以上问题,它不是一个框架,而是通过规范, 让你构建更健壮和可维护的 CSS 代码。
- Components(组件)
从
Components
的角度思考,将网站的模块都作为一个独立的 Components
- Naming components(组件命名)
- 点赞按钮 ( .like-button )
- 搜索框 ( .search-form )
- 文章卡片 ( .article-card )
Components
最少以两个单词命名,通过 -
分离,例如:- Elements(元素)
Elements
是 Components
中的元素。Elements
的类名应尽可能仅有一个单词。.search-form {
> .field { /* ... */ }
> .action { /* ... */ }
}
对于倘若需要两个或以上单词表达的
Elements
类名,不应使用中划线和下划线连接,应直接连接。.profile-box {
> .firstname { /* ... */ }
> .lastname { /* ... */ }
> .avatar { /* ... */ }
}
任何时候尽可能使用
classnames
。标签选择器在使用上没有问题,但是其性能上稍弱,并且表意不明确。.article-card {
> h3 { /* ✗ avoid */ }
> .name { /* ✓ better */ }
}
- Variants(变体/状态)
- 它可以避免歧义与
Elements
- CSS class 仅能以单词和
_
或-
开头 - 中划线比下划线更容易输出
Components
和 Elements
可能都会拥有 variants
。Variants
的 classname
应带有前缀中划线 -
。.like-button {
&.-wide { /* ... */ }
&.-short { /* ... */ }
&.-disabled { /* ... */ }
}
为什么使用中划线作为变体的前缀?
- Layout(布局)
- 避免定位属性
- Positioning (position, top, left, right, bottom)
- Floats (float, clear)
- Margins (margin)
- Dimensions (width, height)
- 固定尺寸
- 在父元素中设置定位
Components
应该在不同的上下文中都可以复用,所以应避免设置以下属性:头像和 logos 这些元素应该设置固定尺寸(宽度,高度...)
倘若你需要为组件设置定位,应将在组件的上下文(父元素)中进行处理,比如以下例子中,将
widths
和 floats
应用在 list component(.article-list)
而不是component(.article-card)
自身。.article-list {
&{
@include clearfix;
}
> .article-card {
width: 33.3%;
float: left;
}
.article-card {
& { /* ... */ }
> .image { /* ... */ }
> .title { /* ... */ }
> .category { /* ... */ }
}
}
- 避免过分嵌套
当出现多个嵌套的时候容易失去控制,应保持不超过一个嵌套。
/* ✗ Avoid: 3 levels of nesting */
.image-frame {
> .description {
/* ... */
> .icon {
/* ... */
}
}
}
/* ✓ Better: 2 levels */
.image-frame {
> .description { /* ... */ }
> .description > .icon { /* ... */ }
}
- Summary (总结)
- 以 Components 的角度思考,以两个单词命名( .screenshot-image )
- Components 中的 Elements ,以一个单词命名( .blog-post .title )
- Variants ,以中划线 - 作为前缀( .shop-banner.-with-icon )
- Components 可以互相嵌套
善用面向对象的思想,继承可以让事情变得更简单👻
Less/Scss 规范
- 代码组织
@import
- 变量声明
- 样式声明
代码按一下顺序组织:
@import "mixins/size.less";
@default-text-color: #333;
.page {
width: 960px;
margin: 0 auto;
}
@import
语句
@import
语句引用的文需要写在一对引号内,.less 后缀不得省略。引号使用
目内需统一。/* Not recommended */
@import "mixins/size";
@import 'mixins/grid.less';
/* Recommended */
@import "mixins/size.less";
@import "mixins/grid.less";
- 混入(Mixin)
- 在定义
mixin
时,如果mixin
名称不是一个需要使用的className
,必须加上括号,否则即使不被调用也会输出到CSS
中。 - 如果混入的是本身不输出内容的
mixin
,需要在mixin
后添加括号(即使不传参数),以区分这是否是一个className
。
/* Not recommended */
.big-text {
font-size: 2em;
}
h3 {
.big-text;
.clearfix;
}
/* Recommended */
.big-text() {
font-size: 2em;
}
h3 {
.big-text(); /* 1 */
.clearfix(); /* 2 */
}
- 避免嵌套层级过多
- 将嵌套深度限制在2级。对于超过3级的嵌套,给予重新评估。这可以避免出现过于详实的CSS选择器。
- 避免大量的嵌套规则。当可读性受到影响时,将之打断。推荐避免出现多于20行的嵌套规则出现。
- 字符串插值
变量可以用类似ruby和php的方式嵌入到字符串中,像
@{name}
这样的结构: @base-url: "http://assets.fnord.com"
.aa {
background-image: url("@{base-url}/images/bg.png")
}
性能优化
慎重选择高消耗的样式
高消耗属性在绘制前需要浏览器进行大量计算:
- box-shadows
- border-radius
- transforms
- filters
- 避免过分重排
- width
- height
- padding
- margin
- display
- border-width
- position
- top
- left
- right
- bottom
- font-size
- float
- text-align
- overflow-y
- font-weight
- overflow
- font-family
- line-height
- vertical-align
- clear
- white-space
- min-height
当发生重排的时候,浏览器需要重新计算布局位置与大小,常见的重排元素:
- 正确使用 Display 的属性
- display: inline后不应该再使用 width、height、margin、padding 以及 float
- display: inline-block 后不应该再使用 float
- display: block 后不应该再使用 vertical-align
- display: table-* 后不应该再使用 margin 或者 float
Display 属性会影响页面的渲染,请合理使用。
- 不滥用 Float
Float在渲染时计算量比较大,尽量减少使用。
- 动画性能优化
- 帧:在动画过程中,每一幅静止画面即为一“帧”
- 帧率:即每秒钟播放的静止画面的数量,单位是fps(Frame per second)
- 帧时长:即每一幅静止画面的停留时间,单位一般是ms(毫秒)
- 跳帧(掉帧/丢帧):在帧率固定的动画中,某一帧的时长远高于平均帧时长,导致其后续数帧被挤压而丢失的现象。
- 如果使用基于 javaScript 的动画,尽量使用 requestAnimationFrame,避免使用 setTimeout, setInterval
- 使用 translate 取代 absolute 定位就会得到更好的 fps,动画会更顺滑。
动画的实现原理,是利用了人眼的“视觉暂留”现象,在短时间内连续播放数幅静止的画面,使肉眼因视觉残象产生错觉,而误以为画面在“动”。
动画的基本概念:
一般浏览器的渲染刷新频率是 60 fps,所以在网页当中,帧率如果达到 50-60 fps 的动画将会相当流畅,让人感到舒适。
- 多利用硬件能力,如通过 3D 变形开启 GPU 加速
一般在 Chrome 中,3D或透视变换(perspective transform)CSS属性和对 opacity 进行 CSS 动画会创建新的图层,在硬件加速渲染通道的优化下,GPU 完成 3D 变形等操作后,将图层进行复合操作(Compesite Layers),从而避免触发浏览器大面积重绘和重排。
注:3D 变形会消耗更多的内存和功耗。
- 提示
CSS
选择器性能 - 避免使用通用选择器
- 避免使用标签或
class
选择器限制id
选择器 - 避免使用标签限制
class
选择器 - 避免使用多层标签选择器。使用
class
选择器替换,减少css
查找 - 避免使用子选择器
- 使用继承(多类名)
CSS 选择器对性能的影响源于浏览器匹配选择器和文档元素时所消耗的时间,所以优化选择器的原则是应尽量避免使 用消耗更多匹配时间的选择器。而在这之前我们需要了解 CSS 选择器匹配的机制, 如子选择器规则:
#header > a {font-weight:blod;}
我们中的大多数人都是从左到右的阅读习惯,会习惯性的设定浏览器也是从左到右的方式进行匹配规则,推测这条规则的开销并不高。
我们会假设浏览器以这样的方式工作:寻找
id
为 header
的元素,然后将样式规则应用到直系子元素中的 a
元素上。我们知道文档中只有一个 id
为 header
的元素,并且它只有几个 a
元素的子节点,所以这个 CSS
选择器应该相当高效。事实上,却恰恰相反,
CSS
选择器是从右到左进行规则匹配。了解这个机制后,例子中看似高效的选择器在实际中的 匹配开销是很高的,浏览器必须遍历页面中所有的 a
元素并且确定其父元素的 id
是否为 header
。如果把例子的子选择器改为后代选择器则会开销更多,在遍历页面中所有
a
元素后还需向其上级遍历直到根节点。#header a {font-weight:blod;}
/* Not recommended */
.content * {color: red;}
浏览器匹配文档中所有的元素后分别向上逐级匹配
class
为 content
的元素,直到文档的根节点。因此其匹配开 销是非常大的,所以应避免使用关键选择器是通配选择器的情况。/* Not recommended */
button#backButton {...}
/* Recommended */
#newMenuIcon {...}
/* Not recommended */
treecell.indented {...}
/* Recommended */
.treecell-indented {...}
/* Much to recommended */
.hierarchy-deep {...}
/* Not recommended */
treeitem[mailfolder="true"] > treerow > treecell {...}
/* Recommended */
.treecell-mailfolder {...}
/* Not recommended */
treehead treerow treecell {...}
/* Recommended */
treehead > treerow > treecell {...}
/* Much to recommended */
.treecell-header {...}
Javascript
通用约定
- 注释
- As short as possible(如无必要,勿增注释): 尽量提高代码本身的清晰性、可读性。
- As long as necessary(如有必要,尽量详尽): 合理的注释、空行排版等,可以让代码更易阅读、更具美感。
- 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识
- 参数和返回值注释必须包含类型信息和说明
- 当函数是内部函数,外部不可访问时,可以使用 @inner 标识
原则
单行注释
必须独占一行。
//
后跟一个空格,缩进与下一行被注释说明的代码一致。多行注释
避免使用
/*...*/
这样的多行注释。有多行注释内容时,使用多个单行注释函数/方法注释
/**
* 函数描述 *
* @param {string} p1 参数1的说明
* @param {string} p2 参数2的说明,比较长
* 那就换行了.
* @param {number=} p3 参数3的说明(可选)
* @return {Object} 返回值描述 */
function foo(p1, p2, p3) {
var p3 = p3 || 10;
return {
p1: p1,
p2: p2,
p3: p3
};
}
- 命名
变量,使用 Camel 命名法。
var loadingModules = {};
私有属性、变量和方法以下划线
_
开头。var _privateMethod = {};
常量,使用全部字母大写,单词间下划线分隔的命名方式。
var HTML_ENTITY = {};
函数,使用 Camel 命名法;函数的参数,使用 Camel 命名法。
function stringFormat(source) {}
function hear(theBells) {}
类使用 Pascal 命名法,类的 方法 / 属性,使用 Camel 命名法。
function TextNode(value, engine) {
this.value = value;
this.engine = engine;
}
TextNode.prototype.clone = function () {
return this;
};
枚举变量,使用 Pascal 命名法;枚举的属性,使用全部字母大写,单词间下划线分隔的命名方式。
var TargetState = {
READING: 1,
READED: 2,
APPLIED: 3,
READY: 4
}
由多个单词组成的 缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一 致。
function XMLParser() {}
function insertHTML(element, html) {}
var httpRequest = new HTTPRequest();
- 命名语法
类名,使用名词。
function Engine(options) {}
函数名,使用动宾短语。
function getStyle(element) {}
boolean 类型的变量使用 is 或 has 开头。
var isReady = false;
var hasMoreCommands = false;
Promise 对象用动宾短语的进行时表达。
var loadingData = ajax.get('url');
loadingData.then(callback);
- 接口命名规范
- 可读性强,见名晓义
- 尽量写全。不用缩写,除非是下面列表中约定的(变量以表达清楚为目标)
常用词 | 说明 |
options | 表示选项,不要用 config, opts 等 |
active | 表示当前,不要用 current 等 |
index | 表示索引,不要用 idx 等 |
trigger | 触点元素 |
triggerType | 触发类型、方式 |
context | 表示传入的 this 对象(或者 global state) |
object | 推荐写全,不推荐简写为 o, obj 等 |
element | 推荐写全,不推荐简写为 el, elem 等 |
length | 不要写成 len, l |
prev | previous 的缩写 |
next | next 下一个 |
constructor | 不能写成 ctor |
min | minimize 的缩写 |
max | maximize 的缩写 |
DOM | 不要写成 dom, Dom |
btn | button 的缩写 |
link | 超链接 |
title | 主要文本 |
theme | 主题 |
className | 类名 |
classNameSpace | class 命名空间 |
ns | 命名空间(NameSpace) |
- True 和 False 布尔表达式
- null
- undefined
- ‘’ 空字符串
- 0 数字0
- ‘0’ 字符串0
- [] 空数组
- {} 空对象
类型检测优先使用 typeof。对象类型检测使用 instanceof。null 或 undefined 的检测使用 == null。 下面的布尔表达式都返回 false:
但小心下面的, 可都返回 true:
- 不要在 Array 上使用 for-in 循环
for-in
循环只用于 object
/map
/hash
的遍历, 对 Array
用 for-in
循环有时会出错. 因为它并不是从 0
到 length - 1
进行遍历, 而是所有出现在对象及其原型链的键值。// Not recommended
function printArray(arr) {
for (var key in arr) {
print(arr[key]);
}
}
printArray([0,1,2,3]); // This works.
var a = new Array(10);
printArray(a); // This is wrong.
a = document.getElementsByTagName('*');
printArray(a); // This is wrong.
a = [0,1,2,3];
a.buhu = 'wine';
printArray(a); // This is wrong again.
a = new Array;
a[3] = 3;
printArray(a); // This is wrong again.
// Recommended
function printArray(arr) {
var l = arr.length;
for (var i = 0; i < l; i++) {
print(arr[i]);
}
}
- 二元和三元操作符
操作符始终写在前一行, 以免分号的隐式插入产生预想不到的问题。
var x = a ? b : c;
var y = a ?
longButSimpleOperandB : longButSimpleOperandC;
var z = a ?
moreComplicatedB :
moreComplicatedC;
.
操作符也是如此:var x = foo.bar().
doSomething().
doSomethingElse();
- 条件(三元)操作符 (?:)
三元操作符用于替代 if 条件判断语句。
// Not recommended
if (val!=0) {
return foo();
} else {
return bar();
}
// Recommended
return val ? foo() : bar();
性能优化
- 避免不必要的 DOM 操作
浏览器遍历 DOM 元素的代价是昂贵的。最简单优化 DOM 树查询的方案是,当一个元素出现多次时,将它保存在一 个变量中,就避免多次查询 DOM 树了。
// Recommended
var myList = "";
var myListHTML = document.getElementById("myList").innerHTML;
for(vari=0;i<100;i++){
myList += "<span>" + i + "</span>";
}
myListHTML = myList;
// Not recommended
for(vari=0;i<100;i++){
document.getElementById("myList").innerHTML += "<span>" + i + "</span>";
}
- 异步加载第三方内容
当你无法保证嵌入第三方内容比如 Youtube 视频。
(function() {
var script,
scripts = document.getElementsByTagName('script')[0];
function load(url) {
script = document.createElement('script');
script.async = true;
script.src = url;
scripts.parentNode.insertBefore(script, scripts);
}
load('//apis.google.com/js/plusone.js');
load('//platform.twitter.com/widgets.js');
load('//s.widgetsite.com/widget.js');
}());
移动端优化
- click 的 300ms 延迟响应
使用
FastClick
window.addEventListener( "load", function() {
FastClick.attach( document.body );
}, false );
- 快速回弹滚动
如果想要为某个元素拥有 Native 般的滚动效果,可以这样操作:
.element {
overflow: auto; /* auto | scroll */
-webkit-overflow-scrolling: touch;
}
- 设备检测
参考:
- 获取滚动条值
PC 端滚动条的值是通过
document.scrollTop
和 document.scrollLeft
获得,但在 iOS 中并没有滚动条的概念,所以仅能通过 windows.scroll
获取,同时也能兼容 Android 。- 清除输入框内阴影
在 iOS 上,输入框默认有内部阴影,但无法使用 box-shadow 来清除,如果不需要阴影,可以这样操作:
input,
textarea {
border: 0; /* 方法1 */
-webkit-appearance: none; /* 方法2 */
}
- Meta 相关
页面窗口自动调整到设备宽度,并禁止用户缩放页面
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum- 1. scale=1.0, user-scalable=no" />
- 电话号码识别
- 7位数字,形如:1234567
- 带括号及加号的数字,形如:(+86)123456789
- 双连接线的数字,形如:00-00-00111
- 11位数字,形如:13800138000
iOS Safari ( Android 或其他浏览器不会) 会自动识别看起来像电话号码的数字,将其处理为电话号码链接,比如:
<!-- 关闭电话号码识别: -->
<meta name="format-detection" content="telephone=no" />
<!-- 开启电话功能: -->
<a href="tel:123456">123456</a>
<!-- 开启短信功能: -->
<a href="sms:123456">123456</a>
- 邮箱地址的识别
在 Android ( iOS 不会)上,浏览器会自动识别看起来像邮箱地址的字符串,不论有你没有加上邮箱链接,当你 在这个字符串上长按,会弹出发邮件的提示。
<!-- 关闭邮箱地址识别: -->
<meta name="format-detection" content="email=no" />
<!-- 开启邮件发送: -->
<a mailto:>[email protected]">[email protected]</a>
- 指定 iOS 的 safari 顶端状态条的样式
<!-- 可选default、black、black-translucent -->
<meta name="apple-mobile-web-app-status-bar-style" content="black" />