跳至主要内容

编写更少的代码

你没有关注的最重要的指标

所有代码都存在错误。因此,可以推断,您编写的代码越多,您的应用程序就会越容易出错。

编写更多代码也需要更多时间,从而减少了用于其他事项的时间,例如优化、不错但非必须的功能,或者是在户外而不是驼背坐在笔记本电脑前。

事实上,人们普遍认为项目开发时间错误数量随着代码库的大小呈二次方增长,而不是线性增长。这与我们的直觉相符:一个十行的拉取请求会得到很少应用于一百行拉取请求的审查级别。而且,一旦某个模块变得太大而无法在一个屏幕上显示,理解它所需的认知工作量就会大大增加。我们通过重构和添加注释来弥补——这些活动几乎总是导致更多代码。这是一个恶性循环。

然而,尽管我们(正确地!)痴迷于性能指标、包大小以及我们可以衡量的任何其他东西,但我们很少关注我们编写的代码量。

可读性很重要

我当然不是说我们应该使用巧妙的技巧将我们的代码压缩成尽可能紧凑的形式,而牺牲可读性。我也不是说减少代码行数必然是一个值得追求的目标,因为它鼓励将像这样的可读代码...

for (let let i: numberi = 0; let i: numberi <= 100; let i: numberi += 1) {
	if (let i: numberi % 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
@seesource
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.

@sincev0.1.100
log
(`${let i: numberi} is even`);
} }

...转换成更难以解析的东西。

for (let let i: numberi = 0; let i: numberi <= 100; let i: numberi += 1) if (let i: numberi % 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
@seesource
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.

@sincev0.1.100
log
(`${let i: numberi} is even`);

相反,我声称我们应该偏爱允许我们自然地编写更少代码的语言和模式。

是的,我指的是Svelte

减少您需要编写的代码量是Svelte的明确目标。为了说明这一点,让我们看看用React、Vue和Svelte实现的一个非常简单的组件。首先,是Svelte版本

我们如何在React中构建这个?它可能看起来像这样

import import ReactReact, { import useStateuseState } from 'react';

export default () => {
	const [const a: anya, const setA: anysetA] = import useStateuseState(1);
	const [const b: anyb, const setB: anysetB] = import useStateuseState(2);

	function function (local function) handleChangeA(event: any): voidhandleChangeA(event: anyevent) {
		const setA: anysetA(+event: anyevent.target.value);
	}

	function function (local function) handleChangeB(event: any): voidhandleChangeB(event: anyevent) {
		const setB: anysetB(+event: anyevent.target.value);
	}

	return (
		<type div = /*unresolved*/ anydiv>
			<type input = /*unresolved*/ anyinput type="number" value={a: anya} onChange={handleChangeA: (event: any) => voidhandleChangeA} />
			<type input = /*unresolved*/ anyinput type="number" value={b: anyb} onChange={handleChangeB: (event: any) => voidhandleChangeB} />

			<type p = /*unresolved*/ anyp>
				{a: anya} + {b: anyb} = {const a: anya + const b: anyb}
			</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): voidhandleChangeA(event: anyevent) {
	setA(+event: anyevent.target.value);
}

这不仅仅是占用屏幕上额外空间的无聊管道,它也是错误的额外表面积。从概念上讲,输入的值绑定到a的值,反之亦然,但这种关系没有被清晰地表达——相反,我们有两个紧密耦合但物理上分离的代码块(事件处理程序和value={a}属性)。不仅如此,我们还必须记住使用+运算符强制转换字符串值,否则2 + 2将等于22而不是4

与Svelte类似,Vue确实有一种表达绑定方式——v-model属性,但同样,我们必须小心使用v-model.number,即使它是一个数字输入。

状态

在Svelte中,您可以使用赋值运算符更新本地组件状态。

let let count: numbercount = 0;

function function increment(): voidincrement() {
	let count: numbercount += 1;
}

在React中,我们使用useState钩子。

const [const count: anycount, const setCount: anysetCount] = useState(0);

function function increment(): voidincrement() {
	const setCount: anysetCount(const count: anycount + 1);
}

这要嘈杂得多——它表达了完全相同的概念,但字符却增加了60%以上。当您阅读代码时,您必须付出更多努力才能理解作者的意图。

同时,在Vue中,我们有一个默认导出,其中包含一个data函数,该函数返回一个对象字面量,其属性对应于我们的本地状态。像辅助函数和子组件这样的东西不能简单地导入并在模板中使用,而必须通过将它们附加到默认导出的正确部分来“注册”。

消灭样板代码

这些只是Svelte帮助您以最少的麻烦构建用户界面的一些方法。还有很多其他的——例如,反应式声明($:语句)基本上完成了React的useMemouseCallbackuseEffect的工作,而无需样板代码(或者实际上是在每次状态更改时创建内联函数和数组的垃圾回收开销)。

如何做到这一点?通过选择一组不同的约束。因为Svelte是一个编译器,所以我们不受JavaScript特性的约束:我们可以设计组件创作体验,而不是必须使其适应语言的语义。矛盾的是,这会导致惯用的代码——例如自然地使用变量而不是通过代理或钩子——同时提供性能明显更高的应用程序。