编写更少的代码
你没有关注的最重要的指标
所有代码都存在错误。因此,可以推断,您编写的代码越多,您的应用程序就会越容易出错。
编写更多代码也需要更多时间,从而减少了用于其他事项的时间,例如优化、不错但非必须的功能,或者是在户外而不是驼背坐在笔记本电脑前。
事实上,人们普遍认为项目开发时间和错误数量随着代码库的大小呈二次方增长,而不是线性增长。这与我们的直觉相符:一个十行的拉取请求会得到很少应用于一百行拉取请求的审查级别。而且,一旦某个模块变得太大而无法在一个屏幕上显示,理解它所需的认知工作量就会大大增加。我们通过重构和添加注释来弥补——这些活动几乎总是导致更多代码。这是一个恶性循环。
然而,尽管我们(正确地!)痴迷于性能指标、包大小以及我们可以衡量的任何其他东西,但我们很少关注我们编写的代码量。
可读性很重要
我当然不是说我们应该使用巧妙的技巧将我们的代码压缩成尽可能紧凑的形式,而牺牲可读性。我也不是说减少代码行数必然是一个值得追求的目标,因为它鼓励将像这样的可读代码...
for (let let i: number
i = 0; let i: number
i <= 100; let i: number
i += 1) {
if (let i: number
i % 2 === 0) {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log(`${let i: number
i} is even`);
}
}
...转换成更难以解析的东西。
for (let let i: number
i = 0; let i: number
i <= 100; let i: number
i += 1) if (let i: number
i % 2 === 0) var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log(`${let i: number
i} is even`);
相反,我声称我们应该偏爱允许我们自然地编写更少代码的语言和模式。
是的,我指的是Svelte
减少您需要编写的代码量是Svelte的明确目标。为了说明这一点,让我们看看用React、Vue和Svelte实现的一个非常简单的组件。首先,是Svelte版本
我们如何在React中构建这个?它可能看起来像这样
import import React
React, { import useState
useState } from 'react';
export default () => {
const [const a: any
a, const setA: any
setA] = import useState
useState(1);
const [const b: any
b, const setB: any
setB] = import useState
useState(2);
function function (local function) handleChangeA(event: any): void
handleChangeA(event: any
event) {
const setA: any
setA(+event: any
event.target.value);
}
function function (local function) handleChangeB(event: any): void
handleChangeB(event: any
event) {
const setB: any
setB(+event: any
event.target.value);
}
return (
<type div = /*unresolved*/ any
div>
<type input = /*unresolved*/ any
input type="number" value={a: any
a} onChange={handleChangeA: (event: any) => void
handleChangeA} />
<type input = /*unresolved*/ any
input type="number" value={b: any
b} onChange={handleChangeB: (event: any) => void
handleChangeB} />
<type p = /*unresolved*/ any
p>
{a: any
a} + {b: any
b} = {const a: any
a + const b: any
b}
</p>
</div>
);
};
这是Vue中对应的组件
<template>
<div>
<input type="number" v-model.number="a">
<input type="number" v-model.number="b">
<p>{{a}} + {{b}} = {{a + b}}</p>
</div>
</template>
<script>
export default {
data: function() {
return {
a: 1,
b: 2
};
}
};
</script>
换句话说,在React中需要442个字符,在Vue中需要263个字符才能实现Svelte中只需145个字符就能实现的功能。React版本实际上大了三倍!
这种差异通常不会那么明显——根据我的经验,React组件通常比其Svelte等效组件大40%左右。让我们看看Svelte设计的哪些特性使您能够更简洁地表达想法。
顶层元素
在Svelte中,组件可以拥有任意数量的顶层元素。在React和Vue中,组件必须具有单个顶层元素——在React的情况下,尝试从组件函数返回两个顶层元素会导致语法无效的代码。(您可以使用片段——<>
——而不是<div>
,但基本原理相同,并且仍然会导致额外的缩进级别)。
在Vue中,您的标记必须包装在<template>
元素中,我认为这是多余的。
绑定
在React中,我们必须自己响应输入事件。
function function handleChangeA(event: any): void
handleChangeA(event: any
event) {
setA(+event: any
event.target.value);
}
这不仅仅是占用屏幕上额外空间的无聊管道,它也是错误的额外表面积。从概念上讲,输入的值绑定到a
的值,反之亦然,但这种关系没有被清晰地表达——相反,我们有两个紧密耦合但物理上分离的代码块(事件处理程序和value={a}
属性)。不仅如此,我们还必须记住使用+
运算符强制转换字符串值,否则2 + 2
将等于22
而不是4
。
与Svelte类似,Vue确实有一种表达绑定方式——v-model
属性,但同样,我们必须小心使用v-model.number
,即使它是一个数字输入。
状态
在Svelte中,您可以使用赋值运算符更新本地组件状态。
let let count: number
count = 0;
function function increment(): void
increment() {
let count: number
count += 1;
}
在React中,我们使用useState
钩子。
const [const count: any
count, const setCount: any
setCount] = useState(0);
function function increment(): void
increment() {
const setCount: any
setCount(const count: any
count + 1);
}
这要嘈杂得多——它表达了完全相同的概念,但字符却增加了60%以上。当您阅读代码时,您必须付出更多努力才能理解作者的意图。
同时,在Vue中,我们有一个默认导出,其中包含一个data
函数,该函数返回一个对象字面量,其属性对应于我们的本地状态。像辅助函数和子组件这样的东西不能简单地导入并在模板中使用,而必须通过将它们附加到默认导出的正确部分来“注册”。
消灭样板代码
这些只是Svelte帮助您以最少的麻烦构建用户界面的一些方法。还有很多其他的——例如,反应式声明($:
语句)基本上完成了React的useMemo
、useCallback
和useEffect
的工作,而无需样板代码(或者实际上是在每次状态更改时创建内联函数和数组的垃圾回收开销)。
如何做到这一点?通过选择一组不同的约束。因为Svelte是一个编译器,所以我们不受JavaScript特性的约束:我们可以设计组件创作体验,而不是必须使其适应语言的语义。矛盾的是,这会导致更惯用的代码——例如自然地使用变量而不是通过代理或钩子——同时提供性能明显更高的应用程序。