React vs Svelte
點擊上方關(guān)注 前端技術(shù)江湖,一起學(xué)習(xí),天天進步

翻譯 | 紅薯 出品 | OSC開源社區(qū)(ID:oschina2013)
在 JavaScript 前端開發(fā)框架中,Svelte 算是一個新來的攪局者,在網(wǎng)上我們已經(jīng)聽到很多關(guān)于 Svelte 的嗶嗶。因此我決定試試這個家伙,順便跟 React 做個簡單的比較。
本文將展示 Svelte 和 React 在構(gòu)建一個基礎(chǔ)應(yīng)用的差異,其中涉及到的內(nèi)容包括:
組件結(jié)構(gòu) 狀態(tài)初始化 屬性傳遞 狀態(tài)向上傳遞 事件偵聽 動態(tài)樣式
還有很多其他方面的內(nèi)容需要討論,例如 按需渲染 和 生命周期 等其他炫酷的概念。限于篇幅,這篇文章還是聚焦在基礎(chǔ)使用上吧。
「準(zhǔn)備工作」
在繼續(xù)往下閱讀之前,你應(yīng)該準(zhǔn)備好如下環(huán)境:
npm 或者 yarn node.js 如果你用 Visual Studio Code 開發(fā),可以裝一個 Svelte 插件。
「Svelte 與 React」
Svelte 和 React.js 兩者都是基于組件的 JavaScript 框架,主要用于 Web 應(yīng)用的開發(fā)。最主要的區(qū)別是 Svelte 沒有使用虛擬 DOM。Svelte 在構(gòu)建的時候就將代碼編譯成 Vanilla JS 代碼,而 React 在運行時解釋代碼。
Svelte 文檔寫道:
?Svelte 是一種全新的構(gòu)建 Web 應(yīng)用的方法。諸如 React 和 Vue 這類傳統(tǒng)的框架,它們的大部分工作都在瀏覽器上執(zhí)行,而 Svelte 在構(gòu)建應(yīng)用的過程做就了大量的工作。
?Svelte 沒有使用虛擬 DOM 技術(shù),而是當(dāng)應(yīng)用狀態(tài)發(fā)生變化時,通過代碼如手術(shù)般的更新 DOM。?
酷!但是這些底層的細節(jié)對我來說并不重要。我只想從開發(fā)人員的角度看看,在使用 Svelte 和 React 開發(fā)應(yīng)用程序時,感覺好嗎?有趣嗎?直觀嗎?
開工!
「創(chuàng)建應(yīng)用腳手架」
在這篇文章中,我們將創(chuàng)建一個很小的 Web 應(yīng)用,產(chǎn)品經(jīng)理給這個應(yīng)用確定了如下需求:
三個組件,分別是:App 、Heading 和 Button 當(dāng)點擊 Button 時,Heading 會更新顯示點擊的次數(shù) 每次點擊 Button 時,Button 自身的顏色會跟著變化
首先使用如下命令在你電腦上創(chuàng)建一個新的目錄,暫且命名為 svelte-react:
mkdir svelte-react
cd svelte-react
接著分別創(chuàng)建 Svelte 和 React 的應(yīng)用模板并運行。這里 Svelte 的初始步驟比 React 多了一步,此外 Svelte 默認(rèn)端口是 5000 ,而 React 是 3000 。
「Svelte」
打開終端窗口,運行如下命令:
npx degit sveltejs/template svelte-test
cd svelte-test
npm install
npm run dev
「React」
打開第二個終端窗口,進入剛建好的 svelte-react 目錄,運行命令:
npx create-react-app react-test
cd svelte-react
npm start
你會發(fā)現(xiàn) Svelte 的命令運行快得多,因為你不是真正在運行一個工具,而是克隆一個項目模板。
「構(gòu)建應(yīng)用組件」
運行完上述命令后,你會注意到 Svelte 和 React 各自生成很多很多的文件,感興趣的話,可以隨便瀏覽看看這些生成的文件。
不管是 Svelte 和 React ,都是把組件源碼放到 src 文件夾下,Svelte 項目主要是一些擴展名為 svelte 的文件,而 React 項目則是一些 .js 的文件。
兩個項目都有一個 App 組件,分別是 App.svelte 和 App.js 。用你喜好的編輯器分別打開這兩個文件,清空它們,我們從頭開始。
「組件結(jié)構(gòu)」
「Svelte」
和 React 組件不同的是,Svelte 的代碼更像是以前我們在寫 HTML、CSS 和 JavaScript 一樣。
所有的 JavaScript 代碼都位于 Svelte 文件頂部的 <script></script> 標(biāo)簽當(dāng)中。
然后是 HTML 代碼,你還可以在 <style></style> 標(biāo)簽中編寫樣式代碼。有趣的是,組件中的樣式代碼只對當(dāng)前組件有效。這意味著在組件中為
標(biāo)簽編寫的樣式不會影響到其他組件中的
元素。
接下來我們開始編寫 App.svelte,首先刪空文件內(nèi)容,然后添加一個空的 <script> 標(biāo)簽:
<script>
</script>
我們將在這個標(biāo)簽中編寫大部分組件代碼。
「React」
在 React 項目中,打開 App.js 文件,清空所有內(nèi)容,然后添加如下代碼:
function App() {
}
export default App;
這幾行代碼創(chuàng)建并輸出了一個最基礎(chǔ)的函數(shù)式組件,名為 App() 。注意到這里還有另外一個不同之處就是 —— Svelte 無需輸出組件。
「Imports」
前面我們介紹過這個應(yīng)用包含三個組件:App, Heading和Button。不管是 Svelte 還是 React ,Heading 和 Button 組件都被引入到 App 中,這樣就可以被當(dāng)成 App 的子組件使用。我們將在后面繼續(xù)編寫這三個組件的代碼,但現(xiàn)在你只需要知道,構(gòu)建 App 組件時需要引入其他兩個組件。
「Svelte」
Svelte 需要在 <script> 使用 import 語句進行組件引入,編輯 App.svelte 文件添加兩個 import 語句:
<script>
import Button from './Button.svelte';
import Heading from './Heading.svelte';
</script>
「React」
React 的 import 語句位于文件的頂部,置于所有的函數(shù)或者類定義之前。在 App.js 最頂部,App() 函數(shù)之前,添加如下代碼:
import Heading from './Heading.js';
import Button from './Button.js';
import { useState } from 'react';
在這里,React 同時引入了 userState 鉤子,因為 App 是一個有狀態(tài)的組件。而 Svelte 不需要這個東西。
「狀態(tài)初始化」
App 是一個有狀態(tài)的組件,它有兩個狀態(tài)值分別是 color 和 count。
color 表示按鈕的顏色,這個值作為一個屬性傳遞給 Button 組件,并且它在每次點擊按鈕的時候改變。其初始值是 #000000,即為黑色。
count 代表按鈕點擊的次數(shù),其初始值為 0。
「Svelte」
在 Svelte 中,狀態(tài)等同于變量賦值,在 import 語句下方,<script> 標(biāo)簽之前添加如下狀態(tài)定義:
let count = 0;
let color = '#000000';
Svelte 同時提供了名為”反應(yīng)式聲明“ 的概念,用來重新計算狀態(tài)值,你不一定必須用這個,但如果狀態(tài)值依賴于其他可能更改的狀態(tài),這時候就很方便。
需要注意的是在 Svelte 中是通過狀態(tài)變量的賦值來實現(xiàn) DOM 更新的。如果狀態(tài)包含數(shù)組或者對象,當(dāng)對數(shù)組使用類似 .push() 方法并不會觸發(fā) DOM 更新。Svelte 提供了一個詳細文檔來介紹這個問題。
「React」
現(xiàn)在已經(jīng)引入了 useState 鉤子,所以只需要讓它工作起來即可。
在 App.js 的 App() 函數(shù)中添加如下狀態(tài)聲明:
const [count, setCount] = useState(0);
const [color, setColor] = useState('#000000');
上述代碼創(chuàng)建一個名為 count 的狀態(tài)變量,其初始值為 0,以及一個用來更新值的函數(shù)名為 setCount()。同樣的,React 創(chuàng)建了另一個狀態(tài)變量 color 初始值為 #000000 以及名為 setColor() 的更新函數(shù)。從這點來看,Svelte 的狀態(tài)初始化方法要簡單易懂得多。
「組件渲染和屬性傳遞」
兩個項目我們都是要創(chuàng)建一個由 <main> 元素構(gòu)建的用戶界面,該元素包含兩個嵌套的組件 Heading 和 Button。
App 組件傳遞屬性給兩個子組件。Heading 組件接收 count 狀態(tài)值,Button 組件接收 color 狀態(tài)值,此外還有一個名為 handleClick() 的事件處理函數(shù)。
「Svelte」
Svelte 使用它自己的模板語言來創(chuàng)建用戶界面,而 React 使用 JSX 。Svelte 模板語言跟寫 HTML 沒什么兩樣。接下來只需在 <script> 標(biāo)簽結(jié)束后開始編寫。
拷貝如下
<script>
...
</script>
<main>
<Heading count={count} />
<Button color={color} handleClick={handleClick} />
</main>
「React」
回到 App.js, 將如下代碼拷貝到你的 App() 函數(shù)中狀態(tài)聲明部分的下方:
return (
<main>
<Heading count={count} />
<Button color={color} handleClick={handleClick} />
</main>
)
該代碼從 App() 函數(shù)中返回 UI 界面的 JSX。
這里 Svelte 和 React 的做法都很類似,屬性的傳遞也幾乎相同。而 Svelte 的模板看起來跟 React 的 JSX 很像。
如果你是一個對 Svelte 充滿好奇的 React 開發(fā)人員,在屬性傳遞上 Svelte 沒有什么新奇之處。而在接收屬性時 Svelte 有點點不一樣,后面將進行介紹。
「狀態(tài)向上傳遞」
為了讓這個應(yīng)用正常工作,每次點擊按鈕時,必須讓 App 組件的 count 狀態(tài)值增1。因此需要一個機制來將數(shù)據(jù)從子組件傳遞給父組件。
前面已經(jīng)通過將 handleClick() 函數(shù)作為屬性傳遞給 Button 組件。
接下來馬上要開始編寫的這個屬于 App 組件的函數(shù)。當(dāng)把它作為屬性傳遞給 Button 子組件,Button 組件就能在每次被點擊時調(diào)用這個函數(shù)。這就是 App 組件能響應(yīng)其子組件狀態(tài)變更的原因。
handleClick() 這個函數(shù)負責(zé)用來更新 App 組件的 count 和 color 狀態(tài)值。
「Svelte」
在 App.svelte 中編寫 handleClick 函數(shù)代碼如下:
const colors = ['#00ff00', '#ff0000', '#0000ff'];
let handleClick = () => {
count++;
color = colors[Math.floor(Math.random() * 3)];
}
「React」
在 App.js 中編寫 handleClick 函數(shù)代碼如下:
const colors = ['#00ff00', '#ff0000', '#0000ff'];
let handleClick = () => {
setCount(count+1);
setColor(colors[Math.floor(Math.random() * 3)]);
}
在 React 需要使用早先聲明的 setCount() 和 setColor() 方法來更新狀態(tài)值,而 Svelte 則可以直接更新。
現(xiàn)在我們可以開始編寫 Heading 組件了。
「編寫 Heading 組件」
Heading 組件顯示這個應(yīng)用的標(biāo)題以及點擊計數(shù)器。這不是一個有狀態(tài)的組件,其接收狀態(tài)值 count 來顯示按鈕點擊次數(shù)。
在 Svelte 項目的 src 文件夾中創(chuàng)建一個名為 Heading.svelte 的文件。
同樣的在 React 項目的 src 文件夾中創(chuàng)建新文件 Heading.js.
「接收屬性」
「Svelte」
拷貝如下代碼到 Heading.svelte 文件:
<script>
export let count;
</script>
<h1>Hello, I am a Svelte App!</h1>
<h2>The following button has been clicked {count} times.</h2>
請注意看上述代碼中 <script> 里的代碼。這行代碼告訴 Svelte 說,該組件將接收一個名為 count 的屬性。
這樣就可以在 Heading 組件的 HTML 模板中直接顯示 count 這個屬性。
這個寫法稍微有點點奇怪,但在文件頂部直接聲明屬性的方式看起來不錯,而且可以直接使用這個屬性。
「React」
切換到 Heading.js 文件,拷貝如下內(nèi)容到該文件:
// ConardLi
function Heading({ count }) {
return (
<div>
<h1>Hello, I am a React App!</h1>
<h2>The following button has been clicked {count} times.</h2>
</div>
)
}
export default Heading;
這段代碼創(chuàng)建一個新的名為 Heading 函數(shù)式組件,該組件有一個參數(shù) { count }, 這是從傳遞給組件的 props 對象中提取出來的。
「編寫 Button 組件」
Button 組件在界面上顯示一個按鈕,同時接收兩個屬性,分別是用來定義顏色的 color 和在點擊時觸發(fā)的 handleClick() 函數(shù)。
在 Svelte 項目的 src 文件夾中創(chuàng)建新文件 Button.svelte.
在 React 項目的 src 文件夾中創(chuàng)建新文件 Button.js.
「事件偵聽」
類似點擊和其他鼠標(biāo)事件等交互式事件的偵聽上,Svelte 和 React 的做法有一些不同。
「Svelte」
拷貝如下代碼到 Button.svelte:
// ConardLi
<script>
export let handleClick;
export let color;
</script>
<button style="--color: {color}" on:click={handleClick}>
Click me!
</button>
上述代碼中兩個屬性都是在頂部的 <script>標(biāo)簽中定義的。
然后它創(chuàng)建了一個按鈕。請注意第 6 行代碼的語法,忽略掉下一節(jié)要介紹的樣式部分,直接看按鈕點擊的事件偵聽器,它跟以往使用的習(xí)慣不同。
Svelte 使用一個 on: 指令來給 DOM 元素添加事件偵聽器。Svelte 使用非常簡潔方法進行事件修改,甚至可以只在按鈕首次點擊時觸發(fā)。更詳細的關(guān)鍵事件的觸發(fā)請閱讀 dispatch your own component events 這篇文檔。
「React」
拷貝如下代碼到 Button.js:
function Button({ color, handleClick }) {
return (
<button style={styles} onClick={handleClick}>
Click me!
</button>
)
}
export default Button;
如果服務(wù)依然運行中,將會看到這里有報錯信息,別擔(dān)心,下面我們將通過添加 styles 對象來可以解決這個問題。
上述代碼創(chuàng)建一個名為 Button() 的函數(shù)式組件,同時接收一個參數(shù) props, 參數(shù)包含兩個屬性 color 和 handleClick。handleClick() 函數(shù)在 handleClick 屬性上定義,可以在 JSX 上使用一個標(biāo)準(zhǔn)的 onClick 事件來觸發(fā)。
「動態(tài)樣式」
在這個應(yīng)用中 Button 組件介紹一個顏色值作為屬性,該顏色值就是按鈕的背景色。
「Svelte」
Svelte 的動態(tài)樣式?jīng)]有我期望的那么直接。
很不幸,不能直接在 <style> 標(biāo)簽中使用屬性值。不過可以使用組件的 HTML 作為在 JavaScript 和 CSS 之間通訊的方法。
在 Button 組件 Button.svelte 的 HTML 代碼下方增加如下代碼:
<style>
button {
color: white;
background-color: var(--color);
}
</style>
background-color 樣式屬性不能直接引用 color 屬性的值,它引用的是一個名為 color的樣式變量,這個樣式變量在前面的 HTML 代碼中通過 style="--color: {color}" 進行定義。
這個做法有一點點笨拙,但考慮到這個樣式僅在組件內(nèi)有效,我們也是可以接受的。當(dāng)然了,也可以定義全局樣式,具體請閱讀 global CSS 這篇文檔:https://svelte.dev/docs#style。
「React」
在 React 中可以有很多種方法給組件添加樣式。直接在元素上編寫樣式是最常用的方法。
要在 JSX 中使用內(nèi)嵌樣式,可以使用樣式創(chuàng)建一個對象,然后賦值給元素的 style 屬性,剩下的部分前面已經(jīng)實現(xiàn)過了。
在 Button() 函數(shù)中的 return 語句前面添加如下代碼來創(chuàng)建 styles 對象:
const styles = {
backgroundColor: color,
color: '#ffffff'
}
「測試應(yīng)用」
保存所有文件,如果應(yīng)用還沒有啟動,那現(xiàn)在就各自啟動服務(wù) ( Svelte : npm run dev, React : npm start)。然后打開瀏覽器的兩個 Tab 分別訪問 localhost:5000 和 localhost:3000 。
依次點擊兩個頁面的按鈕,看看效果
「Svelte」

