少寫程式碼
你最忽略的重要指標
所有程式碼都有錯誤。因此,你必須寫的程式碼越多,你的應用程式就會越容易出錯。
寫更多程式碼也會花費更多時間,導致沒有時間做其他事情,例如最佳化、錦上添花的功能,或者到戶外走走,而不是彎腰坐在筆電前。
事實上,人們普遍認為,隨著程式碼庫的大小增加,專案開發時間和錯誤數量會以平方而非線性方式成長。這與我們的直覺相符:一個十行的拉取請求會得到較少審查,而 100 行的則不然。一旦一個模組變得太大,無法容納在單一螢幕上時,理解它所需的認知努力就會顯著增加。我們會透過重構和新增註解來彌補——這些活動幾乎總是會導致更多程式碼。這是一個惡性循環。
然而,當我們理所當然地痴迷於效能數字、套件大小和任何我們可以測量的東西時,我們很少關注我們正在撰寫的程式碼數量。
可讀性很重要
我當然不是在宣稱我們應該使用巧妙的技巧,以犧牲可讀性為代價,將我們的程式碼壓縮成盡可能緊湊的形式。我也不認為減少程式碼行數必然是一個值得追求的目標,因為它會鼓勵將像這樣可讀的程式碼...
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
hook
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
函式,該函式會返回一個物件字面值,其屬性對應於我們的本地狀態。諸如 helper 函式和子元件之類的東西不能簡單地匯入並在模板中使用,而是必須透過將它們附加到預設匯出的正確部分來「註冊」。
告別樣板程式碼
這些只是 Svelte 幫助你以最少的麻煩建構使用者介面的方法之一。還有很多其他的方法——例如,反應式宣告($:
語句)本質上是在沒有樣板程式碼(或實際上在每次狀態變更時建立內聯函式和陣列的垃圾收集開銷)的情況下完成 React 的 useMemo
、useCallback
和 useEffect
的工作。
如何做到?透過選擇一組不同的約束。因為Svelte 是一個編譯器,我們不受 JavaScript 的特殊性的約束:我們可以設計元件編寫體驗,而不是必須使其適應語言的語義。矛盾的是,這會導致更慣用的程式碼——例如自然地使用變數,而不是透過代理或 hooks——同時提供效能顯著更高的應用程式。