页面跳转
如为双端页面,需要自动判断跳转,PC访问移动端页面,跳转到对应的PC专题上,反之亦然。
PC端添加:
(function() {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
var a = document.referrer,
b = { 'baidu.com': 'seo_baidu', 'sogou.com': 'seo_sogou', 'sm.cn': 'seo_sm', 'so.com': 'seo_360', 'bing.com': 'seo_bing', 'google.com': 'seo_google' },
c
for (c in b) {
if (-1 != a.indexOf(c)) {
a = b[c]
if (window.sessionStorage) {
sessionStorage.setItem('channel', a)
} else {
var d = d || 0,
b = ''
0 != d && ((b = new Date()), b.setTime(b.getTime() + 1000 * d), (b = '; expires=' + b.toGMTString()))
document.cookie = 'channel=' + escape(a) + b + '; path=/'
}
break
}
}
window.location.href = '/m/' + location.search
}
})()
**** 注意**
window.location.href
跳转地址/m/
,默认为移动端官网地址;
如是专题或其他需求,需要根据实际情况将/m/
替换为相对应的移动端地址;
移动端添加:
if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) {
window.location.href = 'PC端专题地址' + location.search
}
HTML规范
- 标签必须合法且闭合、嵌套正确,标签名需小写
- 标签语法无错误,需要符合语义化
- 标签的自定义属性以
data-
开头,如:<a href="#" data-num='18'></a>
- 除非有特定的功能、组件要求等,禁止随意使用id来定义元素样式
类型属性
不需要为 CSS、JS 指定类型属性,HTML5 中默认已包含
推荐:
<link rel="stylesheet" href="" >
<script src=""></script>
不推荐:
<link rel="stylesheet" type="text/css" href="" >
<script type="text/javascript" src="" ></script>
元素属性
- 元素属性值使用双引号语法
- 元素属性值可以写上的都写上
推荐:
<input type="text">
<input type="radio" name="name" checked="checked" >
WebApp Meta
通用类设置
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
- width – viewport的宽度
- height – viewport的高度
- initial-scale – 初始的缩放比例
- minimum-scale – 允许用户缩放到的最小比例
- maximum-scale – 允许用户缩放到的最大比例
- user-scalable – 是否允许用户缩放
默认值
- 默认宽度是 980px,范围从 200px 到 10000px
- initial-scale 缩放比例范围值是 从 >0 到 10 之间
- minimum-scale 默认值是 0.25
- maximum-scale 默认值是 5
- user-scalable – 默认值是 yes,设置 no 还可以在文本框输入文本的时候阻止页面滚动
全屏模式
设置 WebApp 是否进入全屏模式,该设置需要添加到主屏幕才生效
<meta name="apple-mobile-web-app-capable" content="yes">
- content设置 yes 进入全屏模式
- 默认会启动 Safari 应用,使用 Safari 应用浏览
- 通过检测 window.navigator.standalone 的 Boolean 值可以判断 web 应用是否处于全屏模式
状态栏样式
<meta name="apple-mobile-web-app-status-bar-style" content="black">
- 此 meta 设置只在全屏模式生效
- 默认值是 default
- content=”black”,状态栏背景黑色,网页内容在状态栏下面
- content=”black-translucent”,状态栏半透明,背景黑色,网页内容占满全屏
特殊字符引用
文本可以和字符引用混合出现。这种方法可以用来转义在文本中不能合法出现的字符。
在 HTML 中不能使用小于号 “<” 和大于号 “>”特殊字符,浏览器会将它们作为标签解析,若要正确显示,在 HTML 源代码中使用字符实体
推荐:
<a href="#">more>></a>
不推荐:
<a href="#">more>></a>
更多关于符号引用:#Character references
代码嵌套
元素嵌套规范,每个元素独立一行
推荐:
<div>
<h1></h1>
<p></p>
</div>
链接
- 给
<a>
标签加上title属性 <a>
标签的href
属性必须写上链接地址,暂无的加上javascript:alert('敬请期待!')
- 非本专题的页面间跳转,采用打开新窗口模式:
target="_blank"
https协议自适应
如果页面加载使用http协议,则库文件将使用http加载,如果使用https加载页面,则库也将使用相同的协议
将调用静态域名 ossweb-img.qq.com
以及 game.gtimg.cn
的外部请求,写法上一律去掉协议http:
部分,采用自适应的写法。具体方法如下:
<style>
//CSS背景图片
.bg{background: url(//game.gtimg.cn/images/cf/cp/a20161021sqjs/hd.jpg) no-repeat;}
</style>
//链接URL
<a href="//cf.qq.com/main.shtml">进入官网</a>
//图片SRC
<img src="//game.gtimg.cn/images/cf/web201610/logo.png">
//JS链接
<script src="//ossweb-img.qq.com/images/js/title.js"></script>
flash
页面禁止使用flash,动画使用video、CSS3、canvas等方式实现,低版本浏览器使用背景图片降级。
JS 规范
const
和 let
- const 和 let 都是块级作用域,var 是函数级作用域
{
let a = 1
const b = 1
}
console.log(a) // a is not defined
console.log(b) // b is not defined
- 对所有引用都使用
const
,不要使用var
,eslint: prefer-const, no-const-assign
原因:这样做可以确保你无法重新分配引用,以避免出现错误和难以理解的代码
// bad
var a = 1
var b = 2
// good
const a = 1
const b = 2
- 如果引用是可变动的,使用
let
代替var
,eslint: no-var
原因:
let
是块级作用域的,而不像var
属于函数级作用域
for(var i=1; i<10; i++) {
var b =100;
console.log(i);
}
// 10 100
console.log(i,b);
// bad
var count = 1
if (count < 10) {
count += 1
}
// good
let count = 1
if (count < 10) {
count += 1
}
对象
- 请使用字面量值创建对象,eslint: no-new-object
// bad
let a = new Object{}
let a = {}
// good
let b = null
- 别使用保留字作为对象的键值,这样在 IE8 下不会运行
// bad
let a = {
default: {}, // default 是保留字
common: {}
}
// good
let a = {
defaults: {},
common: {}
}
- 当使用动态属性名创建对象时,请使用对象计算属性名来进行创建
原因:因为这样做就可以让你在一个地方定义所有的对象属性,便于维护
function getKey(k) {
return `a key named ${k}`
}
// bad
const obj = {
id: 5,
name: 'San Francisco'
};
obj[getKey('enabled')] = true
// good
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true
};
// {id: 5, name: 'San Francisco', a key named enabled: true}
- 请使用对象方法的简写方式,eslint: object-shorthand
// bad
const item = {
value: 1,
addValue: function (val) {
return item.value + val
}
}
// good
const item = {
value: 1,
addValue (val) {
return item.value + val
}
}
- 请使用对象属性值的简写方式,eslint: object-shorthand
原因:这样更简短且描述更清楚
const job = 'FrontEnd'
// bad
const item = {
job: job
}
// good
const item = {
job
}
- 将简写的对象属性分组后统一放到对象声明的开头
原因:这样更容易区分哪些属性用了简写的方式
const job = 'FrontEnd'
const department = 'JDC'
// bad
const item = {
sex: 'male',
job,
age: 25,
department
}
// good
const item = {
job,
department,
sex: 'male',
age: 25
}
- 只对非法标识符的属性使用引号,eslint: quote-props
原因:因为通常来说我们认为这样主观上会更容易阅读,这样会带来代码高亮上的提升,同时也更容易被主流 JS 引擎优化
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5
}
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5
}
- 不要直接使用
Object.prototype
的方法, 例如hasOwnProperty
,propertyIsEnumerable
和isPrototypeOf
方法,eslint: no-prototype-builtins
原因:这些方法可能会被对象自身的同名属性覆盖 - 比如
{ hasOwnProperty: false }
或者对象可能是一个null
对象(Object.create(null)
)
// bad
console.log(object.hasOwnProperty(key))
// good
console.log(Object.prototype.hasOwnProperty.call(object, key))
// best
const has = Object.prototype.hasOwnProperty // cache the lookup once, in module scope.
console.log(has.call(object, key))
/* or */
import has from 'has' // https://www.npmjs.com/package/has
console.log(has(object, key))
- 优先使用对象展开运算符
...
来做对象浅拷贝而不是使用Object.assign
,使用对象剩余操作符来获得一个包含确定的剩余属性的新对象
// very bad
const original = { a: 1, b: 2 }
// 会改变原始数据original {a: 1, b: 2, c: 3}
const copy = Object.assign(original, { c: 3 })
delete copy.c // so does this
// bad
const original = { a: 1, b: 2 }
const copy = Object.assign({}, original, { c: 3 }) // copy => { a: 1, b: 2, c: 3 }
// good
const original = { a: 1, b: 2 }
const copy = { ...original, c: 3 } // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy // noA => { b: 2, c: 3 }
数组
- 请使用字面量值创建数组,eslint: no-array-constructor
// bad
const items = new Array()
// good
const items = []
- 向数组中添加元素时,请使用
push
方法
const items = []
// bad
items[items.length] = 'test'
// good
items.push('test')
- 使用展开运算符
...
复制数组
// bad
const items = []
const itemsCopy = []
const len = items.length
let i
// bad
for (i = 0; i < len; i++) {
itemsCopy[i] = items[i]
}
// good
itemsCopy = [...items]
- 把一个可迭代的对象转换为数组时,使用展开运算符
...
而不是Array.from
const foo = document.querySelectorAll('.foo')
// good
const nodes = Array.from(foo)
// best
const nodes = [...foo]
- 使用
Array.from
来将一个类数组对象转换为数组
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }
// bad
const arr = Array.prototype.slice.call(arrLike)
// good
const arr = Array.from(arrLike)
// arr: ['foo', 'bar', 'baz']
- 使用数组的
map
等方法时,请使用return
声明,如果是单一声明语句的情况,可省略return
// good
[1, 2, 3].map(x => {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map(x => x + 1)
// bad
inbox.filter((msg) => {
const { subject, author } = msg
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
} else {
return false
}
})
// good
inbox.filter((msg) => {
const { subject, author } = msg
if (subject === 'Mockingbird') {
return author === 'Harper Lee'
}
return false
})
解构赋值
- 当需要使用对象的多个属性时,请使用解构赋值,eslint: prefer-destructuring
原因: 解构可以避免创建属性的临时引用
// bad
function getFullName (user) {
const firstName = user.firstName
const lastName = user.lastName
return `${firstName} ${lastName}`
}
// good
function getFullName (user) {
const { firstName, lastName } = user
return `${firstName} ${lastName}`
}
// better
function getFullName ({ firstName, lastName }) {
return `${firstName} ${lastName}`
}
- 当需要使用数组的多个值时,请同样使用解构赋值,eslint: prefer-destructuring
const arr = [1, 2, 3, 4]
// bad
const first = arr[0]
const second = arr[1]
// good
const [first, second] = arr
- 函数需要回传多个值时,请使用对象的解构,而不是数组的解构
原因:可以非破坏性地随时增加或者改变属性顺序
// bad
function doSomething () {
return [top, right, bottom, left]
}
// 如果是数组解构,那么在调用时就需要考虑数据的顺序
const [top, xx, xxx, left] = doSomething()
// good
function doSomething () {
return { top, right, bottom, left }
}
// 此时不需要考虑数据的顺序
const { top, left } = doSomething()
字符串
- 字符串统一使用单引号的形式
''
,eslint: quotes
// bad
const department = "JDC"
// good
const department = 'JDC'
- 字符串太长的时候,请不要使用字符串连接符换行 ``,而是使用
+
const str = '实验室实验室 实验实验室 实验实验室' +
'实验实验室 实验实验室 实验实验室' +
'实验实验室 实验实验室'
- 程序化生成字符串时,请使用模板字符串,eslint: prefer-template template-curly-spacing
const test = 'test'
// bad
const str = ['a', 'b', test].join()
// bad
const str = 'a' + 'b' + test
// good
const str = `ab${test}`
- 不要对字符串使用eval(),会导致太多漏洞, eslint: no-eval
eval()
是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用 eval()
运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval()
被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function
就不容易被攻击。
eval()
通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。
- 不要在字符串中使用不必要的转义字符, eslint: no-useless-escape
// bad
const foo = ''this' \i\s "quoted"'
// good
const foo = ''this' is "quoted"'
const foo = `my name is '${name}'`
函数
- 不要使用Function构造函数创建函数, eslint: no-new-func
原因:此方式创建函数和对字符串使用
eval()
一样会产生漏洞
// bad
const add = new Function('a', 'b', 'return a + b')
// still bad
const subtract = Function('a', 'b', 'return a - b')
- 在函数签名中使用空格,eslint: space-before-blocks
const f = function(){}
const g = function (){}
const h = function() {}
// good
const x = function b() {}
const y = function a() {}
- 使用具名函数表达式而非函数声明,eslint: func-style
原因:这样做会导致函数声明被提升,这意味着很容易在文件中定义此函数之前引用它,不利于可读性和可维护性。如果你发现函数定义既庞大又复杂以至于不能理解文件的其他部分,或许你应该将它拆分成模块!别忘记要显式命名表达式,而不用管名字是否是从包含的变量(通常出现在现代浏览器中或者使用 Babel 编译器的时候)中推断的。这样会消除错误调用堆栈中的任何假设。 (讨论)
// bad
function foo () {
// ...
}
// bad
const foo = function () {
// ...
}
// good
// lexical name distinguished from the variable-referenced invocation(s)
const short = function longUniqueMoreDescriptiveLexicalFoo () {
// ...
}
- 用圆括号包裹自执行匿名函数,eslint:wrap-iife
原因:一个立即执行匿名函数表达式是一个单一的单元,将其及其调用括号包装在括号中,能够清楚地表达这一点。
// immediately-invoked function expression (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.')
}())
- 不要在非函数代码块(
if
,while
等)中声明函数,eslint:no-loop-func
// bad
if (isUse) {
function test () {
// do something
}
}
// good
let test
if (isUse) {
test = () => {
// do something
}
}
- 不要将参数命名为
arguments
,会导致该参数的优先级高于每个函数作用域内原先存在的arguments
对象
// bad
function foo (name, options, arguments) {
// ...
}
// good
function foo (name, options, args) {
// ...
}
- 不要使用
arguments
,使用 剩余运算符...
arguments
只是一个类数组,而...
是一个真正的数组
// bad
function test () {
const args = Array.prototype.slice.call(arguments)
return args.join('')
}
// good
function test (...args) {
return args.join('')
}
- 使用参数默认值语法而不是修改函数参数
// really bad
function handleThings (opts) {
// No! We shouldn't mutate function arguments.
// Double bad: if opts is falsy it'll be set to an object which may
// be what you want but it can introduce subtle bugs.
opts = opts || {}
// ...
}
// still bad
function handleThings (opts) {
if (opts === void 0) {
opts = {}
}
// ...
}
// good
function handleThings (opts = { }) {
// ...
}
- 避免参数默认值的副作用
let b = 1
// bad
function count (a = b++) {
console.log(a)
}
count() // 1
count() // 2
count(3) // 3
count() // 3
- 将参数默认值放在最后
// bad
function handleThings (opts = {}, name) {
// ...
}
// good
function handleThings (name, opts = {}) {
// ...
}
- 不要更改参数,eslint: no-param-reassign
原因:操作作为参数传入的对象可能在原始调用中造成意想不到的变量副作用
// bad
function f1 (obj) {
obj.key = 1
}
// good
function f2 (obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1
}
- 不要给参数重新赋值,eslint: no-param-reassign
原因:参数重新赋值可能会导致无法预期的行为,尤其是当操作
arguments
对象时,也可能导致优化问题,尤其是在 V8 引擎中
// bad
function f1 (a) {
a = 1
}
function f2 (a) {
if (!a) { a = 1 }
}
// good
function f3 (a) {
const b = a || 1
}
function f4 (a = 1) {
}
- 调用可变参数函数时建议使用展开运算符
....
, eslint: prefer-spread
原因:显然你无需使用上下文,很难结合
new
和apply
// bad
const x = [1, 2, 3, 4, 5]
console.log.apply(console, x)
// good
const x = [1, 2, 3, 4, 5]
console.log(...x)
// bad
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]))
// good
new Date(...[2016, 8, 5])
箭头函数
- 当你必须使用函数表达式(传递匿名函数)时,使用箭头函数标记. eslint: prefer-arrow-callback, arrow-spacing
原因:它将创建在
this
上下文中执行的函数版本,通常是您想要的,并且语法更简洁.
如果您有一个相当复杂的函数,则可以将该逻辑移到其自己的命名函数表达式中
// bad
[1, 2, 3].map(function (x) {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map((x) => {
const y = x + 1
return x * y
})
- 如果函数体只包含一条没有副作用的返回表达式的语句,可以省略花括号并使用隐式的
return
, 否则保留花括号并使用return
语句,eslint: arrow-parens, arrow-body-style
// bad
[1, 2, 3].map(number => {
const nextNumber = number + 1
`A string containing the ${nextNumber}.`
})
// good
[1, 2, 3].map(number => `A string containing the ${number}.`)
// good
[1, 2, 3].map((number) => {
const nextNumber = number + 1
return `A string containing the ${nextNumber}.`
})
// good
[1, 2, 3].map((number, index) => ({
index: number
}))
let bool = false
// bad
foo(() => bool = true)
// good
foo(() => {
bool = true
})
- 一旦表达式跨多行,使用圆括号包裹以便更好阅读
// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod
)
)
// good
['get', 'post', 'put'].map(httpMethod => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod
)
))
- 函数如果只接收一个参数并且没使用用花括号,则省略圆括号,否则为了清晰明确则使用圆括号包裹参数,注意:总是使用圆括号也是可以接受的,eslint 中的 “always” 选项,eslint: arrow-parens
// bad
[1, 2, 3].map((x) => x * x)
// good
[1, 2, 3].map(x => x * x)
// good
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we’ve broken it ` +
'over multiple lines!'
))
// bad
[1, 2, 3].map(x => {
const y = x + 1
return x * y
})
// good
[1, 2, 3].map((x) => {
const y = x + 1
return x * y
})
类&构造函数
- 使用
class
,避免直接操作prototype
// bad
function Queue (contents = []) {
this._queue = [..contents]
}
Queue.prototype.pop = function () {
const value = this._queue[0]
this._queue.splice(0, 1)
return value
}
// good
class Queue {
constructor (contents = []) {
this._queue = [...contents]
}
pop () {
const value = this._queue[0]
this._queue.splice(0, 1)
return value
}
}
- 使用
extends
来实现继承
原因:这是一个不会破坏
instanceof
的内建实现原型式继承的方式
// bad
const inherits = require('inherits')
function PeekableQueue(contents) {
Queue.apply(this, contents)
}
inherits(PeekableQueue, Queue)
PeekableQueue.prototype.peek = function () {
return this.queue[0]
}
// good
class PeekableQueue extends Queue {
peek () {
return this.queue[0]
}
}
- 如果未声明构造函数,则类会有一个默认的构造函数,没必要用空的构造函数或者将其委托给父类,eslint: no-useless-constructor
// bad
class Jedi {
constructor () {}
getName() {
return this.name
}
}
// bad
class Rey extends Jedi {
constructor (...args) {
super(...args)
}
}
// good
class Rey extends Jedi {
constructor (...args) {
super(...args)
this.name = 'Rey'
}
}
- 避免类成员重复,eslint: no-dupe-class-members
原因:重复的类成员声明会默认使用最后声明的,通常会导致 bug
// bad
class Foo {
bar () { return 1 }
bar () { return 2 }
}
// good
class Foo {
bar () { return 1 }
}
// good
class Foo {
bar () { return 2 }
}
模块
- 使用标准的 ES6 模块语法
import
和export
// bad
const util = require('./util')
module.exports = util
// good
import Util from './util'
export default Util
// better
import { Util } from './util'
export default Util
- 不要使用
import
的通配符*
,这样可以确保你只有一个默认的 export
// bad
import * as Util from './util'
// good
import Util from './util'
- 同个文件每个模块只允许
import
一次,有多个import
请书写在一起,eslint: no-duplicate-imports
// bad
import foo from 'foo'
// … some other imports … //
import { named1, named2 } from 'foo'
// good
import foo, { named1, named2 } from 'foo'
// good
import foo, {
named1,
named2
} from 'foo'
- 将所有
import
语句放在文件最前方,eslint: import/imports-first
// bad
import foo from 'foo'
foo.init()
import bar from 'bar'
// good
import foo from 'foo'
import bar from 'bar'
foo.init()
- 多行导入应该一起
// bad
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE
} from 'path'
// good
import { longNameA, longNameB, longNameC, longNameD, longNameE } from 'path'
- 在模块
import
声明中禁止使用Webpack
的loader
语法,eslint: import/no-webpack-loader-syntax
// bad
import fooSass from 'css!sass!foo.scss'
import barCss from 'style!css!bar.css'
// good
import fooSass from 'foo.scss'
import barCss from 'bar.css'
迭代器
- 不要使用
iterators
,建议使用 JS 更高优先级的函数代替 for-in 或 for-of 循环,除非迫不得已,eslint: no-iterator no-restricted-syntax
const numbers = [1, 2, 3, 4, 5]
// bad
let sum = 0
for (let num of numbers) {
sum += num
}
// good
let sum = 0
numbers.forEach(num => sum += num)
// better
const sum = numbers.reduce((total, num) => total + num, 0)
生成器
- 现阶段请不要使用生成器
generator
原因:因为不能很好地翻译成 ES5 代码
对象属性
- 使用
.
来访问对象属性
const joke = {
name: 'haha',
age: 28
}
// bad
const name = joke['name']
// good
const name = joke.name
- 当访问的属性是变量时使用
[]
const luke = {
jedi: true,
age: 28,
}
function getProp (prop) {
return luke[prop]
}
const isJedi = getProp('jedi')
变量声明
- 声明变量时,请使用
const
、let
关键字
// bad
demo = new Demo()
// good
const demo = new Demo()
如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这里推荐使用 const
来声明变量,我们需要避免全局命名空间的污染。eslint: no-undef prefer-const
- 将所有的
const
和let
分组
// bad
let a
const b
let c
const d
let e
// good
const b
const d
let a
let c
let e
- 变量不要进行链式赋值
原因:变量链式赋值会创建隐藏的全局变量
// bad
(function example() {
// JavaScript interprets this as
// let a = ( b = ( c = 1 ) );
// The let keyword only applies to variable a; variables b and c become
// global variables.
let a = b = c = 1
}())
console.log(a) // throws ReferenceError
console.log(b) // 1
console.log(c) // 1
// good
(function example() {
let a = 1
let b = a
let c = a
}())
console.log(a) // throws ReferenceError
console.log(b) // throws ReferenceError
console.log(c) // throws ReferenceError
// the same applies for `const`
- 不允许出现未被使用的变量,eslint: no-unused-vars
原因:声明但未被使用的变量通常是不完全重构犯下的错误.这种变量在代码里浪费空间并会给读者造成困扰
// bad
var some_unused_var = 42
// Write-only variables are not considered as used.
var y = 10
y = 5
// A read for a modification of itself is not considered as used.
var z = 0
z = z + 1
// Unused function arguments.
function getX (x, y) {
return x
}
// good
function getXPlusY (x, y) {
return x + y
}
const x = 1
const y = a + 2
alert(getXPlusY(x, y))
// 'type' is ignored even if unused because it has a rest property sibling.
// This is a form of extracting an object that omits the specified keys.
const { type, ...coords } = data
// 'coords' is now the 'data' object without its 'type' property.
比较运算符&相等
-
使用
===
和!==
而非==
和!=
,eslint: eqeqeq -
条件声明例如
if
会用ToBoolean
这个抽象方法将表达式转成布尔值并遵循如下规则Objects
等于true
Undefined
等于false
Null
等于false
Booleans
等于布尔值
Numbers
在+0
,-0
, 或者NaN
的情况下等于false
, 其他情况是true
Strings
为''
时等于false
, 否则是true
if ([0] && []) {
// true
// 数组(即使是空数组)也是对象,对象等于true
}
分号
- 我们遵循
Standard
的规范,不使用分号。
关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好的
JS
程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。
// bad
const test = 'good';
(function () {
const str = 'hahaha';
})()
// good
const test = 'good'
;(() => {
const str = 'hahaha'
})();
with() {}
由于 with
方法会产生神奇的作用域,所以我们也是禁止使用该方法的
修改内置对象的原型
不要修改内置对象,如 Object
和 Array
CSS规范
代码格式化
展开格式
.jdc {
display: block;
width: 50px;
}
代码大小写
样式选择器,属性名,属性值关键字全部使用小写字母书写
分号
每个属性声明末尾都要加分号;
.jdc {
width: 100%;
height: 100%;
}
代码易读性
左括号与类名之间一个空格,冒号与属性值之间一个空格
.jdc {
width: 100%;
}
逗号分隔的取值,逗号之后一个空格
.jdc {
box-shadow: 1px 1px 1px #333, 2px 2px 2px #ccc;
}
颜色值 rgb()
rgba()
hsl()
hsla()
rect()
中不需有空格,且取值不要带有不必要的 0
.jdc {
color: rgba(255,255,255,.5);
}
属性值十六进制数值能用简写的尽量用简写
.jdc {
color: #fff;
}
不要为 0
指明单位
.jdc {
margin: 0 10px;
}
属性值引号
css属性值需要用到引号时,统一使用单引号
.jdc {
font-family: 'Hiragino Sans GB';
}
属性书写顺序
建议遵循以下顺序:
- 布局定位属性:display / position / float / clear / visibility / overflow
- 自身属性:width / height / margin / padding / border / background
- 文本属性:color / font / text-decoration / text-align / vertical-align / white- space / break-word
- 其他属性(CSS3):content / cursor / border-radius / box-shadow / text-shadow / background:linear-gradient …
.jdc {
display: block;
position: relative;
float: left;
width: 100px;
height: 100px;
margin: 0 10px;
padding: 20px 0;
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
color: #333;
background: rgba(0,0,0,.5);
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
-o-border-radius: 10px;
-ms-border-radius: 10px;
border-radius: 10px;
}
运算规范
运算符之间空出一个空格
.jdc {
width: 100px - 50px;
height: 30px / 5;
}
运算符之间空出一个空格
选择器
- CSS类名命名参考注释与命名
- 禁止使用层级过深的选择器,最多3级。
- 除非有特定的功能、组件要求等,禁止随意使用id来定义元素样式
- 除非是样式reset需要,禁止对纯元素选择器设置特定样式,避免样式污染
错误示范:
//会导致页面所有的a标签都被加上背景
a{background:url(xxx);}
//后期修改可能会添加一些span标签,如果刚好在div里面,会被污染;不利于后期维护
div span{display:block}
reset示例
PC端
body,dl,dd,ul,ol,h1,h2,h3,h4,h5,h6,p,form,header,section,article,footer{margin:0;}
body,button,input,select,textarea{font:12px/1.5 tahoma,'\5FAE\8F6F\96C5\9ED1',sans-serif}
h1,h2,h3,h4,h5,h6{font-size:100%}
em,b{font-style:normal}
a{text-decoration:none}
a:hover{text-decoration:underline}
img{border:0}
body{padding-top:42px}
button,input,select,textarea{font-size:100%;outline:none}
table{border-collapse:collapse;border-spacing:0}
td,th,ul,ol{padding:0}
移动端
有较多文字的文章类页面:
/* 移动端常用reset.css (文字版本) */
/* reset */
html,body,div,p,ul,li,dl,dt,dd,em,i,span,a,img,input,h1,h2,h3,h4,h5 {margin:0;padding:0}
a,img,input {border:none;}
body{font: 14px/1.75 -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;}
a {text-decoration:none;}
ul,li{list-style: none}
如果页面无文字,或者不希望文字被长按选中,可使用下面的reset;适合于大多数专题页面
/* 移动端常用reset.css (无文字版本) */
/* reset */
html,body,div,p,ul,li,dl,dt,dd,em,i,span,a,img,input,h1,h2,h3,h4,h5 {margin:0;padding:0}
a,img,input {border:none;}
body{font: 14px/1.75 -apple-system, "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-tap-highlight-color: rgba(0,0,0,0);}
a {text-decoration:none;}
ul,li{list-style: none}
a, img {-webkit-touch-callout: none; /* 禁止长按链接与图片弹出菜单 */}
html, body {
-webkit-user-select: none; /* 禁止选中文本(如无文本选中需求,此为必选项) */
user-select: none;
}
**** 提示**
移动端页面不需要设置微软雅黑、宋体等字体,终端浏览器字体取决于设备上的系统字体。
图片处理
本章将介绍图片的优化。
内容图
内容图多以商品图等照片类图片形式存在,颜色较为丰富,文件体积较大
- 优先考虑 JPEG 格式,条件允许的话优先考虑 WebP 格式
- 尽量不使用PNG格式,PNG8 色位太低,PNG24 压缩率低,文件体积大
背景图
背景图多为图标等颜色比较简单、文件体积不大、起修饰作用的图片
- PNG 与 GIF 格式,优先考虑使用 PNG 格式,PNG格式允许更多的颜色并提供更好的压缩率
- 图像颜色比较简单的,如纯色块线条图标,优先考虑使用 PNG8 格式,避免不使用 JPEG 格式
- 图像颜色丰富而且图片文件不太大的(40KB 以下)或有半透明效果的优先考虑 PNG24 格式
- 图像颜色丰富而且文件比较大的(40KB - 200KB)优先考虑 JPEG 格式
- 条件允许的,优先考虑 WebP 代替 PNG 和 JPEG 格式
图片优化
- 图片体积不能超过300K
- JPG图片必须压缩,一般80%品质即可,保证文字清晰
- 透明PNG图片必须使用压缩工具压缩后提供
- 上线的图片都应该经过压缩处理,压缩后的图片不应该出现肉眼可感知的失真区域
图片标签
- PC端img图片不需添加
width
、height
属性,alt
属性应该写上 - 移动端必须填写
alt
属性 - alt不能为无意义字符,需要能表现出图片的含义,如图片为道具图,则应该为道具的名称
合理切图
- 需要变动的文字禁止切到图片中,如果不确定是否需要变动,请咨询接口人
- 需要程序后台动态生成的图片,如道具图片、头像、奖品,必须单独切割出来
- 装饰性图片合并成精灵图/雪碧图,减少页面请求
图片地址分离(仅供参考,国际项目不分离)
专题完成,需进行图片地址分离
把图片放到ossweb-img
文件夹里面,图片引用的时候src为game.gtimg.cn
域名下的特定地址。
分离后示例:
Copy
/*分离前*/
.bg{background: url(ossweb-img/hd.jpg) no-repeat;}
/*分离后*/
.bg{background: url(//game.gtimg.cn/images/xiangqi/cp/a20150730avatar/hd.jpg) no-repeat;}
**** 提示**
图片必须分离;css文件、js文件可视情况,不是必须分离。
为了本地正常预览,分离前文件请使用相对路径或补全
http://
如何知道分离路径
如现在专题的是天天爱消除
的一个预约活动专题
,专题目录是a20090817booking
,那采取下列步骤将获得分离地址:
- 百度搜索官方域名,找到官网
http://peng.qq.com/
地址,其中peng
就是项目名的简写;确保一级域名是qq.com - 查看官网首页源码样式表中的图片的地址,确认其中含有上一步得到的项目名;否则请找接口人确认
- 图片分离地址示例:
//game.gtimg.cn/images/peng/cp/a20090817booking/
,其中peng
就是项目名的简写 - 分离地址只有项目名和专题名需要修改
- 图片跨域问题的解决:设置图片
crossOrigin
属性为Anonymous
,同时把图片域名game.gtimg.cn
换为ossweb-img.qq.com
注释与命名
本章包含页面的注释规范与文件命名、样式表类名命名规范。
注释
**** 提示** 可通过编辑器的语法高亮功能,确保HTML、CSS、JS文件中注释语法正确
单行注释
- 注释不能嵌套
注释内容前后各一个空格字符,注释位于要注释代码的上面,单独占一行
推荐:
<!-- Comment Text -->
<div>...</div>
页面头部需要添加制作时间等信息。
在head区域中,title标签下方的注释,按照下方格式分别加上页面设计、页面制作的公司名称,创建的年-月-日
<head>
<title>页面名称-产品中文全称-官方网站-腾讯游戏-产品slogan</title>
<!-- 页面设计:公司名称 | 页面制作:公司名称 | 创建:2017-09-09 -->
</head>
页面设计、页面制作填写具体公司名称,创建:填写页面创建的时间。
禁止注释中出现制作者的个人信息,如姓名、QQ号、邮箱等。
合理的注释有助于后期维护。
较长的的HTML文件,请在板块之间加入注释,使得结构更清晰:
模块注释
注释内容前后各一个空格字符,<!-- S Comment Text -->
表示模块开始,<!-- E Comment Text -->
表示模块结束,模块与模块之间相隔一行
推荐写法:
<!-- S 活动板块 -->
<div class="mod_a">
...
</div>
<!-- E 活动板块 -->
如果是需要和后台开发联调的自定义函数。请注明函数的调用方式,包括函数的用途、参数类型等。
// 转盘初始化
// 参数1:是奖品的个数,数字类型
// 参数2:用来旋转的圆盘元素,可为dom元素 或 选择器字符串
var panel=new PanelLotery({
length:8,
el:'#ltpanel'
});
命名
目录命名
- 项目文件夹:projectname
- 样式文件夹:css
- 脚本文件夹:js
- 样式类图片文件夹:img
样式命名
-
class、id都需小写
-
命名使用英文,禁止使用特殊字符
-
样式名不能包含
ad
、guanggao
、ads
、gg
是广告含义的关键词,避免元素被网页拓展、插件屏蔽 -
名称间隔使用
-
符号 -
涉及数据、交互等需要联调的部分,禁止通过id选择器定义样式,以免开发过程中id名变化,引起页局错乱
-
类名命名需要语义化,参考下面的示例:
.wrap{} //外层容器 .mod-box{} //模块容器 .btn-start{} //开始 .btn-download-ios{} //ios下载 .btn-download-andriod{} //安卓下载 .btn-head-nav1{} //头部导航链接1 .btn-news-more{} //更多新闻 .btn-play{} //播放视频 .btn-ico{} //按钮ico .btn-lottery{} //开始抽奖 .side-nav{} //侧栏导航 .side-nav-btn1{} //侧栏导航-链接1 .btn-more{} //更多
**** 提示** 命名词穷了怎么办?试下这个工具:codelf
图片命名
-
图片名称必须小写,禁止使用特殊字符、中文
-
使用英文或拼音缩写,禁止特殊字符
-
名称间隔使用
-
符号 -
命名需要能体现图片的大概用途
常用示例:
Copy
bg.jpg //背景图片 mod-bg.jpg //模块背景 sprites.png //精灵图 btn-start.png //开始按钮 ico-play.png //修饰性图片
-
禁止文件名和实际图片内容不符。反面例子:图片名为’weixin-qrcode’,图片内容却是头像。
文件命名
文件命名参照文件命名详细说明
文件目录
文件命名
所有文件名统一使用小写,首页命名为index.html
html内页,有明显分类的,参考使用以下示例命名,无明确意义的,可用page01.html、page02.html,最多以下划线连接,禁止使用他标点符号,统一使用小写字母
常用命名:
专题名称 | 描述 |
---|---|
index.html | 引导页&首页 |
main.html | 首页 |
news.html | 资讯页 |
newsdetail.html | 资讯详情页 |
raiders.html | 攻略页 |
raidersdetail.html | 攻略页详情页 |
download.html | 下载页面 |
actlist.html | 活动列表页面 |
video.html | 视频 |
cdkey.html | CDKEY兑换页 |
wallpaper.html | 壁纸页面 |
文件模版
HTML模版指的是团队使用的初始化HTML文件,里面会根据不同平台而采用不一样的设置,一般主要不同的设置就是 mata 标签的设置,以下是 PC 和移动端的 HTML 模版。
HTML5标准模版
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>HTML5标准模版</title>
</head>
<body>
</body>
</html>
移动端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no" />
<meta name="format-detection" content="telephone=no" />
<title>移动端HTML模版</title>
<!-- S DNS预解析 -->
<link rel="dns-prefetch" href="" />
<!-- E DNS预解析 -->
<!-- S 线上样式页面片,开发请直接取消注释引用 -->
<!-- #include virtual="" -->
<!-- E 线上样式页面片 -->
<!-- S 本地调试,根据开发模式选择调试方式,请开发删除 -->
<link rel="stylesheet" href="css/index.css" />
<!-- /本地调试方式 -->
<link rel="stylesheet" href="http://srcPath/index.css" />
<!-- /开发机调试方式 -->
<!-- E 本地调试 -->
</head>
<body></body>
</html>
PC端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="keywords" content="your keywords" />
<meta name="description" content="your description" />
<meta name="author" content="author,email address" />
<meta name="robots" content="index,follow" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
<meta name="renderer" content="ie-stand" />
<title>PC端HTML模版</title>
<!-- S DNS预解析 -->
<link rel="dns-prefetch" href="" />
<!-- E DNS预解析 -->
<!-- S 线上样式页面片,开发请直接取消注释引用 -->
<!-- #include virtual="" -->
<!-- E 线上样式页面片 -->
<!-- S 本地调试,根据开发模式选择调试方式,请开发删除 -->
<link rel="stylesheet" href="css/index.css" />
<!-- /本地调试方式 -->
<link rel="stylesheet" href="http://srcPath/index.css" />
<!-- /开发机调试方式 -->
<!-- E 本地调试 -->
</head>
<body></body>
</html>