`。它维护的是`moduleId`前缀到路径的映射规则。这个对象中的`key`应该是`moduleId`的前缀,`value`如果是一个相对路径的话,那么相对的是`baseUrl`。当然也可以是绝对路径的话,例如:`/this/is/a/path`,`//www.google.com/this/is/a/path`。
244 |
245 | ```javascript
246 | {
247 | baseUrl: '/fe/code/path',
248 | paths: {
249 | 'ui': 'esui/v1.0/ui',
250 | 'ui/Panel': 'esui/v1.2/ui/Panel',
251 | 'tangram': 'third_party/tangram/v1.0',
252 | 'themes': '//www.baidu.com/css/styles/blue'
253 | }
254 | }
255 | ```
256 |
257 | 在`ID-to-path`的阶段,如果`模块`或者`资源`是以`ui`, `ui/Panel`, `tangram`开头的话,那么就会去配置指定的地方去加载。例如:
258 |
259 | * `ui/Button` => `/fe/code/path/esui/v1.0/ui/Button.js`
260 | * `ui/Panel` => `/fe/code/path/esui/v1.2/ui/Panel.js`
261 | * `js!tangram` => `/fe/code/path/third_party/tangram/v1.0/tangram.js`
262 | * `css!themes/base` => `//www.baidu.com/css/styles/blue/base.css`
263 |
264 | 另外,需要支持为插件指定不同的的`paths`,语法如下:
265 |
266 | ```javascript
267 | {
268 | baseUrl: '/fe/code/path',
269 | paths: {
270 | 'css!': '//www.baidu.com/css/styles/blue',
271 | 'css!foo': 'bar',
272 | 'js!': '//www.google.com/js/gcl',
273 | 'js!foo': 'bar'
274 | }
275 | }
276 | ```
277 |
278 | ## 模块加载器插件
279 |
280 | 该文档不限定使用何种`AMD Loader`,但是一个`AMD Loader`应该支持至少三种插件(css,js,tpl)才能满足我们的业务需求。
281 |
282 | ### 插件语法
283 |
284 | [Plugin Module ID]![resource ID]
285 |
286 | `Plugin Module Id`是插件的`moduleId`,例如`css`,`js`,`tpl`等等。`!`是分割符。
287 |
288 | `resource ID`是`资源Id`,可以是`top-level`或者`relative`。如果`resource ID`是`relative`,那么相对的是当前`模块的Id`,而不是当前`模块Url`。例如:
289 |
290 | ```javascript
291 | // src/Button.js
292 | define( function( require, exports, module ){
293 | require( "css!./css/Button.css" );
294 | require( "css!base.css" );
295 | require( "tpl!./tpl/Button.tpl.html" );
296 | });
297 | ```
298 |
299 | 如果当前模块的路径是`${root}/src/ui/Button.js`,那么该模块依赖的`Button.css`和`Button.tpl.html`的路径就应该分别是`${root}/src/css/ui/Button.css`,`${root}/src/tpl/Button.tpl.html`;该模块依赖的`base.css`的路径应该是`${baseUrl}/base.css`。
300 |
301 | ### css插件
302 |
303 | 参考上面的示例。如果`resource ID`省略后缀名的话,默认是`.css`;如果有后缀名,以具体的后缀名为准。例如:`.less`。
304 |
305 | ### js插件
306 |
307 | 用来加载不符合该文档规范的js文件,例如`jquery`,`tangram`等等。例如:
308 |
309 | ```javascript
310 | // src/js/ui/Button.js
311 | define( function( require, exports, module ) {
312 | require( "js!jquery" );
313 | require( "js!./tangram" );
314 | });
315 | ```
316 |
317 | ### tpl插件
318 |
319 | 如果项目需要前端模板,需要通过tpl插件加载。tpl插件由模板引擎提供方实现。插件的语法应该跟上述`js`,`css`插件的语法保持一致,例如:
320 |
321 | ```javascript
322 | require( "tpl!./foo.tpl.html" );
323 | ```
324 |
325 | ## FAQ
326 |
327 | ### 为什么不能采用define(moduleId, deps, factory)来定义模块?
328 |
329 | `define(moduleId, deps, factory)`这种写法,很容易出现很长的deps,影响代码的风格。
330 |
331 | ```javascript
332 | define(
333 | "module/id",
334 | [
335 | "module/a",
336 | "module/b",
337 | "module/c"
338 | ],
339 | function ( require ) {
340 | // blabla...
341 | }
342 | );
343 | ```
344 |
345 | 构建工具对代码进行处理和编译时,允许将代码编译成这种风格,明确硬依赖。
346 |
347 | ### 相对于模块的Id和相对于模块Url有什么区别?
348 |
349 | 还是看 [erik的解释吧](https://github.com/ecomfe/edp/issues/13#issuecomment-14383810)
350 |
351 |
352 |
353 |
--------------------------------------------------------------------------------
/directory.md:
--------------------------------------------------------------------------------
1 | # 项目目录结构规范
2 |
3 |
4 | ## 简介
5 |
6 | 该文档主要的设计目标是项目开发的目录结构保持一致,使容易理解并方便构建与管理。
7 |
8 | ### 编撰
9 |
10 | 李玉北、erik、黄后锦、王杨、张立理、赵雷、陈新乐、刘恺华。
11 |
12 | 本文档由`商业运营体系前端技术组`审校发布。
13 |
14 | ### 要求
15 |
16 | 在本文档中,使用的关键字会以中文+括号包含的关键字英文表示:必须(MUST)。关键字"MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL"被定义在rfc2119中。
17 |
18 |
19 | ### 规范说明约定
20 |
21 | 以下规范文档中:
22 |
23 | 1. `项目`包含但不限于`业务项目`和`包项目`。
24 | 2. `${root}`表示`项目`的根目录。
25 |
26 |
27 |
28 | ## 资源分类
29 |
30 | `资源`分成两大类:
31 |
32 | 1. `源代码资源`:指开发者编写的源代码,包括`js`、`html`、`css`、`template`等。
33 | 2. `内容资源`:指希望做为内容提供给访问者的资源,包括`图片`、`字体`、`flash`、`pdf`等。
34 |
35 |
36 | ## 目录命名原则
37 |
38 | 1. 简洁。有习惯性缩写的单词 *必须(MUST)* 采用容易理解的缩写。如:源代码目录使用`src`,不使用`source`。下面是更多例子:
39 | 1. `img`: 图片。 *不允许(MUST NOT)* 使用`image`、`images`、`imgs`等。
40 | 2. `js`: javascript脚本。 *不允许(MUST NOT)* 使用`script`、`scripts`等。
41 | 3. `css`: 样式表。 *不允许(MUST NOT)* 使用`style`、`styles`等。
42 | 4. `swf`: flash。 *不允许(MUST NOT)* 使用`flash`等。
43 | 5. `src`: 源文件目录。 *不允许(MUST NOT)* 使用`source`等。
44 | 6. `dep`: 引入的第三方依赖包目录。 *不允许(MUST NOT)* 使用`lib`、`library`、`dependency`等。
45 | 2. *不允许(MUST NOT)* 使用复数形式。如:`imgs`、`docs`是不被允许的。
46 |
47 |
48 |
49 | ## 目录划分
50 |
51 |
52 | ### ${root}目录结构划分
53 |
54 | 在${root}下,目录结构 *必须(MUST)* 按照`职能`进行划分, *不允许(MUST NOT)* 将`资源类型`或`业务逻辑`划分的目录直接置于${root}下。
55 |
56 | 常用的目录有`src`、`doc`、`dep`、`test`等。详细请参考[一级目录详细说明](#level1)
57 |
58 | ${root}/
59 | src/
60 | test/
61 | doc/
62 | dep/
63 | ...
64 |
65 | ### 业务项目目录结构划分
66 |
67 | `业务项目`的${root}目录结构划分遵循[${root}目录结构划分](#root)。
68 |
69 |
70 | #### 项目代号
71 |
72 | 业务项目 *可以(SHOULD)* 为项目起一个代号名称。代号名称 *必须(MUST)* 为一个单词,不宜过长。例:北斗的项目代号为`triones`,哥伦布的项目代号为`clb`,百度锦囊的项目代号为`jn`。项目代号有利于区分不同项目,为未来项目之间的重用留下扩展的后路。
73 |
74 | 在项目开发时,通常会使用如下[加载器配置](module.md#config),将项目代号指向`src`。
75 |
76 | ```javascript
77 | {
78 | baseUrl: '${docroot}',
79 | paths: {
80 | 'triones': 'src'
81 | }
82 | }
83 | ```
84 |
85 |
86 | #### 根据业务逻辑划分src目录结构
87 |
88 | `业务项目`的`src`目录内,绝大多数情况 *应当(SHOULD)* 根据`业务逻辑`划分目录结构。划分出的子目录(比如[例子](#bizdirexample)中的`biz1`)我们称为`业务目录`。
89 |
90 | `src`下 *必须(MUST)* 只包含`业务目录`与`common`目录。`业务公共资源` *必须(MUST)* 命名为`common`。`common`目录做为`业务公共资源`的目录,也视如`业务目录`。
91 |
92 | ${root}/
93 | src/
94 | common/
95 | biz1/
96 | subbiz1/
97 | subbiz2/
98 | biz2/
99 |
100 | 较小规模的`业务项目`(如投放端),`src`目录允许视如`业务目录`,直接按照[业务目录划分原则](#bizdirprinciple)划分目录结构。
101 |
102 | ${root}/
103 | src/
104 | foo.js
105 |
106 |
107 |
108 | #### 业务目录划分原则
109 |
110 | 1. `JS资源` *不允许(MUST NOT)* 按`资源类型`划分目录, *必须(MUST)* 按`业务逻辑`划分目录。`JS资源`应直接置于`业务目录`下。即:`业务目录`下不允许出现`js`目录。
111 | 2. 除`JS资源`外的`源文件资源`,当资源数量较多时,为方便管理, *允许(SHOULD)* 按`资源类型`划分目录。即:`业务目录`下允许出现`css`、`tpl`目录。
112 | 3. `内容资源` *允许(SHOULD)* 按`资源类型`划分目录。即:`业务目录`下允许出现`img`、`swf`、`font`目录。
113 | 4. `业务目录`中,如果文件太多不好管理,需要划分子目录时,也 *必须(MUST)* 继续遵守根据`业务逻辑`划分的原则,划分子业务。如:下面例子中的`subbiz1`。
114 |
115 |
116 | 通常,对于一个`业务目录`, *鼓励(SHOULD)* 将业务相关的`源文件资源`都直接置于`业务目录`下。
117 |
118 | biz1/
119 | img/
120 | add_button.png
121 | add.js
122 | add.tpl.html
123 | add.css
124 |
125 | `业务目录`下`源文件资源`数量较多时,我们第一直觉应该是:是否业务划分不够细?是否应该划分子业务,建立子业务目录?
126 |
127 | biz2/
128 | subbiz1/
129 | list.js
130 | list.tpl.html
131 | list.css
132 | subbiz2/
133 |
134 | 遇到确实是一个业务整体,无法划分子业务时, *允许(MAY)* 将非`JS资源`按`资源类型`划分目录进行管理。
135 |
136 | biz1/
137 | css/
138 | add.css
139 | edit.css
140 | remove.css
141 | img/
142 | add_button.png
143 | tpl/
144 | add.html
145 | edit.html
146 | remove.html
147 | add.js
148 | edit.js
149 | remove.js
150 |
151 |
152 | `源文件资源`和`内容资源`请参考[资源分类](#restype)章节,常用`资源目录`请参考[资源目录](#resdir)章节,常用`业务目录`请参考[业务目录](#bizdir)章节。
153 |
154 |
155 |
156 | #### 业务项目目录划分示例
157 |
158 | ${root}/
159 | src/
160 | common/
161 | img/
162 | sprites.png
163 | logo.png
164 | conf.js
165 | layout.css
166 | biz1/
167 | img/
168 | add_button.png
169 | add.js
170 | add.tpl.html
171 | add.less
172 | biz2/
173 | subbiz1/
174 | list.js
175 | list.tpl.html
176 | list.css
177 | subbiz2/
178 | dep/
179 | er/
180 | src/
181 | test/
182 | esui/
183 | src/
184 | test/
185 | test/
186 | doc/
187 | index.html
188 | main.html
189 | ......
190 |
191 |
192 | ### 包项目目录结构划分
193 |
194 | `包项目`的${root}目录结构划分遵循[${root}目录结构划分](#root)。
195 |
196 |
197 | #### 包项目src目录结构划分
198 |
199 | `包`是实现某个独立功能,有复用价值的代码集。按照通常的理解,一个`包项目`不应该特别复杂。
200 |
201 | 所以,`包`可视如一个不太复杂的`业务`,其`src`下的划分原则与`业务项目`的[业务目录划分原则](#bizdirprinciple)保持一致。
202 |
203 | ${root}/
204 | src/
205 | css/
206 | img/
207 | sprites.png
208 | table.css
209 | button.css
210 | select.css
211 | main.js
212 | Control.js
213 | InputControl.js
214 | Button.js
215 | Table.js
216 | Select.js
217 | test/
218 | doc/
219 | package.json
220 | ...
221 |
222 |
223 |
224 |
225 | ## 常用目录
226 |
227 |
228 |
229 | ### 一级目录
230 |
231 | 直接置于`${root}`下的目录称作`一级目录`。一级目录 *必须(MUST)* 具有某种`职能`属性。
232 |
233 | 除了下面列举的一些常见目录之外,`${root}`下面也可以放置一些跟项目发布相关的文件,例如`build.sh`,`build.xml`,`Makefile`,`Gruntfile`等等.
234 |
235 | #### src
236 |
237 | `src`目录用于存放开发时源文件,发布时 *必须(MUST)* 被删除。
238 |
239 |
240 | #### dep
241 |
242 | `dep`目录用于存放`项目`引入依赖的第三方包。该目录下的内容通过平台工具管理,项目开发人员 *不允许(MUST NOT)* 更改`dep`目录下第三方包的任何内容。
243 |
244 | 当项目需要修改引入的第三方代码时,第三方包应将源码直接置于`${root}/src`目录下,规则见该目录下的规定。
245 |
246 | 更多关于`包`的内容请参考 [包结构规范](package.md)
247 |
248 |
249 | #### tool
250 |
251 | `tool`目录用于存放开发时或构建阶段使用的工具。该目录在发布时 *必须(MUST)* 被删除。
252 |
253 |
254 | #### test
255 |
256 | `test`目录用于存放测试用例以及开发阶段的模拟数据。该目录在发布时 *必须(MUST)* 被删除。
257 |
258 |
259 | #### doc
260 |
261 | `doc`目录用于存放项目文档。项目文档可能是开发者维护的文档,也可能是通过工具生成的文档。
262 |
263 |
264 | #### entry
265 |
266 | `entry`目录用于存放项目的`页面入口文件`,通常是上线后可被直接访问的静态页面。
267 |
268 | `RIA项目`通常会包含较少的`页面入口文件`,常见的是`main.html`,这些文件 *可以(SHOULD)* 直接放在`${root}`目录下。
269 |
270 | ${root}/
271 | src/
272 | common/
273 | conf.js
274 | card/
275 | gold/
276 | message/
277 | index.html
278 | main.html
279 | ......
280 |
281 |
282 | `多页面项目`通常`页面入口文件`较多, *可以(SHOULD)* 统一放在`entry`目录中,按`业务逻辑`命名。
283 |
284 | ${root}/
285 | src/
286 | common/
287 | conf.js
288 | card/
289 | gold/
290 | message/
291 | entry/
292 | card.html
293 | gold.html
294 | message.html
295 | ......
296 |
297 |
298 | 项目在发布的时候,构建工具可以`页面入口文件`为入口进行分析和编译。
299 |
300 |
301 | `RIA项目`经过构建工具编译后,目录结构可能如下:
302 |
303 | output/
304 | asset/
305 | js/
306 | css/
307 | tpl/
308 | img/
309 | index.html
310 | main.html
311 |
312 | `多页面项目`经过构建工具编译后,目录结构可能如下:
313 |
314 | output/
315 | card/
316 | asset/
317 | js/
318 | css/
319 | img/
320 | index.html
321 | gold/
322 | asset/
323 | js/
324 | css/
325 | img/
326 | index.html
327 |
328 | #### asset
329 |
330 | `asset`目录用于存放用于`线上访问`的静态资源。
331 |
332 | 通常构建工具会对`src`目录和`dep`目录下的资源进行分析、合并与压缩等,生成到`asset`目录下。所以该目录尽量避免手工管理。下面是一个构建工具生成后的`asset`目录示例:
333 |
334 | ${root}/
335 | asset/
336 | js/
337 | loader.js
338 | build.js
339 | css/
340 | common.css
341 | img/
342 | tpl/
343 | build.tpl.html
344 | img/
345 | ...
346 |
347 |
348 |
349 |
350 | ### 资源目录
351 |
352 | 按`资源`类型命名的目录称作`资源目录`。`资源目录` *不允许(MUST NOT)* 直接置于${root}下。
353 |
354 |
355 | #### js
356 |
357 | `js`目录可用于存放`js`资源文件(包含可编译成`js`的`coffeescript`等语言)。`js`文件后缀名 *必须(MUST)* 为.js,`coffeescript文件`后缀名 *必须(MUST)* 为.coffee。
358 |
359 | `js`目录内 *必须(MUST)* 存放`js`资源文件,但`js`资源文件不一定(MAY NOT)存放于`js`目录下:
360 |
361 | 1. 对于`src`目录,`js`资源文件 *不允许(MUST NOT)* 存放于`js`目录下。
362 | 2. 对于`asset`目录,`js`资源文件 *可以(SHOULD)* 存放于`js`目录下,视构建行为决定。
363 | 3. 对于其他`一级目录`内,`js`资源文件 *可以(SHOULD)* 不存放于`js`目录下。
364 |
365 | #### css
366 |
367 | `css`目录可用于存放`css资源文件`(包含`less`,`sass`等动态样式表语言)。`css`文件后缀名 *必须(MUST)* 为.css,`less`文件后缀名 *必须(MUST)* 为`.less`。
368 |
369 | `css`目录内 *必须(MUST)* 存放`css`资源文件,但`css`资源文件不一定(MAY NOT)存放于`css`目录下:
370 |
371 | 1. 对于`src`目录,`css`资源文件 *可以(SHOULD)* 存放于`业务目录`下,也 *可以(SHOULD)* 存放于`css`目录下。
372 | 2. 对于`asset`目录,`css`资源文件 *可以(SHOULD)* 存放于`css`目录下,视构建行为决定。
373 | 3. 对于其他`一级目录`内,`css`资源文件 *可以(SHOULD)* 不存放于`css`目录下。
374 |
375 | 关于css引用图片的位置说明,请参考[img](#imgdir)章节。
376 |
377 |
378 |
379 |
380 | #### img
381 |
382 | `img`目录可用于存放`图片资源文件`。包括`页面直接引用`的图片与`css引用`图片。常见的图片资源有`gif/jpg/png/svg/bmp`等。
383 |
384 | 对于`css`引用的图片, *必须(MUST)* 放在`./img`目录下,`.`代表当前`css`资源所在的目录。
385 |
386 | 对于`页面直接引用`的图片:
387 |
388 | 1. 被多页面引用的图片 *应该(SHOULD)* 放在`${root}/src/common/img`目录下。
389 | 2. 单一页面引用的图片 *应该(SHOULD)* 放在`./img`目录下,`.`代表当前页面所在的目录。
390 |
391 |
392 | #### tpl
393 |
394 | `tpl`目录可用于存放`template`资源文件。`template`资源文件后缀名 *可以(SHOULD)* 为`.html`或`.tpl`。
395 |
396 | 通常,对于`RIA`系统,`template`资源文件采用`.html`后缀使其能够被`xhr`加载。
397 |
398 |
399 | #### font
400 |
401 | `font`目录可用于存放字体资源文件。常见的字体资源有`tff/woff/svg`等。
402 |
403 |
404 | #### swf
405 |
406 | `swf`目录可用于存放`flash`资源文件。`flash`资源文件 *不允许(MUST NOT)* 置于`img`目录中。
407 |
408 |
409 |
410 |
411 | ### 业务目录
412 |
413 |
414 |
415 | #### common
416 |
417 | `common`目录为业务公共目录,用于存放业务项目的业务公共文件。所以,根据`业务逻辑`划分目录结构时,业务逻辑命名 *不允许(MUST NOT)* 为`common`。
418 |
419 |
420 | ## FAQ
421 |
422 | ### 为啥biz下面没资源类型目录了?
423 |
424 | 如果在`biz`下继续划分`资源目录`,代码的结构可能就是这样子了:
425 |
426 | ${root}/
427 | src/
428 | biz1/
429 | js/
430 | list.js
431 |
432 | 当我们需要使用`list.js`的时候,必须写如下的代码:`require("../biz1/js/list")`,但是从逻辑上说,更合理的写法应该是`require("../biz1/list")`。因此我们不推荐在`biz`下面对源代码资源划分目录。
433 |
434 |
--------------------------------------------------------------------------------
/react-style-guide.md:
--------------------------------------------------------------------------------
1 | # React规范
2 |
3 | ## 文件组织
4 |
5 | - [强制]同一目录下不得拥有同名的`.js`和`.jsx`文件。
6 |
7 | 在使用模块导入时,倾向于不添加后缀,如果存在同名但不同后缀的文件,构建工具将无法决定哪一个是需要引入的模块。
8 |
9 | - [强制]组件文件使用一致的`.js`或 `.jsx`后缀。
10 |
11 | 所有组件文件的后缀名从`.js`或`.jsx`中任选其一。
12 |
13 | 不应在项目中出现部分组件为`.js`文件,部分为`.jsx`的情况。
14 |
15 | - [强制]每一个文件以`export default`的形式暴露一个组件。
16 |
17 | 允许一个文件中存在多个不同的组件,但仅允许通过`export default`暴露一个组件,其它组件均定义为内部组件。
18 |
19 | - [强制]每个存放组件的目录使用一个`index.js`以命名导出的形式暴露所有组件。
20 |
21 | 同目录内的组件相互引用使用`import Foo from './Foo';`进行。
22 |
23 | 引用其它目录的组件使用`import {Foo} from '../component';`进行。
24 |
25 | 建议使用[VSCode的export-index插件](https://marketplace.visualstudio.com/items?itemName=BrunoLM.export-index)等插件自动生成`index.js`的内容。
26 |
27 | ## 命名规则
28 |
29 | - [强制]组件名为PascalCase。
30 |
31 | 包括函数组件,名称均为PascalCase。
32 |
33 | - [强制]组件名称与文件名称保持相同。
34 |
35 | 同时组件名称应当能体现出组件的功能,以便通过观察文件名即确定使用哪一个组件。
36 |
37 | - [强制]高阶组件使用camelCase命名。
38 |
39 | 高阶组件事实上并非一个组件,而是一个“生成组件类型”的函数,因此遵守JavaScript函数命名的规范,使用camelCase命名。
40 |
41 | - [强制]使用`onXxx`形式作为`props`中用于回调的属性名称。
42 |
43 | 使用统一的命名规则用以区分`props`中回调和非回调部分的属性,在JSX上可以清晰地看到一个组件向上和向下的逻辑交互。
44 |
45 | 对于不用于回调的函数类型的属性,使用动词作为属性名称。
46 |
47 | ```javascript
48 | // onClick作为回调以on开头,renderText非回调函数则使用动词
49 | let Label = ({onClick, renderText}) => {renderText()};
50 | ```
51 |
52 | - [建议]使用`withXxx`或`xxxable`形式的词作为高阶组件的名称。
53 |
54 | 高阶组件是为组件添加行为和功能的函数,因此使用如上形式的词有助于对其功能进行理解。
55 |
56 | - [建议]作为组件方法的事件处理函数以具备业务含义的词作为名称,不使用`onXxx`形式命名。
57 |
58 | ```javascript
59 | // Good
60 | class Form {
61 | @autobind
62 | collectAndSubmitData() {
63 | let data = {
64 | name: this.state.name,
65 | age: this.state.age
66 | };
67 | this.props.onSubmit(data);
68 | }
69 |
70 | @autobind
71 | syncName() {
72 | // ...
73 | }
74 |
75 | @autobind
76 | syncAge() {
77 | // ...
78 | }
79 |
80 | render() {
81 | return (
82 |
83 |
84 |
85 |
86 |
87 | );
88 | }
89 | }
90 | ```
91 |
92 | ## 组件声明
93 |
94 | - [强制]使用ES Class声明组件,禁止使用`React.createClass`。
95 |
96 | [React v15.5.0](https://facebook.github.io/react/blog/2017/04/07/react-v15.5.0.html)已经弃用了`React.createClass`函数。
97 |
98 | ```javascript
99 | // Bad
100 | let Message = React.createClass({
101 | render() {
102 | return {this.state.message};
103 | }
104 | });
105 |
106 | // Good
107 | class Message extends PureComponent {
108 | render() {
109 | return {this.state.message};
110 | }
111 | }
112 | ```
113 |
114 | - [强制]不使用`state`的组件声明为函数组件。
115 |
116 | 函数组件在React中有着特殊的地位,在将来也有可能得到更多的内部优化。
117 |
118 | ```javascript
119 | // Bad
120 | class NextNumber {
121 | render() {
122 | return {this.props.value + 1}
123 | }
124 | }
125 |
126 | // Good
127 | let NextNumber = ({value}) => {value + 1};
128 | ```
129 |
130 | - [强制]所有组件均需声明`propTypes`。
131 |
132 | `propsTypes`在提升组件健壮性的同时,也是一种类似组件的文档的存在,有助于代码的阅读和理解。
133 |
134 | - [强制]对于所有非`isRequired`的属性,在`defaultProps`中声明对应的值。
135 |
136 | 声明初始值有助于对组件初始状态的理解,也可以减少`propTypes`对类型进行校验产生的开销。
137 |
138 | 对于初始没有值的属性,应当声明初始值为`null`而非`undefined`。
139 |
140 | - [强制]如无必要,使用静态属性语法声明`propsTypes`、`contextTypes`、`defaultProps`和`state`。
141 |
142 | 仅当初始`state`需要从`props`计算得到的时候,才将`state`的声明放在构造函数中,其它情况下均使用静态属性声明进行。
143 |
144 | - [强制]依照规定顺序编排组件中的方法和属性。
145 |
146 | 按照以下顺序编排组件中的方法和属性:
147 |
148 | 1. `static displayName`
149 | 2. `static propTypes`
150 | 3. `static contextTypes`
151 | 4. `state defaultProps`
152 | 5. `static state`
153 | 6. 其它静态的属性
154 | 7. 用于事件处理并且以属性的方式(`onClick = e => {...}`)声明的方法
155 | 8. 其它实例属性
156 | 9. `constructor`
157 | 10. `getChildContext`
158 | 11. `componentWillMount`
159 | 12. `componentDidMount`
160 | 13. `shouldComponentUpdate`
161 | 14. `componentWillUpdate`
162 | 15. `componentDidUpdate`
163 | 16. `componentWillUnmount`
164 | 17. 事件处理方法
165 | 18. 其它方法
166 | 19. `render`
167 |
168 | 其中`shouldComponentUpdate`和`render`是一个组件最容易被阅读的函数,因此放在最下方有助于快速定位。
169 |
170 | - [建议]无需显式引入React对象。
171 |
172 | 使用JSX隐式地依赖当前环境下有`React`这一对象,但在源码上并没有显式使用,这种情况下添加`import React from 'react';`会造成一个没有使用的变量存在。
173 |
174 | 使用[babel-plugin-react-require](https://www.npmjs.com/package/babel-plugin-react-require)插件可以很好地解决这一问题,因此无需显式地编写`import React from 'react';`这一语句。
175 |
176 | - [建议]使用箭头函数声明函数组件。
177 |
178 | 箭头函数具备更简洁的语法(无需`function`关键字),且可以在仅有一个语句时省去`return`造成的额外缩进。
179 |
180 | - [建议]高阶组件返回新的组件类型时,添加`displayName`属性。
181 |
182 | 同时在`displayName`上声明高阶组件的存在。
183 |
184 | ```javascript
185 | // Good
186 | let asPureComponent = Component => {
187 | let componentName = Component.displayName || Component.name || 'UnknownComponent';
188 | return class extends PureComponent {
189 | static displayName = `asPure(${componentName})`
190 |
191 | render() {
192 | return ;
193 | }
194 | };
195 | };
196 | ```
197 |
198 | ## 组件实现
199 |
200 | - [强制]除顶层或路由级组件以外,所有组件均在概念上实现为纯组件(Pure Component)。
201 |
202 | 本条规则并非要求组件继承自`PureComponent`,“概念上的纯组件”的意思为一个组件在`props`和`state`没有变化(shallowEqual)的情况下,渲染的结果应保持一致,即`shouldComponentUpdate`应当返回`false`。
203 |
204 | 一个典型的非纯组件是使用了随机数或日期等函数:
205 |
206 | ```javascript
207 | let RandomNumber = () => {Math.random()};
208 | let Clock = () => {Date.time()};
209 | ```
210 |
211 | 非纯组件具备向上的“传染性”,即一个包含非纯组件的组件也必须是非纯组件,依次沿组件树结构向上。由于非纯组件无法通过`shouldComponentUpdate`优化渲染性能且具备传染性,因此要避免在非顶层或路由组件中使用。
212 |
213 | 如果需要在组件树的某个节点使用随机数、日期等非纯的数据,应当由顶层组件生成这个值并通过`props`传递下来。对于使用Redux等应用状态管理的系统,可以在应用状态中存放相关值(如Redux使用Action Creator生成这些值并通过Action和reducer更新到store中)。
214 |
215 | - [强制]禁止为继承自`PureComponent`的组件编写`shouldComponentUpdate`实现。
216 |
217 | 参考[React的相关Issue](https://github.com/facebook/react/issues/9239),在React的实现中,`PureComponent`并不直接实现`shouldComponentUpdate`,而是添加一个`isReactPureComponent`的标记,由`CompositeComponent`通过识别这个标记实现相关的逻辑。因此在`PureComponent`上自定义`shouldComponentUpdate`并无法享受`super.shouldComponentUpdate`的逻辑复用,也会使得这个继承关系失去意义。
218 |
219 | - [强制]为非继承自`PureComponent`的纯组件实现`shouldComponentUpdate`方法。
220 |
221 | `shouldComponentUpdate`方法在React的性能中扮演着至关重要的角色,纯组件必定能通过`props`和`state`的变化来决定是否进行渲染,因此如果组件为纯组件且不继承`shouldComponentUpdate`,则应当有自己的`shouldComponentUpdate`实现来减少不必要的渲染。
222 |
223 | - [建议]为函数组件添加`PureComponent`能力。
224 |
225 | 函数组件并非一定是纯组件,因此其`shouldComponentUpdate`的实现为`return true;`,这可能导致额外的无意义渲染,因此推荐使用高阶组件为其添加`shouldComponentUpdate`的相关逻辑。
226 |
227 | 推荐使用[react-pure-stateless-component](https://www.npmjs.com/package/react-pure-stateless-component)库实现这一功能。
228 |
229 | - [建议]使用`@autobind`进行事件处理方法与`this`的绑定。
230 |
231 | 由于`PureComponent`使用[`shallowEqual`](https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js)进行是否渲染的判断,如果在JSX中使用`bind`或箭头函数绑定`this`会造成子组件每次获取的函数都是一个新的引用,这破坏了`shouldComponentUpdate`的逻辑,引入了无意义的重复渲染,因此需要在`render`调用之前就将事件处理方法与`this`绑定,在每次`render`调用中获取同样的引用。
232 |
233 | 当前比较流行的事前绑定`this`的方法有2种,其一使用类属性的语法:
234 |
235 | ```javascript
236 | class Foo {
237 | onClick = e => {
238 | // ...
239 | }
240 | };
241 | ```
242 |
243 | 其二使用`@autobind`的装饰器:
244 |
245 | ```javascript
246 | class Foo {
247 | @autobind
248 | onClick(e) {
249 | // ...
250 | }
251 | }
252 | ```
253 |
254 | 使用类属性语法虽然可以避免引入一个`autobind`的实现,但存在一定的缺陷:
255 |
256 | 1. 对于新手不容易理解函数内的`this`的定义。
257 | 2. 无法在函数上使用其它的装饰器(如`memoize`、`deprecated`或检验相关的逻辑等)。
258 |
259 | 因此,推荐使用`@autobind`装饰器实现`this`的事先绑定,推荐使用[core-decorators](https://www.npmjs.com/package/core-decorators)库提供的相关装饰器实现。
260 |
261 | ## JSX
262 |
263 | - [强制]没有子节点的非DOM组件使用自闭合语法。
264 |
265 | 对于DOM节点,按照HTML编码规范相关规则进行闭合,**其中void element使用自闭合语法**。
266 |
267 | ```javascript
268 | // Bad
269 |
270 |
271 | // Good
272 |
273 | ```
274 |
275 | - [强制]保持起始和结束标签在同一层缩进。
276 |
277 | 对于标签前面有其它语句(如`return`的情况,使用括号进行换行和缩进)。
278 |
279 | ```javascript
280 | // Bad
281 | class Message {
282 | render() {
283 | return
284 | Hello World
285 |
;
286 | }
287 | }
288 |
289 | // Good
290 | class Message {
291 | render() {
292 | return (
293 |
294 | Hello World
295 |
296 | );
297 | }
298 | }
299 | ```
300 |
301 | 对于直接`return`的函数组件,可以直接使用括号而省去大括号和`return`关键字:
302 |
303 | ```javascript
304 | let Message = () => (
305 |
306 | Hello World
307 |
308 | );
309 | ```
310 |
311 | - [强制]对于多属性需要换行,从第一个属性开始,每个属性一行。
312 |
313 | ```javascript
314 | // 没有子节点
315 |
319 |
320 | // 有子节点
321 |
325 |
326 |
327 |
328 | ```
329 |
330 | - [强制]以字符串字面量作为值的属性使用双引号(`"`),在其它类型表达式中的字符串使用单引号(`'`)。
331 |
332 | ```javascript
333 | // Bad
334 |
335 |
336 |
337 | // Good
338 |
339 |
340 | ```
341 |
342 | - [强制]自闭合标签的`/>`前添加一个空格。
343 |
344 | ```javascript
345 | // Bad
346 |
347 |
348 |
349 | // Good
350 |
351 | ```
352 |
353 | - [强制]对于值为`true`的属性,省去值部分。
354 |
355 | ```javascript
356 | // Bad
357 |
358 |
359 | // Good
360 |
361 | ```
362 |
363 | - [强制]对于需要使用`key`的场合,提供一个唯一标识作为`key`属性的值,禁止使用可能会变化的属性(如索引)。
364 |
365 | `key`属性是React在进行列表更新时的重要属性,如该属性会发生变化,渲染的性能和**正确性**都无法得到保证。
366 |
367 | ```javascript
368 | // Bad
369 | {list.map((item, index) => )}
370 |
371 | // Good
372 | {list.map(item => )}
373 | ```
374 |
375 | - [建议]避免在JSX的属性值中直接使用对象和函数表达式。
376 |
377 | `PureComponent`使用[`shallowEqual`](https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js)对`props`和`state`进行比较来决定是否需要渲染,而在JSX的属性值中使用对象、函数表达式会造成每一次的对象引用不同,从而`shallowEqual`会返回`false`,导致不必要的渲染。
378 |
379 |
380 | ```javascript
381 | // Bad
382 | class WarnButton {
383 | alertMessage(message) {
384 | alert(message);
385 | }
386 |
387 | render() {
388 | return
389 | }
390 | }
391 |
392 | // Good
393 | class WarnButton {
394 | @autobind
395 | alertMessage() {
396 | alert(this.props.message);
397 | }
398 |
399 | render() {
400 | return
401 | }
402 | }
403 | ```
404 |
405 | - [建议]将JSX的层级控制在3层以内。
406 |
407 | JSX提供了基于组件的便携的复用形式,因此可以通过将结构中的一部分封装为一个函数组件来很好地拆分大型复杂的结构。层次过深的结构会带来过多缩进、可读性下降等缺点。如同控制函数内代码行数和分支层级一样,对JSX的层级进行控制可以有效提升代码的可维护性。
408 |
409 | ```javascript
410 | // Bad
411 | let List = ({items}) => (
412 |
413 | {
414 | items.map(item => (
415 | -
416 |
417 |
{item.title}
418 | {item.subtitle}
419 |
420 |
421 |
424 |
425 | ))
426 | }
427 |
428 | );
429 |
430 | // Good
431 | let Header = ({title, subtitle}) => (
432 |
433 | {title}
434 | {subtitle}
435 |
436 | );
437 |
438 | let Content = ({content}) => ;
439 |
440 | let Footer = ({author, postTime}) => (
441 |
444 | );
445 |
446 | let Item = item => (
447 |
448 |
449 |
450 |
451 |
452 | );
453 |
454 | let List = ({items}) => (
455 |
456 | {items.map(Item)}
457 |
458 | );
459 | ```
460 |
--------------------------------------------------------------------------------
/html-style-guide.md:
--------------------------------------------------------------------------------
1 |
2 | # HTML编码规范
3 |
4 |
5 |
6 |
7 | [1 前言](#user-content-1-%E5%89%8D%E8%A8%80)
8 |
9 | [2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC)
10 |
11 | [2.1 缩进与换行](#user-content-21-%E7%BC%A9%E8%BF%9B%E4%B8%8E%E6%8D%A2%E8%A1%8C)
12 |
13 | [2.2 命名](#user-content-22-%E5%91%BD%E5%90%8D)
14 |
15 | [2.3 标签](#user-content-23-%E6%A0%87%E7%AD%BE)
16 |
17 | [2.4 属性](#user-content-24-%E5%B1%9E%E6%80%A7)
18 |
19 | [3 通用](#user-content-3-%E9%80%9A%E7%94%A8)
20 |
21 | [3.1 DOCTYPE](#user-content-31-doctype)
22 |
23 | [3.2 编码](#user-content-32-%E7%BC%96%E7%A0%81)
24 |
25 | [3.3 CSS 和 JavaScript 引入](#user-content-33-css-%E5%92%8C-javascript-%E5%BC%95%E5%85%A5)
26 |
27 | [4 head](#user-content-4-head)
28 |
29 | [4.1 title](#user-content-41-title)
30 |
31 | [4.2 favicon](#user-content-42-favicon)
32 |
33 | [4.3 viewport](#user-content-43-viewport)
34 |
35 | [5 图片](#user-content-5-%E5%9B%BE%E7%89%87)
36 |
37 | [6 表单](#user-content-6-%E8%A1%A8%E5%8D%95)
38 |
39 | [6.1 控件标题](#user-content-61-%E6%8E%A7%E4%BB%B6%E6%A0%87%E9%A2%98)
40 |
41 | [6.2 按钮](#user-content-62-%E6%8C%89%E9%92%AE)
42 |
43 | [6.3 可访问性 (A11Y)](#user-content-63-%E5%8F%AF%E8%AE%BF%E9%97%AE%E6%80%A7-a11y)
44 |
45 | [7 多媒体](#user-content-7-%E5%A4%9A%E5%AA%92%E4%BD%93)
46 |
47 | [8 模板中的 HTML](#user-content-8-%E6%A8%A1%E6%9D%BF%E4%B8%AD%E7%9A%84-html)
48 |
49 |
50 |
51 |
52 |
53 | ## 1 前言
54 |
55 |
56 | HTML 作为描述网页结构的超文本标记语言,在百度一直有着广泛的应用。本文档的目标是使 HTML 代码风格保持一致,容易被理解和被维护。
57 |
58 |
59 |
60 |
61 | ## 2 代码风格
62 |
63 |
64 | ### 2.1 缩进与换行
65 |
66 |
67 | #### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。
68 |
69 | 解释:
70 | 对于非 HTML 标签之间的缩进,比如 script 或 style 标签内容缩进,与 script 或 style 标签的缩进同级。
71 |
72 | 示例:
73 |
74 | ```html
75 |
81 |
82 | - first
83 | - second
84 |
85 |
91 | ```
92 |
93 | #### [建议] 每行不得超过 `120` 个字符。
94 |
95 | 解释:
96 |
97 | 过长的代码不容易阅读与维护。但是考虑到 HTML 的特殊性,不做硬性要求。
98 |
99 |
100 | ### 2.2 命名
101 |
102 |
103 |
104 | #### [强制] `class` 必须单词全字母小写,单词间以 `-` 分隔。
105 |
106 | #### [强制] `class` 必须代表相应模块或部件的内容或功能,不得以样式信息进行命名。
107 |
108 | 示例:
109 |
110 | ```html
111 |
112 |
113 |
114 |
115 |
116 | ```
117 |
118 | #### [强制] 元素 `id` 必须保证页面唯一。
119 |
120 | 解释:
121 |
122 | 同一个页面中,不同的元素包含相同的 `id`,不符合 `id` 的属性含义。并且使用 `document.getElementById` 时可能导致难以追查的问题。
123 |
124 |
125 | #### [建议] `id` 建议单词全字母小写,单词间以 `-` 分隔。同项目必须保持风格一致。
126 |
127 |
128 | #### [建议] `id`、`class` 命名,在避免冲突并描述清楚的前提下尽可能短。
129 |
130 | 示例:
131 |
132 | ```html
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | ```
148 |
149 | #### [强制] 禁止为了 `hook 脚本`,创建无样式信息的 `class`。
150 |
151 | 解释:
152 |
153 | 不允许 `class` 只用于让 JavaScript 选择某些元素,`class` 应该具有明确的语义和样式。否则容易导致 CSS class 泛滥。
154 |
155 | 使用 `id`、属性选择作为 hook 是更好的方式。
156 |
157 |
158 | #### [强制] 同一页面,应避免使用相同的 `name` 与 `id`。
159 |
160 | 解释:
161 |
162 | IE 浏览器会混淆元素的 `id` 和 `name` 属性, `document.getElementById` 可能获得不期望的元素。所以在对元素的 `id` 与 `name` 属性的命名需要非常小心。
163 |
164 | 一个比较好的实践是,为 `id` 和 `name` 使用不同的命名法。
165 |
166 | 示例:
167 |
168 | ```html
169 |
170 |
171 |
175 | ````
176 |
177 |
178 | ### 2.3 标签
179 |
180 |
181 | #### [强制] 标签名必须使用小写字母。
182 |
183 | 示例:
184 |
185 | ```html
186 |
187 | Hello StyleGuide!
188 |
189 |
190 | Hello StyleGuide!
191 | ```
192 |
193 | #### [强制] 对于无需自闭合的标签,不允许自闭合。
194 |
195 | 解释:
196 |
197 | 常见无需自闭合标签有 `input`、`br`、`img`、`hr` 等。
198 |
199 |
200 | 示例:
201 |
202 | ```html
203 |
204 |
205 |
206 |
207 |
208 | ```
209 |
210 | #### [强制] 对 `HTML5` 中规定允许省略的闭合标签,不允许省略闭合标签。
211 |
212 | 解释:
213 |
214 | 对代码体积要求非常严苛的场景,可以例外。比如:第三方页面使用的投放系统。
215 |
216 |
217 | 示例:
218 |
219 | ```html
220 |
221 |
222 | - first
223 | - second
224 |
225 |
226 |
227 |
228 | - first
229 |
- second
230 |
231 | ```
232 |
233 |
234 | #### [强制] 标签使用必须符合标签嵌套规则。
235 |
236 | 解释:
237 |
238 | 比如 `div` 不得置于 `p` 中,`tbody` 必须置于 `table` 中。
239 |
240 | 详细的标签嵌套规则参见[HTML DTD](http://www.cs.tut.fi/~jkorpela/html5.dtd)中的 `Elements` 定义部分。
241 |
242 |
243 | #### [建议] HTML 标签的使用应该遵循标签的语义。
244 |
245 | 解释:
246 |
247 | 下面是常见标签语义
248 |
249 | - p - 段落
250 | - h1,h2,h3,h4,h5,h6 - 层级标题
251 | - strong,em - 强调
252 | - ins - 插入
253 | - del - 删除
254 | - abbr - 缩写
255 | - code - 代码标识
256 | - cite - 引述来源作品的标题
257 | - q - 引用
258 | - blockquote - 一段或长篇引用
259 | - ul - 无序列表
260 | - ol - 有序列表
261 | - dl,dt,dd - 定义列表
262 |
263 |
264 | 示例:
265 |
266 | ```html
267 |
268 | Esprima serves as an important building block for some JavaScript language tools.
269 |
270 |
271 | Esprima serves as an important building block for some JavaScript language tools.
272 | ```
273 |
274 |
275 | #### [建议] 在 CSS 可以实现相同需求的情况下不得使用表格进行布局。
276 |
277 | 解释:
278 |
279 | 在兼容性允许的情况下应尽量保持语义正确性。对网格对齐和拉伸性有严格要求的场景允许例外,如多列复杂表单。
280 |
281 |
282 | #### [建议] 标签的使用应尽量简洁,减少不必要的标签。
283 |
284 | 示例:
285 |
286 | ```html
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 | ```
295 |
296 |
297 |
298 | ### 2.4 属性
299 |
300 |
301 | #### [强制] 属性名必须使用小写字母。
302 |
303 | 示例:
304 |
305 | ```html
306 |
307 |
308 |
309 |
310 |
311 | ```
312 |
313 |
314 | #### [强制] 属性值必须用双引号包围。
315 |
316 | 解释:
317 |
318 | 不允许使用单引号,不允许不使用引号。
319 |
320 |
321 | 示例:
322 |
323 | ```html
324 |
325 |
326 |
327 |
328 |
329 |
330 | ```
331 |
332 | #### [建议] 布尔类型的属性,建议不添加属性值。
333 |
334 | 示例:
335 |
336 | ```html
337 |
338 |
339 | ```
340 |
341 |
342 | #### [建议] 自定义属性建议以 `xxx-` 为前缀,推荐使用 `data-`。
343 |
344 | 解释:
345 |
346 | 使用前缀有助于区分自定义属性和标准定义的属性。
347 |
348 |
349 | 示例:
350 |
351 | ```html
352 |
353 | ```
354 |
355 |
356 |
357 | ## 3 通用
358 |
359 |
360 | ### 3.1 DOCTYPE
361 |
362 |
363 | #### [强制] 使用 `HTML5` 的 `doctype` 来启用标准模式,建议使用大写的 `DOCTYPE`。
364 |
365 | 示例:
366 |
367 | ```html
368 |
369 | ```
370 |
371 | #### [建议] 启用 IE Edge 模式。
372 |
373 | 示例:
374 |
375 | ```html
376 |
377 | ```
378 |
379 | #### [建议] 在 `html` 标签上设置正确的 `lang` 属性。
380 |
381 | 解释:
382 |
383 | 有助于提高页面的可访问性,如:让语音合成工具确定其所应该采用的发音,令翻译工具确定其翻译语言等。
384 |
385 |
386 | 示例:
387 |
388 | ```html
389 |
390 | ```
391 |
392 |
393 | ### 3.2 编码
394 |
395 |
396 | #### [强制] 页面必须使用精简形式,明确指定字符编码。指定字符编码的 `meta` 必须是 `head` 的第一个直接子元素。
397 |
398 | 解释:
399 |
400 | 见 [HTML5 Charset能用吗](http://www.qianduan.net/html5-charset-can-it.html) 一文。
401 |
402 | 示例:
403 |
404 | ```html
405 |
406 |
407 |
408 | ......
409 |
410 |
411 | ......
412 |
413 |
414 | ```
415 |
416 | #### [建议] `HTML` 文件使用无 `BOM` 的 `UTF-8` 编码。
417 |
418 | 解释:
419 |
420 | `UTF-8` 编码具有更广泛的适应性。`BOM` 在使用程序或工具处理文件时可能造成不必要的干扰。
421 |
422 |
423 |
424 | ### 3.3 CSS 和 JavaScript 引入
425 |
426 |
427 | #### [强制] 引入 `CSS` 时必须指明 `rel="stylesheet"`。
428 |
429 | 示例:
430 |
431 | ```html
432 |
433 | ```
434 |
435 |
436 | #### [建议] 引入 `CSS` 和 `JavaScript` 时无须指明 `type` 属性。
437 |
438 | 解释:
439 |
440 | `text/css` 和 `text/javascript` 是 `type` 的默认值。
441 |
442 |
443 | #### [建议] 展现定义放置于外部 `CSS` 中,行为定义放置于外部 `JavaScript` 中。
444 |
445 | 解释:
446 |
447 | 结构-样式-行为的代码分离,对于提高代码的可阅读性和维护性都有好处。
448 |
449 |
450 | #### [建议] 在 `head` 中引入页面需要的所有 `CSS` 资源。
451 |
452 | 解释:
453 |
454 | 在页面渲染的过程中,新的CSS可能导致元素的样式重新计算和绘制,页面闪烁。
455 |
456 |
457 | #### [建议] `JavaScript` 应当放在页面末尾,或采用异步加载。
458 |
459 | 解释:
460 |
461 | 将 `script` 放在页面中间将阻断页面的渲染。出于性能方面的考虑,如非必要,请遵守此条建议。
462 |
463 |
464 | 示例:
465 |
466 | ```html
467 |
468 |
469 |
470 |
471 | ```
472 |
473 |
474 | #### [建议] 移动环境或只针对现代浏览器设计的 Web 应用,如果引用外部资源的 `URL` 协议部分与页面相同,建议省略协议前缀。
475 |
476 | 解释:
477 |
478 | 使用 `protocol-relative URL` 引入 CSS,在 `IE7/8` 下,会发两次请求。是否使用 `protocol-relative URL` 应充分考虑页面针对的环境。
479 |
480 |
481 | 示例:
482 |
483 | ```html
484 |
485 | ```
486 |
487 |
488 |
489 |
490 |
491 |
492 | ## 4 head
493 |
494 |
495 | ### 4.1 title
496 |
497 |
498 | #### [强制] 页面必须包含 `title` 标签声明标题。
499 |
500 | #### [强制] `title` 必须作为 `head` 的直接子元素,并紧随 `charset` 声明之后。
501 |
502 | 解释:
503 |
504 | `title` 中如果包含 ASCII 之外的字符,浏览器需要知道字符编码类型才能进行解码,否则可能导致乱码。
505 |
506 |
507 | 示例:
508 |
509 | ```html
510 |
511 |
512 | 页面标题
513 |
514 | ```
515 |
516 | ### 4.2 favicon
517 |
518 |
519 | #### [强制] 保证 `favicon` 可访问。
520 |
521 | 解释:
522 |
523 | 在未指定 favicon 时,大多数浏览器会请求 Web Server 根目录下的 `favicon.ico` 。为了保证 favicon 可访问,避免 404,必须遵循以下两种方法之一:
524 |
525 | 1. 在 Web Server 根目录放置 `favicon.ico` 文件。
526 | 2. 使用 `link` 指定 favicon。
527 |
528 |
529 | 示例:
530 |
531 | ```html
532 |
533 | ```
534 |
535 | ### 4.3 viewport
536 |
537 |
538 | #### [建议] 若页面欲对移动设备友好,需指定页面的 `viewport`。
539 |
540 | 解释:
541 |
542 | viewport meta tag 可以设置可视区域的宽度和初始缩放大小,避免在移动设备上出现页面展示不正常。
543 |
544 | 比如,在页面宽度小于 `980px` 时,若需 iOS 设备友好,应当设置 viewport 的 `width` 值来适应你的页面宽度。同时因为不同移动设备分辨率不同,在设置时,应当使用 `device-width` 和 `device-height` 变量。
545 |
546 | 另外,为了使 viewport 正常工作,在页面内容样式布局设计上也要做相应调整,如避免绝对定位等。关于 viewport 的更多介绍,可以参见 [Safari Web Content Guide的介绍](https://developer.apple.com/library/mac/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html#//apple_ref/doc/uid/TP40006509-SW26)
547 |
548 |
549 |
550 |
551 | ## 5 图片
552 |
553 |
554 |
555 | #### [强制] 禁止 `img` 的 `src` 取值为空。延迟加载的图片也要增加默认的 `src`。
556 |
557 | 解释:
558 |
559 | `src` 取值为空,会导致部分浏览器重新加载一次当前页面,参考:
560 |
561 |
562 | #### [建议] 避免为 `img` 添加不必要的 `title` 属性。
563 |
564 | 解释:
565 |
566 | 多余的 `title` 影响看图体验,并且增加了页面尺寸。
567 |
568 | #### [建议] 为重要图片添加 `alt` 属性。
569 |
570 | 解释:
571 |
572 | 可以提高图片加载失败时的用户体验。
573 |
574 | #### [建议] 添加 `width` 和 `height` 属性,以避免页面抖动。
575 |
576 | #### [建议] 有下载需求的图片采用 `img` 标签实现,无下载需求的图片采用 CSS 背景图实现。
577 |
578 | 解释:
579 |
580 | 1. 产品 logo、用户头像、用户产生的图片等有潜在下载需求的图片,以 `img` 形式实现,能方便用户下载。
581 | 2. 无下载需求的图片,比如:icon、背景、代码使用的图片等,尽可能采用 CSS 背景图实现。
582 |
583 |
584 |
585 | ## 6 表单
586 |
587 |
588 | ### 6.1 控件标题
589 |
590 |
591 | #### [强制] 有文本标题的控件必须使用 `label` 标签将其与其标题相关联。
592 |
593 | 解释:
594 |
595 | 有两种方式:
596 |
597 | 1. 将控件置于 `label` 内。
598 | 2. `label` 的 `for` 属性指向控件的 `id`。
599 |
600 | 推荐使用第一种,减少不必要的 `id`。如果 DOM 结构不允许直接嵌套,则应使用第二种。
601 |
602 |
603 | 示例:
604 |
605 | ```html
606 |
607 |
608 |
609 | ```
610 |
611 |
612 | ### 6.2 按钮
613 |
614 |
615 | #### [强制] 使用 `button` 元素时必须指明 `type` 属性值。
616 |
617 | 解释:
618 |
619 | `button` 元素的默认 `type` 为 `submit`,如果被置于 `form` 元素中,点击后将导致表单提交。为显示区分其作用方便理解,必须给出 `type` 属性。
620 |
621 |
622 | 示例:
623 |
624 | ```html
625 |
626 |
627 | ```
628 |
629 | #### [建议] 尽量不要使用按钮类元素的 `name` 属性。
630 |
631 | 解释:
632 |
633 | 由于浏览器兼容性问题,使用按钮的 `name` 属性会带来许多难以发现的问题。具体情况可参考[此文](http://w3help.org/zh-cn/causes/CM2001)。
634 |
635 |
636 | ### 6.3 可访问性 (A11Y)
637 |
638 |
639 | #### [建议] 负责主要功能的按钮在 DOM 中的顺序应靠前。
640 |
641 | 解释:
642 |
643 | 负责主要功能的按钮应相对靠前,以提高可访问性。如果在 CSS 中指定了 `float: right` 则可能导致视觉上主按钮在前,而 DOM 中主按钮靠后的情况。
644 |
645 |
646 | 示例:
647 |
648 | ```html
649 |
650 |
655 |
656 |
662 |
663 |
664 |
669 |
670 |
671 |
672 |
673 |
674 | ```
675 |
676 | #### [建议] 当使用 JavaScript 进行表单提交时,如果条件允许,应使原生提交功能正常工作。
677 |
678 | 解释:
679 |
680 | 当浏览器 JS 运行错误或关闭 JS 时,提交功能将无法工作。如果正确指定了 `form` 元素的 `action` 属性和表单控件的 `name` 属性时,提交仍可继续进行。
681 |
682 |
683 | 示例:
684 |
685 | ```html
686 |
690 | ```
691 |
692 | #### [建议] 在针对移动设备开发的页面时,根据内容类型指定输入框的 `type` 属性。
693 |
694 | 解释:
695 |
696 | 根据内容类型指定输入框类型,能获得能友好的输入体验。
697 |
698 |
699 | 示例:
700 |
701 | ```html
702 |
703 | ```
704 |
705 |
706 |
707 |
708 |
709 | ## 7 多媒体
710 |
711 |
712 |
713 | #### [建议] 当在现代浏览器中使用 `audio` 以及 `video` 标签来播放音频、视频时,应当注意格式。
714 |
715 | 解释:
716 |
717 | 音频应尽可能覆盖到如下格式:
718 |
719 | * MP3
720 | * WAV
721 | * Ogg
722 |
723 | 视频应尽可能覆盖到如下格式:
724 |
725 | * MP4
726 | * WebM
727 | * Ogg
728 |
729 | #### [建议] 在支持 `HTML5` 的浏览器中优先使用 `audio` 和 `video` 标签来定义音视频元素。
730 |
731 | #### [建议] 使用退化到插件的方式来对多浏览器进行支持。
732 |
733 | 示例:
734 |
735 | ```html
736 |
743 |
744 |
751 | ```
752 |
753 | #### [建议] 只在必要的时候开启音视频的自动播放。
754 |
755 |
756 | #### [建议] 在 `object` 标签内部提供指示浏览器不支持该标签的说明。
757 |
758 | 示例:
759 |
760 | ```html
761 |
762 | ```
763 |
764 |
765 |
766 |
767 | ## 8 模板中的 HTML
768 |
769 |
770 | #### [建议] 模板代码的缩进优先保证 HTML 代码的缩进规则。
771 |
772 | 示例:
773 |
774 | ```html
775 |
776 | {if $display == true}
777 |
778 |
779 | {foreach $item_list as $item}
780 | - {$item.name}
-
781 | {/foreach}
782 |
783 |
784 | {/if}
785 |
786 |
787 | {if $display == true}
788 |
789 |
790 | {foreach $item_list as $item}
791 | - {$item.name}
-
792 | {/foreach}
793 |
794 |
795 | {/if}
796 | ```
797 |
798 | #### [建议] 模板代码应以保证 HTML 单个标签语法的正确性为基本原则。
799 |
800 | 示例:
801 |
802 | ```html
803 |
804 | { $item.type_name }
805 |
806 |
807 | { $item.type_name }
808 | ```
809 |
810 | #### [建议] 在循环处理模板数据构造表格时,若要求每行输出固定的个数,建议先将数据分组,之后再循环输出。
811 |
812 | 示例:
813 |
814 | ```html
815 |
816 |
817 | {foreach $item_list as $item_group}
818 |
819 | {foreach $item_group as $item}
820 | | { $item.name } |
821 | {/foreach}
822 |
823 | {/foreach}
824 |
825 |
826 |
827 |
828 |
829 | {foreach $item_list as $item}
830 | | { $item.name } |
831 | {if $item@iteration is div by 5}
832 |
833 |
834 | {/if}
835 | {/foreach}
836 |
837 |
838 | ```
839 |
840 |
841 |
842 |
843 |
--------------------------------------------------------------------------------
/css-style-guide.md:
--------------------------------------------------------------------------------
1 |
2 | # CSS编码规范
3 |
4 |
5 |
6 |
7 | [1 前言](#user-content-1-%E5%89%8D%E8%A8%80)
8 |
9 | [2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC)
10 |
11 | [2.1 文件](#user-content-21-%E6%96%87%E4%BB%B6)
12 |
13 | [2.2 缩进](#user-content-22-%E7%BC%A9%E8%BF%9B)
14 |
15 | [2.3 空格](#user-content-23-%E7%A9%BA%E6%A0%BC)
16 |
17 | [2.4 行长度](#user-content-24-%E8%A1%8C%E9%95%BF%E5%BA%A6)
18 |
19 | [2.5 选择器](#user-content-25-%E9%80%89%E6%8B%A9%E5%99%A8)
20 |
21 | [2.6 属性](#user-content-26-%E5%B1%9E%E6%80%A7)
22 |
23 | [3 通用](#user-content-3-%E9%80%9A%E7%94%A8)
24 |
25 | [3.1 选择器](#user-content-31-%E9%80%89%E6%8B%A9%E5%99%A8)
26 |
27 | [3.2 属性缩写](#user-content-32-%E5%B1%9E%E6%80%A7%E7%BC%A9%E5%86%99)
28 |
29 | [3.3 属性书写顺序](#user-content-33-%E5%B1%9E%E6%80%A7%E4%B9%A6%E5%86%99%E9%A1%BA%E5%BA%8F)
30 |
31 | [3.4 清除浮动](#user-content-34-%E6%B8%85%E9%99%A4%E6%B5%AE%E5%8A%A8)
32 |
33 | [3.5 !important](#user-content-35-important)
34 |
35 | [3.6 z-index](#user-content-36-z-index)
36 |
37 | [4 值与单位](#user-content-4-%E5%80%BC%E4%B8%8E%E5%8D%95%E4%BD%8D)
38 |
39 | [4.1 文本](#user-content-41-%E6%96%87%E6%9C%AC)
40 |
41 | [4.2 数值](#user-content-42-%E6%95%B0%E5%80%BC)
42 |
43 | [4.3 url()](#user-content-43-url)
44 |
45 | [4.4 长度](#user-content-44-%E9%95%BF%E5%BA%A6)
46 |
47 | [4.5 颜色](#user-content-45-%E9%A2%9C%E8%89%B2)
48 |
49 | [4.6 2D 位置](#user-content-46-2d-%E4%BD%8D%E7%BD%AE)
50 |
51 | [5 文本编排](#user-content-5-%E6%96%87%E6%9C%AC%E7%BC%96%E6%8E%92)
52 |
53 | [5.1 字体族](#user-content-51-%E5%AD%97%E4%BD%93%E6%97%8F)
54 |
55 | [5.2 字号](#user-content-52-%E5%AD%97%E5%8F%B7)
56 |
57 | [5.3 字体风格](#user-content-53-%E5%AD%97%E4%BD%93%E9%A3%8E%E6%A0%BC)
58 |
59 | [5.4 字重](#user-content-54-%E5%AD%97%E9%87%8D)
60 |
61 | [5.5 行高](#user-content-55-%E8%A1%8C%E9%AB%98)
62 |
63 | [6 变换与动画](#user-content-6-%E5%8F%98%E6%8D%A2%E4%B8%8E%E5%8A%A8%E7%94%BB)
64 |
65 | [7 响应式](#user-content-7-%E5%93%8D%E5%BA%94%E5%BC%8F)
66 |
67 | [8 兼容性](#user-content-8-%E5%85%BC%E5%AE%B9%E6%80%A7)
68 |
69 | [8.1 属性前缀](#user-content-81-%E5%B1%9E%E6%80%A7%E5%89%8D%E7%BC%80)
70 |
71 | [8.2 Hack](#user-content-82-hack)
72 |
73 | [8.3 Expression](#user-content-83-expression)
74 |
75 |
76 |
77 |
78 |
79 | ## 1 前言
80 |
81 |
82 | CSS 作为网页样式的描述语言,在百度一直有着广泛的应用。本文档的目标是使 CSS 代码风格保持一致,容易被理解和被维护。
83 |
84 | 虽然本文档是针对 CSS 设计的,但是在使用各种 CSS 的预编译器(如 less、sass、stylus 等)时,适用的部分也应尽量遵循本文档的约定。
85 |
86 |
87 |
88 |
89 | ## 2 代码风格
90 |
91 |
92 | ### 2.1 文件
93 |
94 |
95 | #### [建议] `CSS` 文件使用无 `BOM` 的 `UTF-8` 编码。
96 |
97 | 解释:
98 |
99 | UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。
100 |
101 | ### 2.2 缩进
102 |
103 |
104 | #### [强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。
105 |
106 |
107 | 示例:
108 |
109 | ```css
110 | .selector {
111 | margin: 0;
112 | padding: 0;
113 | }
114 | ```
115 |
116 | ### 2.3 空格
117 |
118 |
119 | #### [强制] `选择器` 与 `{` 之间必须包含空格。
120 |
121 | 示例:
122 |
123 | ```css
124 | .selector {
125 | }
126 | ```
127 |
128 | #### [强制] `属性名` 与之后的 `:` 之间不允许包含空格, `:` 与 `属性值` 之间必须包含空格。
129 |
130 | 示例:
131 |
132 | ```css
133 | margin: 0;
134 | ```
135 |
136 | #### [强制] `列表型属性值` 书写在单行时,`,` 后必须跟一个空格。
137 |
138 | 示例:
139 |
140 | ```css
141 | font-family: Arial, sans-serif;
142 | ```
143 |
144 | ### 2.4 行长度
145 |
146 |
147 | #### [强制] 每行不得超过 `120` 个字符,除非单行不可分割。
148 |
149 | 解释:
150 |
151 | 常见不可分割的场景为URL超长。
152 |
153 |
154 | #### [建议] 对于超长的样式,在样式值的 `空格` 处或 `,` 后换行,建议按逻辑分组。
155 |
156 | 示例:
157 |
158 | ```css
159 | /* 不同属性值按逻辑分组 */
160 | background:
161 | transparent url(aVeryVeryVeryLongUrlIsPlacedHere)
162 | no-repeat 0 0;
163 |
164 | /* 可重复多次的属性,每次重复一行 */
165 | background-image:
166 | url(aVeryVeryVeryLongUrlIsPlacedHere)
167 | url(anotherVeryVeryVeryLongUrlIsPlacedHere);
168 |
169 | /* 类似函数的属性值可以根据函数调用的缩进进行 */
170 | background-image: -webkit-gradient(
171 | linear,
172 | left bottom,
173 | left top,
174 | color-stop(0.04, rgb(88,94,124)),
175 | color-stop(0.52, rgb(115,123,162))
176 | );
177 | ```
178 |
179 | ### 2.5 选择器
180 |
181 |
182 | #### [强制] 当一个 rule 包含多个 selector 时,每个选择器声明必须独占一行。
183 |
184 | 示例:
185 |
186 | ```css
187 | /* good */
188 | .post,
189 | .page,
190 | .comment {
191 | line-height: 1.5;
192 | }
193 |
194 | /* bad */
195 | .post, .page, .comment {
196 | line-height: 1.5;
197 | }
198 | ```
199 |
200 | #### [强制] `>`、`+`、`~` 选择器的两边各保留一个空格。
201 |
202 | 示例:
203 |
204 | ```css
205 | /* good */
206 | main > nav {
207 | padding: 10px;
208 | }
209 |
210 | label + input {
211 | margin-left: 5px;
212 | }
213 |
214 | input:checked ~ button {
215 | background-color: #69C;
216 | }
217 |
218 | /* bad */
219 | main>nav {
220 | padding: 10px;
221 | }
222 |
223 | label+input {
224 | margin-left: 5px;
225 | }
226 |
227 | input:checked~button {
228 | background-color: #69C;
229 | }
230 | ```
231 |
232 | #### [强制] 属性选择器中的值必须用双引号包围。
233 |
234 | 解释:
235 |
236 | 不允许使用单引号,不允许不使用引号。
237 |
238 |
239 | 示例:
240 |
241 | ```css
242 | /* good */
243 | article[character="juliet"] {
244 | voice-family: "Vivien Leigh", victoria, female;
245 | }
246 |
247 | /* bad */
248 | article[character='juliet'] {
249 | voice-family: "Vivien Leigh", victoria, female;
250 | }
251 | ```
252 |
253 | ### 2.6 属性
254 |
255 |
256 | #### [强制] 属性定义必须另起一行。
257 |
258 | 示例:
259 |
260 | ```css
261 | /* good */
262 | .selector {
263 | margin: 0;
264 | padding: 0;
265 | }
266 |
267 | /* bad */
268 | .selector { margin: 0; padding: 0; }
269 | ```
270 |
271 | #### [强制] 属性定义后必须以分号结尾。
272 |
273 | 示例:
274 |
275 | ```css
276 | /* good */
277 | .selector {
278 | margin: 0;
279 | }
280 |
281 | /* bad */
282 | .selector {
283 | margin: 0
284 | }
285 | ```
286 |
287 |
288 |
289 |
290 |
291 |
292 | ## 3 通用
293 |
294 |
295 |
296 |
297 | ### 3.1 选择器
298 |
299 |
300 | #### [强制] 如无必要,不得为 `id`、`class` 选择器添加类型选择器进行限定。
301 |
302 | 解释:
303 |
304 | 在性能和维护性上,都有一定的影响。
305 |
306 |
307 | 示例:
308 |
309 |
310 | ```css
311 | /* good */
312 | #error,
313 | .danger-message {
314 | font-color: #c00;
315 | }
316 |
317 | /* bad */
318 | dialog#error,
319 | p.danger-message {
320 | font-color: #c00;
321 | }
322 | ```
323 |
324 | #### [建议] 选择器的嵌套层级应不大于 `3` 级,位置靠后的限定条件应尽可能精确。
325 |
326 | 示例:
327 |
328 | ```css
329 | /* good */
330 | #username input {}
331 | .comment .avatar {}
332 |
333 | /* bad */
334 | .page .header .login #username input {}
335 | .comment div * {}
336 | ```
337 |
338 |
339 |
340 | ### 3.2 属性缩写
341 |
342 |
343 |
344 | #### [建议] 在可以使用缩写的情况下,尽量使用属性缩写。
345 |
346 | 示例:
347 |
348 | ```css
349 | /* good */
350 | .post {
351 | font: 12px/1.5 arial, sans-serif;
352 | }
353 |
354 | /* bad */
355 | .post {
356 | font-family: arial, sans-serif;
357 | font-size: 12px;
358 | line-height: 1.5;
359 | }
360 | ```
361 |
362 | #### [建议] 使用 `border` / `margin` / `padding` 等缩写时,应注意隐含值对实际数值的影响,确实需要设置多个方向的值时才使用缩写。
363 |
364 | 解释:
365 |
366 | `border` / `margin` / `padding` 等缩写会同时设置多个属性的值,容易覆盖不需要覆盖的设定。如某些方向需要继承其他声明的值,则应该分开设置。
367 |
368 |
369 | 示例:
370 |
371 | ```css
372 | /* centering horizontally and highlight featured ones */
373 | article {
374 | margin: 5px;
375 | border: 1px solid #999;
376 | }
377 |
378 | /* good */
379 | .page {
380 | margin-right: auto;
381 | margin-left: auto;
382 | }
383 |
384 | .featured {
385 | border-color: #69c;
386 | }
387 |
388 | /* bad */
389 | .page {
390 | margin: 5px auto; /* introducing redundancy */
391 | }
392 |
393 | .featured {
394 | border: 1px solid #69c; /* introducing redundancy */
395 | }
396 | ```
397 |
398 |
399 | ### 3.3 属性书写顺序
400 |
401 |
402 | #### [建议] 同一 rule set 下的属性在书写时,应按功能进行分组,并以 **Formatting Model(布局方式、位置) > Box Model(尺寸) > Typographic(文本相关) > Visual(视觉效果)** 的顺序书写,以提高代码的可读性。
403 |
404 | 解释:
405 |
406 | - Formatting Model 相关属性包括:`position` / `top` / `right` / `bottom` / `left` / `float` / `display` / `overflow` 等
407 | - Box Model 相关属性包括:`border` / `margin` / `padding` / `width` / `height` 等
408 | - Typographic 相关属性包括:`font` / `line-height` / `text-align` / `word-wrap` 等
409 | - Visual 相关属性包括:`background` / `color` / `transition` / `list-style` 等
410 |
411 | 另外,如果包含 `content` 属性,应放在最前面。
412 |
413 |
414 | 示例:
415 |
416 | ```css
417 | .sidebar {
418 | /* formatting model: positioning schemes / offsets / z-indexes / display / ... */
419 | position: absolute;
420 | top: 50px;
421 | left: 0;
422 | overflow-x: hidden;
423 |
424 | /* box model: sizes / margins / paddings / borders / ... */
425 | width: 200px;
426 | padding: 5px;
427 | border: 1px solid #ddd;
428 |
429 | /* typographic: font / aligns / text styles / ... */
430 | font-size: 14px;
431 | line-height: 20px;
432 |
433 | /* visual: colors / shadows / gradients / ... */
434 | background: #f5f5f5;
435 | color: #333;
436 | -webkit-transition: color 1s;
437 | -moz-transition: color 1s;
438 | transition: color 1s;
439 | }
440 | ```
441 |
442 |
443 | ### 3.4 清除浮动
444 |
445 |
446 |
447 | #### [建议] 当元素需要撑起高度以包含内部的浮动元素时,通过对伪类设置 `clear` 或触发 `BFC` 的方式进行 `clearfix`。尽量不使用增加空标签的方式。
448 |
449 | 解释:
450 |
451 | 触发 BFC 的方式很多,常见的有:
452 |
453 | * float 非 none
454 | * position 非 static
455 | * overflow 非 visible
456 |
457 | 如希望使用更小副作用的清除浮动方法,参见 [A new micro clearfix hack](http://nicolasgallagher.com/micro-clearfix-hack/) 一文。
458 |
459 | 另需注意,对已经触发 BFC 的元素不需要再进行 clearfix。
460 |
461 |
462 | ### 3.5 !important
463 |
464 |
465 | #### [建议] 尽量不使用 `!important` 声明。
466 |
467 |
468 | #### [建议] 当需要强制指定样式且不允许任何场景覆盖时,通过标签内联和 `!important` 定义样式。
469 |
470 | 解释:
471 |
472 | 必须注意的是,仅在设计上 `确实不允许任何其它场景覆盖样式` 时,才使用内联的 `!important` 样式。通常在第三方环境的应用中使用这种方案。下面的 `z-index` 章节是其中一个特殊场景的典型样例。
473 |
474 |
475 |
476 | ### 3.6 z-index
477 |
478 |
479 |
480 | #### [建议] 将 `z-index` 进行分层,对文档流外绝对定位元素的视觉层级关系进行管理。
481 |
482 | 解释:
483 |
484 | 同层的多个元素,如多个由用户输入触发的 Dialog,在该层级内使用相同的 `z-index` 或递增 `z-index`。
485 |
486 | 建议每层包含100个 `z-index` 来容纳足够的元素,如果每层元素较多,可以调整这个数值。
487 |
488 |
489 | #### [建议] 在可控环境下,期望显示在最上层的元素,`z-index` 指定为 `999999`。
490 |
491 | 解释:
492 |
493 | 可控环境分成两种,一种是自身产品线环境;还有一种是可能会被其他产品线引用,但是不会被外部第三方的产品引用。
494 |
495 | 不建议取值为 `2147483647`。以便于自身产品线被其他产品线引用时,当遇到层级覆盖冲突的情况,留出向上调整的空间。
496 |
497 |
498 | #### [建议] 在第三方环境下,期望显示在最上层的元素,通过标签内联和 `!important`,将 `z-index` 指定为 `2147483647`。
499 |
500 | 解释:
501 |
502 | 第三方环境对于开发者来说完全不可控。在第三方环境下的元素,为了保证元素不被其页面其他样式定义覆盖,需要采用此做法。
503 |
504 |
505 |
506 |
507 | ## 4 值与单位
508 |
509 |
510 | ### 4.1 文本
511 |
512 |
513 | #### [强制] 文本内容必须用双引号包围。
514 |
515 | 解释:
516 |
517 | 文本类型的内容可能在选择器、属性值等内容中。
518 |
519 |
520 | 示例:
521 |
522 | ```css
523 | /* good */
524 | html[lang|="zh"] q:before {
525 | font-family: "Microsoft YaHei", sans-serif;
526 | content: "“";
527 | }
528 |
529 | html[lang|="zh"] q:after {
530 | font-family: "Microsoft YaHei", sans-serif;
531 | content: "”";
532 | }
533 |
534 | /* bad */
535 | html[lang|=zh] q:before {
536 | font-family: 'Microsoft YaHei', sans-serif;
537 | content: '“';
538 | }
539 |
540 | html[lang|=zh] q:after {
541 | font-family: "Microsoft YaHei", sans-serif;
542 | content: "”";
543 | }
544 | ```
545 |
546 | ### 4.2 数值
547 |
548 |
549 | #### [强制] 当数值为 0 - 1 之间的小数时,省略整数部分的 `0`。
550 |
551 | 示例:
552 |
553 | ```css
554 | /* good */
555 | panel {
556 | opacity: .8;
557 | }
558 |
559 | /* bad */
560 | panel {
561 | opacity: 0.8;
562 | }
563 | ```
564 |
565 | ### 4.3 url()
566 |
567 |
568 | #### [强制] `url()` 函数中的路径不加引号。
569 |
570 | 示例:
571 |
572 | ```css
573 | body {
574 | background: url(bg.png);
575 | }
576 | ```
577 |
578 |
579 | #### [建议] `url()` 函数中的绝对路径可省去协议名。
580 |
581 |
582 | 示例:
583 |
584 | ```css
585 | body {
586 | background: url(//baidu.com/img/bg.png) no-repeat 0 0;
587 | }
588 | ```
589 |
590 |
591 | ### 4.4 长度
592 |
593 |
594 | #### [强制] 长度为 `0` 时须省略单位。 (也只有长度单位可省)
595 |
596 | 示例:
597 |
598 | ```css
599 | /* good */
600 | body {
601 | padding: 0 5px;
602 | }
603 |
604 | /* bad */
605 | body {
606 | padding: 0px 5px;
607 | }
608 | ```
609 |
610 |
611 | ### 4.5 颜色
612 |
613 |
614 | #### [强制] RGB颜色值必须使用十六进制记号形式 `#rrggbb`。不允许使用 `rgb()`。
615 |
616 | 解释:
617 |
618 | 带有alpha的颜色信息可以使用 `rgba()`。使用 `rgba()` 时每个逗号后必须保留一个空格。
619 |
620 |
621 | 示例:
622 |
623 | ```css
624 | /* good */
625 | .success {
626 | box-shadow: 0 0 2px rgba(0, 128, 0, .3);
627 | border-color: #008000;
628 | }
629 |
630 | /* bad */
631 | .success {
632 | box-shadow: 0 0 2px rgba(0,128,0,.3);
633 | border-color: rgb(0, 128, 0);
634 | }
635 | ```
636 |
637 | #### [强制] 颜色值可以缩写时,必须使用缩写形式。
638 |
639 | 示例:
640 |
641 | ```css
642 | /* good */
643 | .success {
644 | background-color: #aca;
645 | }
646 |
647 | /* bad */
648 | .success {
649 | background-color: #aaccaa;
650 | }
651 | ```
652 |
653 | #### [强制] 颜色值不允许使用命名色值。
654 |
655 | 示例:
656 |
657 | ```css
658 | /* good */
659 | .success {
660 | color: #90ee90;
661 | }
662 |
663 | /* bad */
664 | .success {
665 | color: lightgreen;
666 | }
667 | ```
668 |
669 | #### [建议] 颜色值中的英文字符采用小写。如不用小写也需要保证同一项目内保持大小写一致。
670 |
671 |
672 | 示例:
673 |
674 | ```css
675 | /* good */
676 | .success {
677 | background-color: #aca;
678 | color: #90ee90;
679 | }
680 |
681 | /* good */
682 | .success {
683 | background-color: #ACA;
684 | color: #90EE90;
685 | }
686 |
687 | /* bad */
688 | .success {
689 | background-color: #ACA;
690 | color: #90ee90;
691 | }
692 | ```
693 |
694 |
695 | ### 4.6 2D 位置
696 |
697 |
698 | #### [强制] 必须同时给出水平和垂直方向的位置。
699 |
700 | 解释:
701 |
702 | 2D 位置初始值为 `0% 0%`,但在只有一个方向的值时,另一个方向的值会被解析为 center。为避免理解上的困扰,应同时给出两个方向的值。[background-position属性值的定义](http://www.w3.org/TR/CSS21/colors.html#propdef-background-position)
703 |
704 |
705 | 示例:
706 |
707 | ```css
708 | /* good */
709 | body {
710 | background-position: center top; /* 50% 0% */
711 | }
712 |
713 | /* bad */
714 | body {
715 | background-position: top; /* 50% 0% */
716 | }
717 | ```
718 |
719 |
720 |
721 |
722 |
723 | ## 5 文本编排
724 |
725 |
726 | ### 5.1 字体族
727 |
728 |
729 | #### [强制] `font-family` 属性中的字体族名称应使用字体的英文 `Family Name`,其中如有空格,须放置在引号中。
730 |
731 | 解释:
732 |
733 | 所谓英文 Family Name,为字体文件的一个元数据,常见名称如下:
734 |
735 | 字体 | 操作系统 | Family Name
736 | -----|----------|------------
737 | 宋体 (中易宋体) | Windows | SimSun
738 | 黑体 (中易黑体) | Windows | SimHei
739 | 微软雅黑 | Windows | Microsoft YaHei
740 | 微软正黑 | Windows | Microsoft JhengHei
741 | 华文黑体 | Mac/iOS | STHeiti
742 | 冬青黑体 | Mac/iOS | Hiragino Sans GB
743 | 文泉驿正黑 | Linux | WenQuanYi Zen Hei
744 | 文泉驿微米黑 | Linux | WenQuanYi Micro Hei
745 |
746 |
747 | 示例:
748 |
749 | ```css
750 | h1 {
751 | font-family: "Microsoft YaHei";
752 | }
753 | ```
754 |
755 |
756 | #### [强制] `font-family` 按「西文字体在前、中文字体在后」、「效果佳 (质量高/更能满足需求) 的字体在前、效果一般的字体在后」的顺序编写,最后必须指定一个通用字体族( `serif` / `sans-serif` )。
757 |
758 | 解释:
759 |
760 | 更详细说明可参考[本文](http://www.zhihu.com/question/19911793/answer/13329819)。
761 |
762 | 示例:
763 |
764 | ```css
765 | /* Display according to platform */
766 | .article {
767 | font-family: Arial, sans-serif;
768 | }
769 |
770 | /* Specific for most platforms */
771 | h1 {
772 | font-family: "Helvetica Neue", Arial, "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif;
773 | }
774 | ```
775 |
776 | #### [强制] `font-family` 不区分大小写,但在同一个项目中,同样的 `Family Name` 大小写必须统一。
777 |
778 | 示例:
779 |
780 | ```css
781 | /* good */
782 | body {
783 | font-family: Arial, sans-serif;
784 | }
785 |
786 | h1 {
787 | font-family: Arial, "Microsoft YaHei", sans-serif;
788 | }
789 |
790 | /* bad */
791 | body {
792 | font-family: arial, sans-serif;
793 | }
794 |
795 | h1 {
796 | font-family: Arial, "Microsoft YaHei", sans-serif;
797 | }
798 | ```
799 |
800 | ### 5.2 字号
801 |
802 |
803 | #### [强制] 需要在 Windows 平台显示的中文内容,其字号应不小于 `12px`。
804 |
805 | 解释:
806 |
807 | 由于 Windows 的字体渲染机制,小于 `12px` 的文字显示效果极差、难以辨认。
808 |
809 |
810 | ### 5.3 字体风格
811 |
812 |
813 | #### [建议] 需要在 Windows 平台显示的中文内容,不要使用除 `normal` 外的 `font-style`。其他平台也应慎用。
814 |
815 | 解释:
816 |
817 | 由于中文字体没有 `italic` 风格的实现,所有浏览器下都会 fallback 到 `obilique` 实现 (自动拟合为斜体),小字号下 (特别是 Windows 下会在小字号下使用点阵字体的情况下) 显示效果差,造成阅读困难。
818 |
819 |
820 | ### 5.4 字重
821 |
822 |
823 | #### [强制] `font-weight` 属性必须使用数值方式描述。
824 |
825 | 解释:
826 |
827 | CSS 的字重分 100 – 900 共九档,但目前受字体本身质量和浏览器的限制,实际上支持 `400` 和 `700` 两档,分别等价于关键词 `normal` 和 `bold`。
828 |
829 | 浏览器本身使用一系列[启发式规则](http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight)来进行匹配,在 `<700` 时一般匹配字体的 Regular 字重,`>=700` 时匹配 Bold 字重。
830 |
831 | 但已有浏览器开始支持 `=600` 时匹配 Semibold 字重 (见[此表](http://justineo.github.io/slideshows/font/#/3/15)),故使用数值描述增加了灵活性,也更简短。
832 |
833 | 示例:
834 |
835 | ```css
836 | /* good */
837 | h1 {
838 | font-weight: 700;
839 | }
840 |
841 | /* bad */
842 | h1 {
843 | font-weight: bold;
844 | }
845 | ```
846 |
847 | ### 5.5 行高
848 |
849 |
850 | #### [建议] `line-height` 在定义文本段落时,应使用数值。
851 |
852 | 解释:
853 |
854 | 将 `line-height` 设置为数值,浏览器会基于当前元素设置的 `font-size` 进行再次计算。在不同字号的文本段落组合中,能达到较为舒适的行间间隔效果,避免在每个设置了 `font-size` 都需要设置 `line-height`。
855 |
856 | 当 `line-height` 用于控制垂直居中时,还是应该设置成与容器高度一致。
857 |
858 |
859 | 示例:
860 |
861 | ```css
862 | .container {
863 | line-height: 1.5;
864 | }
865 | ```
866 |
867 |
868 |
869 | ## 6 变换与动画
870 |
871 |
872 |
873 | #### [强制] 使用 `transition` 时应指定 `transition-property`。
874 |
875 | 示例:
876 |
877 | ```css
878 | /* good */
879 | .box {
880 | transition: color 1s, border-color 1s;
881 | }
882 |
883 | /* bad */
884 | .box {
885 | transition: all 1s;
886 | }
887 | ```
888 |
889 | #### [建议] 尽可能在浏览器能高效实现的属性上添加过渡和动画。
890 |
891 | 解释:
892 |
893 | 见[本文](http://www.html5rocks.com/en/tutorials/speed/high-performance-animations/),在可能的情况下应选择这样四种变换:
894 |
895 | * `transform: translate(npx, npx);`
896 | * `transform: scale(n);`
897 | * `transform: rotate(ndeg);`
898 | * `opacity: 0..1;`
899 |
900 | 典型的,可以使用 `translate` 来代替 `left` 作为动画属性。
901 |
902 | 示例:
903 |
904 | ```css
905 | /* good */
906 | .box {
907 | transition: transform 1s;
908 | }
909 | .box:hover {
910 | transform: translate(20px); /* move right for 20px */
911 | }
912 |
913 | /* bad */
914 | .box {
915 | left: 0;
916 | transition: left 1s;
917 | }
918 | .box:hover {
919 | left: 20px; /* move right for 20px */
920 | }
921 | ```
922 |
923 |
924 |
925 |
926 | ## 7 响应式
927 |
928 |
929 |
930 | #### [强制] `Media Query` 不得单独编排,必须与相关的规则一起定义。
931 |
932 | 示例:
933 |
934 | ```css
935 | /* Good */
936 | /* header styles */
937 | @media (...) {
938 | /* header styles */
939 | }
940 |
941 | /* main styles */
942 | @media (...) {
943 | /* main styles */
944 | }
945 |
946 | /* footer styles */
947 | @media (...) {
948 | /* footer styles */
949 | }
950 |
951 |
952 | /* Bad */
953 | /* header styles */
954 | /* main styles */
955 | /* footer styles */
956 |
957 | @media (...) {
958 | /* header styles */
959 | /* main styles */
960 | /* footer styles */
961 | }
962 | ```
963 |
964 | #### [强制] `Media Query` 如果有多个逗号分隔的条件时,应将每个条件放在单独一行中。
965 |
966 | 示例:
967 |
968 | ```css
969 | @media
970 | (-webkit-min-device-pixel-ratio: 2), /* Webkit-based browsers */
971 | (min--moz-device-pixel-ratio: 2), /* Older Firefox browsers (prior to Firefox 16) */
972 | (min-resolution: 2dppx), /* The standard way */
973 | (min-resolution: 192dpi) { /* dppx fallback */
974 | /* Retina-specific stuff here */
975 | }
976 | ```
977 |
978 | #### [建议] 尽可能给出在高分辨率设备 (Retina) 下效果更佳的样式。
979 |
980 |
981 |
982 | ## 8 兼容性
983 |
984 |
985 | ### 8.1 属性前缀
986 |
987 |
988 | #### [强制] 带私有前缀的属性由长到短排列,按冒号位置对齐。
989 |
990 | 解释:
991 |
992 | 标准属性放在最后,按冒号对齐方便阅读,也便于在编辑器内进行多行编辑。
993 |
994 |
995 | 示例:
996 |
997 | ```css
998 | .box {
999 | -webkit-box-sizing: border-box;
1000 | -moz-box-sizing: border-box;
1001 | box-sizing: border-box;
1002 | }
1003 | ```
1004 |
1005 |
1006 | ### 8.2 Hack
1007 |
1008 |
1009 | #### [建议] 需要添加 `hack` 时应尽可能考虑是否可以采用其他方式解决。
1010 |
1011 | 解释:
1012 |
1013 | 如果能通过合理的 HTML 结构或使用其他的 CSS 定义达到理想的样式,则不应该使用 hack 手段解决问题。通常 hack 会导致维护成本的增加。
1014 |
1015 | #### [建议] 尽量使用 `选择器 hack` 处理兼容性,而非 `属性 hack`。
1016 |
1017 | 解释:
1018 |
1019 | 尽量使用符合 CSS 语法的 selector hack,可以避免一些第三方库无法识别 hack 语法的问题。
1020 |
1021 |
1022 | 示例:
1023 |
1024 | ```css
1025 | /* IE 7 */
1026 | *:first-child + html #header {
1027 | margin-top: 3px;
1028 | padding: 5px;
1029 | }
1030 |
1031 | /* IE 6 */
1032 | * html #header {
1033 | margin-top: 5px;
1034 | padding: 4px;
1035 | }
1036 | ```
1037 |
1038 |
1039 | #### [建议] 尽量使用简单的 `属性 hack`。
1040 |
1041 | 示例:
1042 |
1043 | ```css
1044 | .box {
1045 | _display: inline; /* fix double margin */
1046 | float: left;
1047 | margin-left: 20px;
1048 | }
1049 |
1050 | .container {
1051 | overflow: hidden;
1052 | *zoom: 1; /* triggering hasLayout */
1053 | }
1054 | ```
1055 |
1056 | ### 8.3 Expression
1057 |
1058 |
1059 | #### [强制] 禁止使用 `Expression`。
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
--------------------------------------------------------------------------------
/es-next-style-guide.md:
--------------------------------------------------------------------------------
1 |
2 | # JavaScript 编码规范 - ESNext 补充篇(草案)
3 |
4 |
5 |
6 |
7 | [1 前言](#user-content-1-%E5%89%8D%E8%A8%80)
8 |
9 | [2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC)
10 |
11 | [2.1 文件](#user-content-21-%E6%96%87%E4%BB%B6)
12 |
13 | [2.2 结构](#user-content-22-%E7%BB%93%E6%9E%84)
14 |
15 | [2.2.1 缩进](#user-content-221-%E7%BC%A9%E8%BF%9B)
16 |
17 | [2.2.2 空格](#user-content-222-%E7%A9%BA%E6%A0%BC)
18 |
19 | [2.2.3 语句](#user-content-223-%E8%AF%AD%E5%8F%A5)
20 |
21 | [3 语言特性](#user-content-3-%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7)
22 |
23 | [3.1 变量](#user-content-31-%E5%8F%98%E9%87%8F)
24 |
25 | [3.2 解构](#user-content-32-%E8%A7%A3%E6%9E%84)
26 |
27 | [3.3 模板字符串](#user-content-33-%E6%A8%A1%E6%9D%BF%E5%AD%97%E7%AC%A6%E4%B8%B2)
28 |
29 | [3.4 函数](#user-content-34-%E5%87%BD%E6%95%B0)
30 |
31 | [3.5 箭头函数](#user-content-35-%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0)
32 |
33 | [3.6 对象](#user-content-36-%E5%AF%B9%E8%B1%A1)
34 |
35 | [3.7 类](#user-content-37-%E7%B1%BB)
36 |
37 | [3.8 模块](#user-content-38-%E6%A8%A1%E5%9D%97)
38 |
39 | [3.9 集合](#user-content-39-%E9%9B%86%E5%90%88)
40 |
41 | [3.10 异步](#user-content-310-%E5%BC%82%E6%AD%A5)
42 |
43 | [4 环境](#user-content-4-%E7%8E%AF%E5%A2%83)
44 |
45 | [4.1 运行环境](#user-content-41-%E8%BF%90%E8%A1%8C%E7%8E%AF%E5%A2%83)
46 |
47 | [4.2 预编译](#user-content-42-%E9%A2%84%E7%BC%96%E8%AF%91)
48 |
49 |
50 |
51 |
52 |
53 | ## 1 前言
54 |
55 |
56 | 随着 ECMAScript 的不断发展,越来越多更新的语言特性将被使用,给应用的开发带来方便。本文档的目标是使 ECMAScript 新特性的代码风格保持一致,并给予一些实践建议。
57 |
58 | 本文档仅包含新特性部分。基础部分请遵循 [JavaScript Style Guide](javascript-style-guide.md)。
59 |
60 | 由于 ECMAScript 依然在快速的不断发展,本文档也将可能随时保持更新。更新内容主要涉及对新增的语言特性的格式规范化、实践指导,引擎与编译器环境变化的使用指导。
61 |
62 | 虽然本文档是针对 ECMAScript 设计的,但是在使用各种基于 ECMAScript 扩展的语言时(如 JSX、TypeScript 等),适用的部分也应尽量遵循本文档的约定。
63 |
64 |
65 |
66 |
67 |
68 | ## 2 代码风格
69 |
70 |
71 |
72 |
73 |
74 | ### 2.1 文件
75 |
76 |
77 | ##### [建议] ESNext 语法的 JavaScript 文件使用 `.js` 扩展名。
78 |
79 | ##### [强制] 当文件无法使用 `.js` 扩展名时,使用 `.es` 扩展名。
80 |
81 | 解释:
82 |
83 | 某些应用开发时,可能同时包含 ES 5和 ESNext 文件,运行环境仅支持 ES5,ESNext 文件需要经过预编译。部分场景下,编译工具的选择可能需要通过扩展名区分,需要重新定义ESNext文件的扩展名。此时,ESNext 文件必须使用 `.es` 扩展名。
84 |
85 | 但是,更推荐使用其他条件作为是否需要编译的区分:
86 |
87 | 1. 基于文件内容。
88 | 2. 不同类型文件放在不同目录下。
89 |
90 |
91 |
92 |
93 |
94 | ### 2.2 结构
95 |
96 |
97 | #### 2.2.1 缩进
98 |
99 |
100 | ##### [建议] 使用多行模板字符串时遵循缩进原则。当空行与空白字符敏感时,不使用多行模板字符串。
101 |
102 | 解释:
103 |
104 | `4` 空格为一个缩进,换行后添加一层缩进。将起始和结束的 `` ` `` 符号单独放一行,有助于生成 HTML 时的标签对齐。
105 |
106 | 为避免破坏缩进的统一,当空行与空白字符敏感时,建议使用 `多个模板字符串` 或 `普通字符串` 进行连接运算,也可使用数组 `join` 生成字符串。
107 |
108 | 示例:
109 |
110 | ```javascript
111 | // good
112 | function foo() {
113 | let html = `
114 |
118 | `;
119 | }
120 |
121 | // Good
122 | function greeting(name) {
123 | return 'Hello, \n'
124 | + `${name.firstName} ${name.lastName}`;
125 | }
126 |
127 | // Bad
128 | function greeting(name) {
129 | return `Hello,
130 | ${name.firstName} ${name.lastName}`;
131 | }
132 | ```
133 |
134 |
135 | #### 2.2.2 空格
136 |
137 |
138 | ##### [强制] 使用 `generator` 时,`*` 前面不允许有空格,`*` 后面必须有一个空格。
139 |
140 | 示例:
141 |
142 | ```javascript
143 | // good
144 | function* caller() {
145 | yield 'a';
146 | yield* callee();
147 | yield 'd';
148 | }
149 |
150 | // bad
151 | function * caller() {
152 | yield 'a';
153 | yield *callee();
154 | yield 'd';
155 | }
156 | ```
157 |
158 |
159 | #### 2.2.3 语句
160 |
161 |
162 | ##### [强制] 类声明结束不允许添加分号。
163 |
164 | 解释:
165 |
166 | 与函数声明保持一致。
167 |
168 |
169 | ##### [强制] 类成员定义中,方法定义后不允许添加分号,成员属性定义后必须添加分号。
170 |
171 | 解释:
172 |
173 | 成员属性是当前 **Stage 0** 的标准,如果使用的话,则定义后加上分号。
174 |
175 | 示例:
176 |
177 | ```javascript
178 | // good
179 | class Foo {
180 | foo = 3;
181 |
182 | bar() {
183 |
184 | }
185 | }
186 |
187 | // bad
188 | class Foo {
189 | foo = 3
190 |
191 | bar() {
192 |
193 | }
194 | }
195 | ```
196 |
197 | ##### [强制] `export` 语句后,不允许出现表示空语句的分号。
198 |
199 | 解释:
200 |
201 | `export` 关键字不影响后续语句类型。
202 |
203 | 示例:
204 |
205 | ```javascript
206 | // good
207 | export function foo() {
208 | }
209 |
210 | export default function bar() {
211 | }
212 |
213 |
214 | // bad
215 | export function foo() {
216 | };
217 |
218 | export default function bar() {
219 | };
220 | ```
221 |
222 |
223 | ##### [强制] 属性装饰器后,可以不加分号的场景,不允许加分号。
224 |
225 | 解释:
226 |
227 | 只有一种场景是必须加分号的:当属性 `key` 是 `computed property key` 时,其装饰器必须加分号,否则修饰 `key` 的 `[]` 会做为之前表达式的 `property accessor`。
228 |
229 | 上面描述的场景,装饰器后需要加分号。其余场景下的属性装饰器后不允许加分号。
230 |
231 | 示例:
232 |
233 | ```javascript
234 | // good
235 | class Foo {
236 | @log('INFO')
237 | bar() {
238 |
239 | }
240 |
241 | @log('INFO');
242 | ['bar' + 2]() {
243 |
244 | }
245 | }
246 |
247 | // bad
248 | class Foo {
249 | @log('INFO');
250 | bar() {
251 |
252 | }
253 |
254 | @log('INFO')
255 | ['bar' + 2]() {
256 |
257 | }
258 | }
259 | ```
260 |
261 |
262 | ##### [强制] 箭头函数的参数只有一个,并且不包含解构时,参数部分的括号必须省略。
263 |
264 | 示例:
265 |
266 | ```javascript
267 | // good
268 | list.map(item => item * 2);
269 |
270 | // good
271 | let fetchName = async id => {
272 | let user = await request(`users/${id}`);
273 | return user.fullName;
274 | };
275 |
276 | // bad
277 | list.map((item) => item * 2);
278 |
279 | // bad
280 | let fetchName = async (id) => {
281 | let user = await request(`users/${id}`);
282 | return user.fullName;
283 | };
284 | ```
285 |
286 | ##### [建议] 箭头函数的函数体只有一个单行表达式语句,且作为返回值时,省略 `{}` 和 `return`。
287 |
288 | 如果单个表达式过长,可以使用 `()` 进行包裹。
289 |
290 | 示例:
291 |
292 | ```javascript
293 | // good
294 | list.map(item => item * 2);
295 |
296 | let foo = () => (
297 | condition
298 | ? returnValueA()
299 | : returnValueB()
300 | );
301 |
302 | // bad
303 | list.map(item => {
304 | return item * 2;
305 | });
306 | ```
307 |
308 | ##### [建议] 箭头函数的函数体只有一个 `Object Literal`,且作为返回值时,使用 `()` 包裹。
309 |
310 | 示例:
311 |
312 | ```javascript
313 | // good
314 | list.map(item => ({name: item[0], email: item[1]}));
315 | ```
316 |
317 | ##### [强制] 解构多个变量时,如果超过行长度限制,每个解构的变量必须单独一行。
318 |
319 | 解释:
320 |
321 | 太多的变量解构会让一行的代码非常长,极有可能超过单行长度控制,使代码可读性下降。
322 |
323 | 示例:
324 |
325 | ```javascript
326 | // good
327 | let {
328 | name: personName,
329 | email: personEmail,
330 | sex: personSex,
331 | age: personAge
332 | } = person;
333 |
334 | // bad
335 | let {name: personName, email: personEmail,
336 | sex: personSex, age: personAge
337 | } = person;
338 | ```
339 |
340 |
341 |
342 |
343 |
344 |
345 | ## 3 语言特性
346 |
347 |
348 |
349 |
350 |
351 | ### 3.1 变量
352 |
353 |
354 | #### [强制] 使用 `let` 和 `const` 定义变量,不使用 `var`。
355 |
356 | 解释:
357 |
358 | 使用 `let` 和 `const` 定义时,变量作用域范围更明确。
359 |
360 | 示例:
361 |
362 | ```javascript
363 | // good
364 | for (let i = 0; i < 10; i++) {
365 |
366 | }
367 |
368 | // bad
369 | for (var i = 0; i < 10; i++) {
370 |
371 | }
372 | ```
373 |
374 |
375 |
376 | ### 3.2 解构
377 |
378 |
379 | #### [强制] 不要使用3层及以上的解构。
380 |
381 | 解释:
382 |
383 | 过多层次的解构会让代码变得难以阅读。
384 |
385 | 示例:
386 |
387 | ```javascript
388 | // bad
389 | let {documentElement: {firstElementChild: {nextSibling}}} = window;
390 | ```
391 |
392 | #### [建议] 使用解构减少中间变量。
393 |
394 | 解释:
395 |
396 | 常见场景如变量值交换,可能产生中间变量。这种场景推荐使用解构。
397 |
398 | 示例:
399 |
400 | ```javascript
401 | // good
402 | [x, y] = [y, x];
403 |
404 | // bad
405 | let temp = x;
406 | x = y;
407 | y = temp;
408 | ```
409 |
410 | #### [强制] 仅定义一个变量时不允许使用解构。
411 |
412 | 解释:
413 |
414 | 在这种场景下,使用解构将降低代码可读性。
415 |
416 | 示例:
417 |
418 | ```javascript
419 | // good
420 | let len = myString.length;
421 |
422 | // bad
423 | let {length: len} = myString;
424 | ```
425 |
426 | #### [强制] 如果不节省编写时产生的中间变量,解构表达式 `=` 号右边不允许是 `ObjectLiteral` 和 `ArrayLiteral`。
427 |
428 | 解释:
429 |
430 | 在这种场景下,使用解构将降低代码可读性,通常也并无收益。
431 |
432 | 示例:
433 |
434 | ```javascript
435 | // good
436 | let {first: firstName, last: lastName} = person;
437 | let one = 1;
438 | let two = 2;
439 |
440 | // bad
441 | let [one, two] = [1, 2];
442 | ```
443 |
444 | #### [强制] 使用剩余运算符时,剩余运算符之前的所有元素必需具名。
445 |
446 | 解释:
447 |
448 | 剩余运算符之前的元素省略名称可能带来较大的程序阅读障碍。如果仅仅为了取数组后几项,请使用 `slice` 方法。
449 |
450 | 示例:
451 |
452 | ```javascript
453 | // good
454 | let [one, two, ...anyOther] = myArray;
455 | let other = myArray.slice(3);
456 |
457 | // bad
458 | let [,,, ...other] = myArray;
459 | ```
460 |
461 |
462 |
463 | ### 3.3 模板字符串
464 |
465 |
466 |
467 | #### [强制] 字符串内变量替换时,不要使用 `2` 次及以上的函数调用。
468 |
469 | 解释:
470 |
471 | 在变量替换符内有太多的函数调用等复杂语法会导致可读性下降。
472 |
473 | 示例:
474 |
475 | ```javascript
476 | // good
477 | let fullName = getFullName(getFirstName(), getLastName());
478 | let s = `Hello ${fullName}`;
479 |
480 | // bad
481 | let s = `Hello ${getFullName(getFirstName(), getLastName())}`;
482 | ```
483 |
484 |
485 |
486 | ### 3.4 函数
487 |
488 |
489 | #### [建议] 使用变量默认语法代替基于条件判断的默认值声明。
490 |
491 | 解释:
492 |
493 | 添加默认值有助于引擎的优化,在未来 `strong mode` 下也会有更好的效果。
494 |
495 | 示例:
496 |
497 | ```javascript
498 | // good
499 | function foo(text = 'hello') {
500 | }
501 |
502 | // bad
503 | function foo(text) {
504 | text = text || 'hello';
505 | }
506 | ```
507 |
508 |
509 | #### [强制] 不要使用 `arguments` 对象,应使用 `...args` 代替。
510 |
511 | 解释:
512 |
513 | 在未来 `strong mode` 下 `arguments` 将被禁用。
514 |
515 | 示例:
516 |
517 | ```javascript
518 | // good
519 | function foo(...args) {
520 | console.log(args.join(''));
521 | }
522 |
523 | // bad
524 | function foo() {
525 | console.log([].join.call(arguments));
526 | }
527 | ```
528 |
529 |
530 |
531 |
532 | ### 3.5 箭头函数
533 |
534 |
535 |
536 | #### [强制] 一个函数被设计为需要 `call` 和 `apply` 的时候,不能是箭头函数。
537 |
538 | 解释:
539 |
540 | 箭头函数会强制绑定当前环境下的 `this`。
541 |
542 |
543 |
544 | ### 3.6 对象
545 |
546 |
547 | #### [建议] 定义对象时,如果所有键均指向同名变量,则所有键都使用缩写;如果有一个键无法指向同名变量,则所有键都不使用缩写。
548 |
549 | 解释:
550 |
551 | 目的在于保持所有键值对声明的一致性。
552 |
553 | ```javascript
554 | // good
555 | let foo = {x, y, z};
556 |
557 | let foo2 = {
558 | x: 1,
559 | y: 2,
560 | z: z
561 | };
562 |
563 |
564 | // bad
565 | let foo = {
566 | x: x,
567 | y: y,
568 | z: z
569 | };
570 |
571 | let foo2 = {
572 | x: 1,
573 | y: 2,
574 | z
575 | };
576 | ```
577 |
578 | #### [强制] 定义方法时使用 `MethodDefinition` 语法,不使用 `PropertyName: FunctionExpression` 语法。
579 |
580 | 解释:
581 |
582 | `MethodDefinition` 语法更清晰简洁。
583 |
584 | 示例:
585 |
586 | ```javascript
587 | // good
588 | let foo = {
589 | bar(x, y) {
590 | return x + y;
591 | }
592 | };
593 |
594 | // bad
595 | let foo = {
596 | bar: function (x, y) {
597 | return x + y;
598 | }
599 | };
600 | ```
601 |
602 | #### [建议] 使用 `Object.keys` 或 `Object.entries` 进行对象遍历。
603 |
604 | 解释:
605 |
606 | 不建议使用 `for .. in` 进行对象的遍历,以避免遗漏 `hasOwnProperty` 产生的错误。
607 |
608 | 示例:
609 |
610 | ```javascript
611 | // good
612 | for (let key of Object.keys(foo)) {
613 | let value = foo[key];
614 | }
615 |
616 | // good
617 | for (let [key, value] of Object.entries(foo)) {
618 | // ...
619 | }
620 | ```
621 |
622 | #### [建议] 定义对象的方法不应使用箭头函数。
623 |
624 | 解释:
625 |
626 | 箭头函数将 `this` 绑定到当前环境,在 `obj.method()` 调用时容易导致不期待的 `this`。除非明确需要绑定 `this`,否则不应使用箭头函数。
627 |
628 | 示例:
629 |
630 | ```javascript
631 | // good
632 | let foo = {
633 | bar(x, y) {
634 | return x + y;
635 | }
636 | };
637 |
638 | // bad
639 | let foo = {
640 | bar: (x, y) => x + y
641 | };
642 | ```
643 |
644 |
645 | #### [建议] 尽量使用计算属性键在一个完整的字面量中完整地定义一个对象,避免对象定义后直接增加对象属性。
646 |
647 | 解释:
648 |
649 | 在一个完整的字面量中声明所有的键值,而不需要将代码分散开来,有助于提升代码可读性。
650 |
651 | 示例:
652 |
653 | ```javascript
654 | // good
655 | const MY_KEY = 'bar';
656 | let foo = {
657 | [MY_KEY + 'Hash']: 123
658 | };
659 |
660 | // bad
661 | const MY_KEY = 'bar';
662 | let foo = {};
663 | foo[MY_KEY + 'Hash'] = 123;
664 | ```
665 |
666 |
667 |
668 |
669 |
670 | ### 3.7 类
671 |
672 |
673 |
674 | #### [强制] 使用 `class` 关键字定义一个类。
675 |
676 | 解释:
677 |
678 | 直接使用 `class` 定义类更清晰。不要再使用 `function` 和 `prototype` 形式的定义。
679 |
680 | ```javascript
681 | // good
682 | class TextNode {
683 | constructor(value, engine) {
684 | this.value = value;
685 | this.engine = engine;
686 | }
687 |
688 | clone() {
689 | return this;
690 | }
691 | }
692 |
693 | // bad
694 | function TextNode(value, engine) {
695 | this.value = value;
696 | this.engine = engine;
697 | }
698 |
699 | TextNode.prototype.clone = function () {
700 | return this;
701 | };
702 | ```
703 |
704 | #### [强制] 使用 `super` 访问父类成员,而非父类的 `prototype`。
705 |
706 | 解释:
707 |
708 | 使用 `super` 和 `super.foo` 可以快速访问父类成员,而不必硬编码父类模块而导致修改和维护的不便,同时更节省代码。
709 |
710 | ```javascript
711 | // good
712 | class TextNode extends Node {
713 | constructor(value, engine) {
714 | super(value);
715 | this.engine = engine;
716 | }
717 |
718 | setNodeValue(value) {
719 | super.setNodeValue(value);
720 | this.textContent = value;
721 | }
722 | }
723 |
724 | // bad
725 | class TextNode extends Node {
726 | constructor(value, engine) {
727 | Node.apply(this, arguments);
728 | this.engine = engine;
729 | }
730 |
731 | setNodeValue(value) {
732 | Node.prototype.setNodeValue.call(this, value);
733 | this.textContent = value;
734 | }
735 | }
736 | ```
737 |
738 |
739 |
740 | ### 3.8 模块
741 |
742 |
743 |
744 | #### [强制] `export` 与内容定义放在一起。
745 |
746 | 解释:
747 |
748 | 何处声明要导出的东西,就在何处使用 `export` 关键字,不在声明后再统一导出。
749 |
750 | 示例:
751 |
752 | ```javascript
753 | // good
754 | export function foo() {
755 | }
756 |
757 | export const bar = 3;
758 |
759 |
760 | // bad
761 | function foo() {
762 | }
763 |
764 | const bar = 3;
765 |
766 | export {foo};
767 | export {bar};
768 | ```
769 |
770 | #### [建议] 相互之间无关联的内容使用命名导出。
771 |
772 | 解释:
773 |
774 | 举个例子,工具对象中的各个方法,相互之间并没有强关联,通常外部会选择几个使用,则应该使用命名导出。
775 |
776 | 简而言之,当一个模块只扮演命名空间的作用时,使用命名导出。
777 |
778 |
779 |
780 | #### [强制] 所有 `import` 语句写在模块开始处。
781 |
782 | 示例:
783 |
784 | ```javascript
785 | // good
786 | import {bar} from './bar';
787 |
788 | function foo() {
789 | bar.work();
790 | }
791 |
792 | // bad
793 | function foo() {
794 | import {bar} from './bar';
795 | bar.work();
796 | }
797 | ```
798 |
799 |
800 |
801 |
802 | ### 3.9 集合
803 |
804 |
805 | #### [建议] 对数组进行连接操作时,使用数组展开语法。
806 |
807 | 解释:
808 |
809 | 用数组展开代替 `concat` 方法,数组展开对 `Iterable` 有更好的兼容性。
810 |
811 | 示例:
812 |
813 | ```javascript
814 | // good
815 | let foo = [...foo, newValue];
816 | let bar = [...bar, ...newValues];
817 |
818 | // bad
819 | let foo = foo.concat(newValue);
820 | let bar = bar.concat(newValues);
821 | ```
822 |
823 | #### [建议] 不要使用数组展开进行数组的复制操作。
824 |
825 | 解释:
826 |
827 | 使用数组展开语法进行复制,代码可读性较差。推荐使用 `Array.from` 方法进行复制操作。
828 |
829 | 示例:
830 |
831 | ```javascript
832 | // good
833 | let otherArr = Array.from(arr);
834 |
835 | // bad
836 | let otherArr = [...arr];
837 | ```
838 |
839 | #### [建议] 尽可能使用 `for .. of` 进行遍历。
840 |
841 | 解释:
842 |
843 | 使用 `for .. of` 可以更好地接受任何的 `Iterable` 对象,如 `Map#values` 生成的迭代器,使得方法的通用性更强。
844 |
845 | 以下情况除外:
846 |
847 | 1. 遍历确实成为了性能瓶颈,需要使用原生 `for` 循环提升性能。
848 | 2. 需要遍历过程中的索引。
849 |
850 |
851 | #### [强制] 当键值有可能不是字符串时,必须使用 `Map`;当元素有可能不是字符串时,必须使用 `Set`。
852 |
853 | 解释:
854 |
855 | 使用普通 Object,对非字符串类型的 `key`,需要自己实现序列化。并且运行过程中的对象变化难以通知 Object。
856 |
857 |
858 | #### [建议] 需要一个不可重复的集合时,应使用 `Set`。
859 |
860 | 解释:
861 |
862 | 不要使用 `{foo: true}` 这样的普通 `Object`。
863 |
864 | 示例:
865 |
866 | ```javascript
867 | // good
868 | let members = new Set(['one', 'two', 'three']);
869 |
870 | // bad
871 | let members = {
872 | one: true,
873 | two: true,
874 | three: true
875 | };
876 | ```
877 |
878 |
879 | #### [建议] 当需要遍历功能时,使用 `Map` 和 `Set`。
880 |
881 | 解释:
882 |
883 | `Map` 和 `Set` 是可遍历对象,能够方便地使用 `for...of` 遍历。不要使用使用普通 Object。
884 |
885 | 示例:
886 |
887 | ```javascript
888 | // good
889 | let membersAge = new Map([
890 | ['one', 10],
891 | ['two', 20],
892 | ['three', 30]
893 | ]);
894 | for (let [key, value] of map) {
895 | }
896 |
897 | // bad
898 | let membersAge = {
899 | one: 10,
900 | two: 20,
901 | three: 30
902 | };
903 | for (let key in membersAge) {
904 | if (membersAge.hasOwnProperty(key)) {
905 | let value = membersAge[key];
906 | }
907 | }
908 | ```
909 |
910 | #### [建议] 程序运行过程中有添加或移除元素的操作时,使用 `Map` 和 `Set`。
911 |
912 | 解释:
913 |
914 | 使用 `Map` 和 `Set`,程序的可理解性更好;普通 Object 的语义更倾向于表达固定的结构。
915 |
916 | 示例:
917 |
918 | ```javascript
919 | // good
920 | let membersAge = new Map();
921 | membersAge.set('one', 10);
922 | membersAge.set('two', 20);
923 | membersAge.set('three', 30);
924 | membersAge.delete('one');
925 |
926 | // bad
927 | let membersAge = {};
928 | membersAge.one = 10;
929 | membersAge.two = 20;
930 | membersAge.three = 30;
931 | delete membersAge['one'];
932 | ```
933 |
934 |
935 |
936 |
937 | ### 3.10 异步
938 |
939 |
940 | #### [强制] 回调函数的嵌套不得超过3层。
941 |
942 | 解释:
943 |
944 | 深层次的回调函数的嵌套会让代码变得难以阅读。
945 |
946 | 示例:
947 |
948 | ```javascript
949 | // bad
950 | getUser(userId, function (user) {
951 | validateUser(user, function (isValid) {
952 | if (isValid) {
953 | saveReport(report, user, function () {
954 | notice('Saved!');
955 | });
956 | }
957 | });
958 | });
959 | ```
960 |
961 | #### [建议] 使用 `Promise` 代替 `callback`。
962 |
963 | 解释:
964 |
965 | 相比 `callback`,使用 `Promise` 能够使复杂异步过程的代码更清晰。
966 |
967 | 示例:
968 |
969 | ```javascript
970 | // good
971 | let user;
972 | getUser(userId)
973 | .then(function (userObj) {
974 | user = userObj;
975 | return validateUser(user);
976 | })
977 | .then(function (isValid) {
978 | if (isValid) {
979 | return saveReport(report, user);
980 | }
981 |
982 | return Promise.reject('Invalid!');
983 | })
984 | .then(
985 | function () {
986 | notice('Saved!');
987 | },
988 | function (message) {
989 | notice(message);
990 | }
991 | );
992 | ```
993 |
994 |
995 | #### [强制] 使用标准的 `Promise` API。
996 |
997 | 解释:
998 |
999 | 1. 不允许使用非标准的 `Promise` API,如 `jQuery` 的 `Deferred`、`Q.js` 的 `defer` 等。
1000 | 2. 不允许使用非标准的 `Promise` 扩展 API,如 `bluebird` 的 `Promise.any` 等。
1001 |
1002 | 使用标准的 `Promise` API,当运行环境都支持时,可以把 Promise Lib 直接去掉。
1003 |
1004 |
1005 | #### [强制] 不允许直接扩展 `Promise` 对象的 `prototype`。
1006 |
1007 | 解释:
1008 |
1009 | 理由和 **不允许修改和扩展任何原生对象和宿主对象的原型** 是一样的。如果想要使用更方便,可以用 utility 函数的形式。
1010 |
1011 |
1012 | #### [强制] 不得为了编写的方便,将可以并行的IO过程串行化。
1013 |
1014 | 解释:
1015 |
1016 | 并行 IO 消耗时间约等于 IO 时间最大的那个过程,串行的话消耗时间将是所有过程的时间之和。
1017 |
1018 | 示例:
1019 |
1020 | ```javascript
1021 | requestData().then(function (data) {
1022 | renderTags(data.tags);
1023 | renderArticles(data.articles);
1024 | });
1025 |
1026 | // good
1027 | async function requestData() {
1028 | const [tags, articles] = await Promise.all([
1029 | requestTags(),
1030 | requestArticles()
1031 | ]);
1032 | return {tags, articles};
1033 | }
1034 |
1035 | // bad
1036 | async function requestData() {
1037 | let tags = await requestTags();
1038 | let articles = await requestArticles();
1039 |
1040 | return Promise.resolve({tags, articles});
1041 | }
1042 | ```
1043 |
1044 | #### [建议] 使用 `async/await` 代替 `generator` + `co`。
1045 |
1046 | 解释:
1047 |
1048 | 使用语言自身的能力可以使代码更清晰,也无需引入 `co` 库。
1049 |
1050 | 示例:
1051 |
1052 | ```javascript
1053 | addReport(report, userId).then(
1054 | function () {
1055 | notice('Saved!');
1056 | },
1057 | function (message) {
1058 | notice(message);
1059 | }
1060 | );
1061 |
1062 | // good
1063 | async function addReport(report, userId) {
1064 | let user = await getUser(userId);
1065 | let isValid = await validateUser(user);
1066 |
1067 | if (isValid) {
1068 | let savePromise = saveReport(report, user);
1069 | return savePromise();
1070 | }
1071 |
1072 | return Promise.reject('Invalid');
1073 | }
1074 |
1075 | // bad
1076 | function addReport(report, userId) {
1077 | return co(function* () {
1078 | let user = yield getUser(userId);
1079 | let isValid = yield validateUser(user);
1080 |
1081 | if (isValid) {
1082 | let savePromise = saveReport(report, user);
1083 | return savePromise();
1084 | }
1085 |
1086 | return Promise.reject('Invalid');
1087 | });
1088 | }
1089 | ```
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 | ## 4 环境
1100 |
1101 |
1102 |
1103 |
1104 |
1105 |
1106 | ### 4.1 运行环境
1107 |
1108 |
1109 | #### [建议] 持续跟进与关注运行环境对语言特性的支持程度。
1110 |
1111 | 解释:
1112 |
1113 | [查看环境对语言特性的支持程度](https://kangax.github.io/compat-table/es6/)
1114 |
1115 | ES 标准的制定还在不断进行中,各种环境对语言特性的支持也日新月异。了解项目中用到了哪些 ESNext 的特性,了解项目的运行环境,并持续跟进这些特性在运行环境中的支持程度是很有必要的。这意味着:
1116 |
1117 | 1. 如果有任何一个运行环境(比如 chrome)支持了项目里用到的所有特性,你可以在开发时抛弃预编译。
1118 | 2. 如果所有环境都支持了某一特性(比如 Promise),你可以抛弃相关的 shim,或无需在预编译时进行转换。
1119 | 3. 如果所有环境都支持了项目里用到的所有特性,你可以完全抛弃预编译。
1120 |
1121 | 无论如何,在选择预编译工具时,你都需要清晰的知道你现阶段将在项目里使用哪些语言特性,然后了解预编译工具对语言特性的支持程度,做出选择。
1122 |
1123 |
1124 | #### [强制] 在运行环境中没有 `Promise` 时,将 `Promise` 的实现 `shim` 到 `global` 中。
1125 |
1126 | 解释:
1127 |
1128 | 当前运行环境下没有 `Promise` 时,可以引入 `shim` 的扩展。如果自己实现,需要实现在 `global` 下,并且与标准 API 保持一致。
1129 |
1130 | 这样,未来运行环境支持时,可以随时把 `Promise` 扩展直接扔掉,而应用代码无需任何修改。
1131 |
1132 |
1133 |
1134 |
1135 |
1136 | ### 4.2 预编译
1137 |
1138 |
1139 | #### [建议] 使用 `babel` 做为预编译工具时,建议使用 `5.x` 版本。
1140 |
1141 | 解释:
1142 |
1143 | 由于 `babel` 最新的 `6` 暂时还不稳定,建议暂时使用 `5.x`。不同的产品,对于浏览器支持的情况不同,使用 `babel` 的时候,需要设置的参数也有一些区别。下面在示例中给出一些建议的参数。
1144 |
1145 | 示例:
1146 |
1147 | ```shell
1148 | # 建议的参数
1149 | --loose all --modules amd --blacklist strict
1150 |
1151 | # 如果需要使用 es7.classProperties、es7.decorators 等一些特性,需要额外的 --stage 0 参数
1152 | --loose all --modules amd --blacklist strict --stage 0
1153 | ```
1154 |
1155 |
1156 | #### [建议] 使用 `babel` 做为预编译工具时,通过 `external-helpers` 减少生成文件的大小。
1157 |
1158 | 解释:
1159 |
1160 | 当 `babel` 在转换代码的过程中发现需要一些特性时,会在该文件头部生成对应的 `helper` 代码。默认情况下,对于每一个经由 `babel` 处理的文件,均会在文件头部生成对应需要的辅助函数,多份文件辅助函数存在重复,占用了不必要的代码体积。
1161 |
1162 | 因此推荐打开`externalHelpers: true`选项,使 `babel` 在转换后内容中不写入 `helper` 相关的代码,而是使用一个外部的 `.js`统一提供所有的 `helper`。对于[external-helpers](https://github.com/babel/babel.github.io/blob/5.0.0/docs/usage/external-helpers.md)的使用,可以有两种方式:
1163 |
1164 | 1. 默认方式:需要通过 `
2858 | ```
2859 |
2860 |
2861 | ##### [建议] 获取元素的直接子元素时使用 `children`。避免使用`childNodes`,除非预期是需要包含文本、注释和属性类型的节点。
2862 |
2863 |
2864 |
2865 |
2866 | #### 4.2.2 样式获取
2867 |
2868 |
2869 | ##### [建议] 获取元素实际样式信息时,应使用 `getComputedStyle` 或 `currentStyle`。
2870 |
2871 | 解释:
2872 |
2873 | 通过 style 只能获得内联定义或通过 JavaScript 直接设置的样式。通过 CSS class 设置的元素样式无法直接通过 style 获取。
2874 |
2875 |
2876 |
2877 |
2878 | #### 4.2.3 样式设置
2879 |
2880 |
2881 | ##### [建议] 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。
2882 |
2883 | ##### [强制] 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。
2884 |
2885 | 解释:
2886 |
2887 | 除了 IE,标准浏览器会忽略不规范的属性值,导致兼容性问题。
2888 |
2889 |
2890 |
2891 |
2892 | #### 4.2.4 DOM 操作
2893 |
2894 |
2895 | ##### [建议] 操作 `DOM` 时,尽量减少页面 `reflow`。
2896 |
2897 | 解释:
2898 |
2899 | 页面 reflow 是非常耗时的行为,非常容易导致性能瓶颈。下面一些场景会触发浏览器的reflow:
2900 |
2901 | - DOM元素的添加、修改(内容)、删除。
2902 | - 应用新的样式或者修改任何影响元素布局的属性。
2903 | - Resize浏览器窗口、滚动页面。
2904 | - 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 。
2905 |
2906 |
2907 | ##### [建议] 尽量减少 `DOM` 操作。
2908 |
2909 | 解释:
2910 |
2911 | DOM 操作也是非常耗时的一种操作,减少 DOM 操作有助于提高性能。举一个简单的例子,构建一个列表。我们可以用两种方式:
2912 |
2913 | 1. 在循环体中 createElement 并 append 到父元素中。
2914 | 2. 在循环体中拼接 HTML 字符串,循环结束后写父元素的 innerHTML。
2915 |
2916 | 第一种方法看起来比较标准,但是每次循环都会对 DOM 进行操作,性能极低。在这里推荐使用第二种方法。
2917 |
2918 |
2919 |
2920 |
2921 | #### 4.2.5 DOM 事件
2922 |
2923 |
2924 | ##### [建议] 优先使用 `addEventListener / attachEvent` 绑定事件,避免直接在 HTML 属性中或 DOM 的 `expando` 属性绑定事件处理。
2925 |
2926 | 解释:
2927 |
2928 | expando 属性绑定事件容易导致互相覆盖。
2929 |
2930 |
2931 | ##### [建议] 使用 `addEventListener` 时第三个参数使用 `false`。
2932 |
2933 | 解释:
2934 |
2935 | 标准浏览器中的 addEventListener 可以通过第三个参数指定两种时间触发模型:冒泡和捕获。而 IE 的 attachEvent 仅支持冒泡的事件触发。所以为了保持一致性,通常 addEventListener 的第三个参数都为 false。
2936 |
2937 |
2938 | ##### [建议] 在没有事件自动管理的框架支持下,应持有监听器函数的引用,在适当时候(元素释放、页面卸载等)移除添加的监听器。
2939 |
2940 |
2941 |
2942 |
--------------------------------------------------------------------------------