「React」

從運行效果來看,Svelte 和 React 似乎在樣式上有點不同,但是功能已經(jīng)完成了。你對這兩個框架的感覺怎樣呢?
本文翻譯自 React vs. Svelte: Comparing the Basics (twilio.com):https://www.twilio.com/blog/react-svelte-comparing-basics
抖音前端正急缺人才,如果你想加入我們,歡迎加我微信和我聯(lián)系。另外如果你想加入高質(zhì)量前端交流群,或者你有任何其他事情想和我交流也可以添加我的個人微信 ConardLi 。
文中如有錯誤,歡迎在留言區(qū)和我留言,如果這篇文章幫助到了你,歡迎點贊、在看和關(guān)注。你的點贊、在看和關(guān)注是對我最大的支持!
「結(jié)論」
這是一次對 Svelte 有趣的探索,到目前位置二者能力差不多。Svelte 的模板語言非常有趣,特別是 on: 指令。實話實說我很懷念編寫 HTML 模板的日子。我一定會用 Svelte 來編寫更多的應(yīng)用,同時我也將深入了解諸如生命周期和數(shù)據(jù)綁定方面的能力,這些對 React 當(dāng)前階段來說還是有點痛苦的。
如果你也在學(xué)習(xí) Svelte 的話,別忘了跟大家分享。
你覺得哪個更好用呢?
The End
歡迎自薦投稿到《前端技術(shù)江湖》,如果你覺得這篇內(nèi)容對你挺有啟發(fā),記得點個 「在看」哦
點個『在看』支持下 
