在一般的项目配置中,通常会使用eslint和stylelint来规范代码质量。除了解决格式问题,还有一些需要注意的代码写法误区和需要注意的地方。翻了几天的stylelint,eslint的官网,再加上我之前的项目的一些东西,总结出了这篇文章。欢迎讨论
eslint (JavaScript)
一目了然的配置:
ruby
{
"constructor-super": "error", // 强制要求构造函数中的super()调用
"no-cond-assign" : "error", // 不允许在条件语句中使用赋值运算符
"no-func-assign":"error", // 禁止重新分配函数
"no-this-before-super":"error", // 禁止在super构造函数中使用this之前使用
"no-unreachable":"error", // 禁止在return、throw、continue、break语句之后出现不可达代码
"eqeqeq":"error" // 强制使用全等(===)比较
}
10.5.1 require-atomic-updates | 重要
错误示例如下:
javascript
let Result = 0;
let getResult = (time)=>{
return new Promise((resolve)=>{
setTimeout((time)=>{
resolve(time)
},time,time)
})
}
async function addResult(pageNum) {
Result += await getResult(pageNum); // 错误的输出 输出 1000
}
Promise.all([addResult(500),addResult(1000)]).then(() => {
console.log('Count:', Result);
});
我们可以看到一个现象,我想在 Promise.all
中将 500 和 1000加起来。但是返回结果输出了1000。原因是新值在未被读取的情况下被覆盖,下面一开始的result就是直接被覆盖了。解决的方法是将await
放在前面,将函数调用放在前面。另外需要小心使用+=
,包括yield
也绝不能放在后面。
javascript
let Result = 0;
let getResult = (time)=>{
return new Promise((resolve)=>{
setTimeout((time)=>{
resolve(time)
},time,time)
})
}
async function addResult(pageNum) {
// 正确的输出 输出 1500
Result = await getResult(pageNum) + Result;
}
Promise.all([addResult(500),addResult(1000)]).then(() => {
console.log('Count:', Result);
});
10.5.2 no-await-in-loop
简单来说,尽量不要在循环中使用await
,除非你的循环迭代实际上是相互独立的,或者一次迭代的输出可以用作另一次迭代的输入,或者循环用于重试不成功的异步操作,或者用于防止代码并行发送过多的请求。在这些情况下,使用await
在循环中是有意义的。
10.5.3 no-compare-neg-zero
这个规则的主要原因是JavaScript中的一个特性,即 +0 === -0
相等。因此,当遇到这种情况时,最好使用 Object.is()
来进行比较。
10.5.4 no-constructor-return
不允许在JavaScript的类构造函数中返回值,否则在使用new
时将得到一个空对象。
10.5.5 no-unsafe-finally
在finally
块中,如果有return
或者throw
语句,它们会先执行。这是由于JavaScript的特性,所以需要注意在finally
中使用return
的特性。
javascript
(() => {
try {
console.log(33)
return 1; // 输出33
} catch(err) {
return 2;
} finally {
throw 3; // 输出3
}
})();
10.5.6 no-fallthrough
arduino
case a:
do
case b
do
case c
do
有些人可能会以为用 case
来写的话,a成立就不会继续执行了,但是事实是 case
是如果 不加上 break
是能够一直执行下去的。这会造成无谓的性能损耗和不可预料的后果。因此最好加上break
10.5.7 no-new-symbol
要使用Symbol
来创建Symbol
,而不是使用new Symbol
。
10.5.8 no-empty-pattern
当使用解构时,有可能创建一个没有效果的模式。当嵌入对象解构模式右侧使用空花括号时,会发生这种情况。下面是示例:
ini
const foo = {c: 232323}
var {b= {}} = foo;
console.log(b)
10.5.9 no-setter-return
就是说 像是 对象里面的 set 方法不要 返回值,因为 返回值没有用。一般就是 this.xxx = xxx
普通 变量 foo.a 的 时候 会触发 set
方法
ts
let foo = {
set a(value) {
this.val = value;
return value;
}
};
class 里面 也一样 静态属性是
ts
class a{
static set a(value) {
this.val = value;
}
};
但是特别需要注意一点。proxy 是必须要 return 值回来的。正确写法是类似这样
ts
let b = new Proxy(store, {
get(...args) {
return Reflect.get(...args)
},
set(...args) {
Reflect.set(...args)
},
})
10.5.10 no-prototype-builtins | guard-for-in
no-prototype-builtins
规则不允许直接在对象实例上调用某些方法。主要针对的对象是指node作为服务器的对象。我们知道一个对象中有着hasOwnProperty
这个属性,但是如果我们返回的数据里面有这样一个对象:
bash
let arr = {
"hasOwnProperty": "为了防止这些客户端搞事情导致服务器崩溃",
"id": 5
}
但是如果我们要是不知道这个东西而调用arr.hasOwnProperty("id")
这个方法,肯定会报错hasOwnProperty is not a function
。因此在guard-for-in
规则下需要这样写:
ini
var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");
10.5.11 dot-notation | 点符号 -属性 | 变量-方括号
可以使用点符号 (foo.bar
) 或方括号符号 (foo["bar"]
) 访问属性。然而,点表示法通常是首选,因为它更容易阅读、更简洁。但是如果属性名是一个变量,就必须使用方括号符号。
10.5.12 prefer-promise-reject-errors
对于Promises中用户定义的错误,仅将内置Error
对象的实例传递给函数被认为是良好的做法。对象自动存储堆栈跟踪,可用于通过确定错误的来源来调试错误。如果Promise因无值而被拒绝,则可能很难确定拒绝发生在哪里。
javascript
Promise.reject(new Error("something bad happened"));
10.5.13 prefer-rest-params
这个规则的原因是,ES2015引入了剩余参数(rest parameters)来更方便地处理可变参数的函数。相比之下,使用arguments
存在一些不足之处。
以下是使用剩余参数替代arguments
的优点:
- 明确性:剩余参数允许你在函数声明中显式地定义参数,提高了代码的可读性和可维护性。相比之下,
arguments
是一个隐式对象,并且没有明确的方式来知道参数的数量或含义。 - 数组功能:剩余参数将可变参数作为数组提供,使你能够直接使用数组相关的方法,如
forEach
、map
、reduce
等。这让代码更加简洁和语义清晰。 - 参数数量与限制:使用剩余参数,你可以更容易地对参数数量进行限制和验证,例如使用解构赋值来确保函数参数的结构。而
arguments
对象没有提供内置的方法来访问或验证参数。 - 函数箭头和匿名函数:在箭头函数和匿名函数中,不能使用
arguments
对象,但可以使用剩余参数。
javascript
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 输出: 10
stylelint (CSS)
以下是stylelint中一些规则的描述:
json
{
"property-no-unknown": true, // 不允许未知的属性
"selector-anb-no-unmatchable": true, // 伪类给你一个0或者0n,但是css选择器是0往上面走的
"declaration-block-no-shorthand-property-overrides": true, // 速记属性覆盖padding-left: 10px; padding: 20px;一下子就被覆盖了
"no-invalid-position-at-import-rule": true // @import放在最顶上
}
10.6.1 function-calc-no-unspaced-operator
此规则检查calc
中运算符之前是否有单个空格或换行符加缩进-
,以及该运算符之后是否有单个空格或换行符。
css
a {
top: calc(1px+ 2px);
}
这样是错的,注意一下运算符号:
css
top: calc(1px + 2px);
这样就对了,否则calc
不会执行。
10.6.2 keyframe-declaration-no-important
css
@keyframes foo {
from {
opacity: 0;
}
to {
opacity: 1 !important;
}
}
在keyframe中不应该使用!important
,因为它可能会导致未知的问题。
10.6.3 media-query-no-invalid
下面的是错误的:
scss
@media (width == 100px) {
}
@media not(min-width: 300px) {
}
这是正确的:
scss
@media (width = 100px) {
}
@media (width: 100px) {
}
@media not (min-width: 300px) {
}
规则如下:
- 只有
=
、>=
、<=
,没有==
not
前后需要空格
10.6.4 declaration-block-no-redundant-longhand-properties
在样式中一些属性有简写属性,尽量使用简写属性。例如,flex-flow
属性值可以是 row wrap
,它合并了flex-direction
和flex-wrap
的写法。以下是一些示例:
css
/* 定位和边距示例 */
.position-example {
position: absolute;
inset: 20px;
background-color: #f0f0f0;
padding: 10px;
border: 1px solid #333;
}
/* 背景示例 */
.background-example {
background: #f0f0f0 url("example-background.png") no-repeat center;
height: 200px;
width: 300px;
padding: 20px;
}
/* 字体示例 */
.font-example {
font: italic bold 16px Arial, sans-serif;
padding: 10px;
}
/* 边框示例 */
.border-example {
border: 2px dashed #999;
border-top: 1px solid #333;
border-radius: 10px;
padding: 20px;
}
/* 过渡效果示例 */
.transition-example {
width: 200px;
height: 100px;
background-color: #f0f0f0;
transition: width 0.5s ease-in-out;
}
.transition-example:hover {
width: 300px;
}
/* 动画示例 */
@keyframes example-animation {
0% {
transform: scale(1);
background-color: #f0f0f0;
}
100% {
transform: scale(1.2);
background-color: #ffcc00;
}
}
.animation-example {
width: 100px;
height: 100px;
background-color: #f0f0f0;
animation: example-animation 2s infinite alternate;
}
/* 弹性布局示例 */
.flex-example {
display: flex;
flex-flow: row wrap; /* flex-direction, flex-wrap */
justify-content: space-between;
align-items: center;
}
.flex-item {
width: 100px;
height: 100px;
background-color: #f0f0f0;
margin: 10px;
}
/* 网格布局示例 */
.grid-example {
display: grid;
grid-template: 100px 100px / 1fr 1fr;
grid-gap: 10px;
padding: 10px;
}
.grid-item {
background-color: #f0f0f0;
padding: 20px;
}
/* 文字修饰示例 */
.text-decoration-example {
text-decoration: underline overline wavy red;
padding: 10px;
}
/* 文字着重号示例 */
.text-emphasis-example {
font-size: 24px;
text-emphasis: open circle gold;
padding: 10px;
}
/* 遮罩示例 */
.mask-example {
width: 200px;
height: 200px;
background-image: url("mask-image.png");
background-size: cover;
mask: url("mask-image.png") center / contain;
}
10.6.5 no-invalid-position-at-import-rule
任何@import
规则都必须位于样式表中所有其他有效的at
规则和样式规则之前(忽略@charset
和@layer
),否则该@import
规则无效。