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

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

「React」

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