前端框架 React 和 Svelte 的基礎比較

翻譯 | 紅薯

出品 | OSC 開源社區(ID:oschina2013)

在 JavaScript 前端開發框架中,Svelte 算是一個新來的攪局者,在網上我們已經聽到很多關於 Svelte 的嗶嗶。因此我決定試試這個傢伙,順便跟 React 做個簡單的比較。

本文將展示 Svelte 和 React 在構建一個基礎應用的差異,其中涉及到的內容包括:

還有很多其他方面的內容需要討論,例如 按需渲染 和 生命週期 等其他炫酷的概念。限於篇幅,這篇文章還是聚焦在基礎使用上吧。

準備工作

在繼續往下閱讀之前,你應該準備好如下環境:

Svelte 與 React

Svelte 和 React.js 兩者都是基於組件的 JavaScript 框架,主要用於 Web 應用的開發。最主要的區別是 Svelte 沒有使用虛擬 DOM。Svelte 在構建的時候就將代碼編譯成 Vanilla JS 代碼,而 React 在運行時解釋代碼。

Svelte 文檔寫道:

‎Svelte 是一種全新的構建 Web 應用的方法。諸如 React 和 Vue 這類傳統的框架,它們的大部分工作都在瀏覽器上執行,而 Svelte 在構建應用的過程做就了大量的工作。

‎Svelte 沒有使用虛擬 DOM 技術,而是當應用狀態發生變化時,通過代碼如手術般的更新 DOM。‎

酷!但是這些底層的細節對我來說並不重要。我只想從開發人員的角度看看,在使用 Svelte 和 React 開發應用程序時,感覺好嗎?有趣嗎?直觀嗎?

創建應用腳手架

在這篇文章中,我們將創建一個很小的 Web 應用,產品經理給這個應用確定瞭如下需求:

首先使用如下命令在你電腦上創建一個新的目錄,暫且命名爲 svelte-react:

接着分別創建 Svelte 和 React 的應用模板並運行。這裏 Svelte 的初始步驟比 React 多了一步,此外 Svelte 默認端口是 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

你會發現 Svelte 的命令運行快得多,因爲你不是真正在運行一個工具,而是克隆一個項目模板。

構建應用組件

運行完上述命令後,你會注意到 Svelte 和 React 各自生成很多很多的文件,感興趣的話,可以隨便瀏覽看看這些生成的文件。

不管是 Svelte 和 React ,都是把組件源碼放到 src 文件夾下,Svelte 項目主要是一些擴展名爲 svelte 的文件,而 React 項目則是一些 .js 的文件。

兩個項目都有一個 App 組件,分別是 App.svelte 和 App.js 。用你喜好的編輯器分別打開這兩個文件,清空它們,我們從頭開始。

組件結構

Svelte

和 React 組件不同的是,Svelte 的代碼更像是以前我們在寫 HTML、CSS 和 JavaScript 一樣。

所有的 JavaScript 代碼都位於 Svelte 文件頂部的  標籤當中。

然後是 HTML 代碼,你還可以在  標籤中編寫樣式代碼。有趣的是,組件中的樣式代碼只對當前組件有效。這意味着在組件中爲  標籤編寫的樣式不會影響到其他組件中的  元素。

接下來我們開始編寫 App.svelte,首先刪空文件內容,然後添加一個空的  標籤:

<script>
</script>

我們將在這個標籤中編寫大部分組件代碼。

React

在 React 項目中,打開 App.js 文件,清空所有內容,然後添加如下代碼:

function App() {

}

export default App;

這幾行代碼創建並輸出了一個最基礎的函數式組件,名爲 App() 。注意到這裏還有另外一個不同之處就是 —— Svelte 無需輸出組件。

Imports

前面我們介紹過這個應用包含三個組件:App, Heading 和 Button。不管是 Svelte 還是 React ,Heading 和 Button 組件都被引入到 App 中,這樣就可以被當成 App 的子組件使用。我們將在後面繼續編寫這三個組件的代碼,但現在你只需要知道,構建 App 組件時需要引入其他兩個組件。

Svelte

Svelte 需要在  使用 import 語句進行組件引入,編輯 App.svelte 文件添加兩個 import 語句:

<script>
  import Button from './Button.svelte';
  import Heading from './Heading.svelte';
</script>

React

React 的 import 語句位於文件的頂部,置於所有的函數或者類定義之前。在 App.js 最頂部,App() 函數之前,添加如下代碼:

import Heading from './Heading.js';
import Button from './Button.js';
import { useState } from 'react';

在這裏,React 同時引入了 userState 鉤子,因爲 App 是一個有狀態的組件。而 Svelte 不需要這個東西。

狀態初始化

App 是一個有狀態的組件,它有兩個狀態值分別是 color 和 count。

color 表示按鈕的顏色,這個值作爲一個屬性傳遞給 Button 組件,並且它在每次點擊按鈕的時候改變。其初始值是 #000000,即爲黑色。

count 代表按鈕點擊的次數,其初始值爲 0。

Svelte

在 Svelte 中,狀態等同於變量賦值,在 import 語句下方, 標籤之前添加如下狀態定義:

let count = 0;
let color = '#000000';

Svelte 同時提供了名爲” 反應式聲明 “ 的概念,用來重新計算狀態值,你不一定必須用這個,但如果狀態值依賴於其他可能更改的狀態,這時候就很方便。

需要注意的是在 Svelte 中是通過狀態變量的賦值來實現 DOM 更新的。如果狀態包含數組或者對象,當對數組使用類似 .push() 方法並不會觸發 DOM 更新。Svelte 提供了一個詳細文檔來介紹這個問題。

React

現在已經引入了 useState 鉤子,所以只需要讓它工作起來即可。

在 App.js 的 App() 函數中添加如下狀態聲明:

const [count, setCount] = useState(0);
const [color, setColor] = useState('#000000');

上述代碼創建一個名爲 count 的狀態變量,其初始值爲 0,以及一個用來更新值的函數名爲 setCount()。同樣的,React 創建了另一個狀態變量 color 初始值爲 #000000 以及名爲 setColor() 的更新函數。從這點來看,Svelte 的狀態初始化方法要簡單易懂得多。

組件渲染和屬性傳遞

兩個項目我們都是要創建一個由  元素構建的用戶界面,該元素包含兩個嵌套的組件 Heading 和 Button。

App 組件傳遞屬性給兩個子組件。Heading 組件接收 count 狀態值,Button 組件接收 color 狀態值,此外還有一個名爲 handleClick() 的事件處理函數。

Svelte

Svelte 使用它自己的模板語言來創建用戶界面,而 React 使用 JSX 。Svelte 模板語言跟寫 HTML 沒什麼兩樣。接下來只需在  標籤結束後開始編寫。

拷貝如下 部分代碼到 App.svelte 文件中,形如:

<script>
  ...
</script>

<main>
  <Heading count={count} />
  <Button color={color} handleClick={handleClick} />
</main>

React

回到 App.js, 將如下代碼拷貝到你的 App() 函數中狀態聲明部分的下方:

return (
  <main>
    <Heading count={count} />     
    <Button color={color} handleClick={handleClick} />
  </main>
)

該代碼從 App() 函數中返回 UI 界面的 JSX。

這裏 Svelte 和 React 的做法都很類似,屬性的傳遞也幾乎相同。而 Svelte 的模板看起來跟 React 的 JSX 很像。

如果你是一個對 Svelte 充滿好奇的 React 開發人員,在屬性傳遞上 Svelte 沒有什麼新奇之處。而在接收屬性時 Svelte 有點點不一樣,後面將進行介紹。

狀態向上傳遞

爲了讓這個應用正常工作,每次點擊按鈕時,必須讓 App 組件的 count 狀態值增 1。因此需要一個機制來將數據從子組件傳遞給父組件。

前面已經通過將 handleClick() 函數作爲屬性傳遞給 Button 組件。

接下來馬上要開始編寫的這個屬於 App 組件的函數。當把它作爲屬性傳遞給 Button 子組件,Button 組件就能在每次被點擊時調用這個函數。這就是 App 組件能響應其子組件狀態變更的原因。

handleClick() 這個函數負責用來更新 App 組件的 count 和 color 狀態值。

Svelte

在 App.svelte 中編寫 handleClick 函數代碼如下:

const colors = ['#00ff00', '#ff0000', '#0000ff'];

let handleClick = () => {
  count++;
  color = colors[Math.floor(Math.random() * 3)];
}

React

在 App.js 中編寫 handleClick 函數代碼如下:

const colors = ['#00ff00', '#ff0000', '#0000ff'];

let handleClick = () => {
  setCount(count+1);
  setColor(colors[Math.floor(Math.random() * 3)]);
}

在 React 需要使用早先聲明的 setCount() 和 setColor() 方法來更新狀態值,而 Svelte 則可以直接更新。

現在我們可以開始編寫 Heading 組件了。

編寫 Heading 組件

Heading 組件顯示這個應用的標題以及點擊計數器。這不是一個有狀態的組件,其接收狀態值 count 來顯示按鈕點擊次數。

在 Svelte 項目的 src  文件夾中創建一個名爲 Heading.svelte 的文件。

同樣的在 React 項目的 src 文件夾中創建新文件 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>

請注意看上述代碼中  裏的代碼。這行代碼告訴 Svelte 說,該組件將接收一個名爲 count 的屬性。

這樣就可以在 Heading 組件的 HTML 模板中直接顯示 count 這個屬性。

這個寫法稍微有點點奇怪,但在文件頂部直接聲明屬性的方式看起來不錯,而且可以直接使用這個屬性。

React

切換到 Heading.js 文件,拷貝如下內容到該文件:

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;

這段代碼創建一個新的名爲 Heading 函數式組件,該組件有一個參數 {count}, 這是從傳遞給組件的 props 對象中提取出來的。

編寫 Button 組件

Button 組件在界面上顯示一個按鈕,同時接收兩個屬性,分別是用來定義顏色的 color 和在點擊時觸發的 handleClick() 函數。

在 Svelte 項目的 src 文件夾中創建新文件 Button.svelte.

在 React 項目的 src 文件夾中創建新文件 Button.js.

事件偵聽

類似點擊和其他鼠標事件等交互式事件的偵聽上,Svelte 和 React 的做法有一些不同。

Svelte

拷貝如下代碼到 Button.svelte:

<script>
  export let handleClick;
  export let color;
</script>

<button style="--color: {color}" on:click={handleClick}>
  Click me!
</button>

上述代碼中兩個屬性都是在頂部的  標籤中定義的。

然後它創建了一個按鈕。請注意第 6 行代碼的語法,忽略掉下一節要介紹的樣式部分,直接看按鈕點擊的事件偵聽器,它跟以往使用的習慣不同。

Svelte 使用一個 on: 指令來給 DOM 元素添加事件偵聽器。Svelte 使用非常簡潔方法進行事件修改,甚至可以只在按鈕首次點擊時觸發。更詳細的關鍵事件的觸發請閱讀 dispatch your own component events 這篇文檔。

React

拷貝如下代碼到 Button.js:

function Button({ color, handleClick }) {
  return (
    <button style={styles}  onClick={handleClick}>
      Click me!
    </button>
  )
}

export default Button;

如果服務依然運行中,將會看到這裏有報錯信息,別擔心,下面我們將通過添加 styles 對象來可以解決這個問題。

上述代碼創建一個名爲 Button() 的函數式組件,同時接收一個參數 props, 參數包含兩個屬性 color 和 handleClick。handleClick() 函數在 handleClick 屬性上定義,可以在 JSX 上使用一個標準的 onClick 事件來觸發。

動態樣式

在這個應用中 Button 組件介紹一個顏色值作爲屬性,該顏色值就是按鈕的背景色。

Svelte

Svelte 的動態樣式沒有我期望的那麼直接。

很不幸,不能直接在  標籤中使用屬性值。不過可以使用組件的 HTML 作爲在 JavaScript 和 CSS 之間通訊的方法。

在 Button 組件 Button.svelte 的 HTML 代碼下方增加如下代碼:

<style>
  button {
    color: white;
    background-color: var(--color);
  }
</style>

background-color 樣式屬性不能直接引用 color 屬性的值,它引用的是一個名爲 color 的樣式變量,這個樣式變量在前面的 HTML 代碼中通過 style="--color: {color}" 進行定義。

這個做法有一點點笨拙,但考慮到這個樣式僅在組件內有效,我們也是可以接受的。當然了,也可以定義全局樣式,具體請閱讀 global CSS 這篇文檔:https://svelte.dev/docs#style。

React

在 React 中可以有很多種方法給組件添加樣式。直接在元素上編寫樣式是最常用的方法。

要在 JSX 中使用內嵌樣式,可以使用樣式創建一個對象,然後賦值給元素的 style 屬性,剩下的部分前面已經實現過了。

在 Button() 函數中的 return 語句前面添加如下代碼來創建 styles 對象:

const styles = {
  backgroundColor: color,
  color: '#ffffff'
}

測試應用

保存所有文件,如果應用還沒有啓動,那現在就各自啓動服務 ( Svelte : npm run dev, React : npm start)。然後打開瀏覽器的兩個 Tab 分別訪問 localhost:5000 和 localhost:3000 。

依次點擊兩個頁面的按鈕,看看效果。

Svelte

React

從運行效果來看,Svelte 和 React 似乎在樣式上有點不同,但是功能已經完成了。你對這兩個框架的感覺怎樣呢?

結論

這是一次對 Svelte 有趣的探索,到目前位置二者能力差不多。Svelte 的模板語言非常有趣,特別是 on: 指令。實話實說我很懷念編寫 HTML 模板的日子。我一定會用 Svelte 來編寫更多的應用,同時我也將深入瞭解諸如生命週期和數據綁定方面的能力,這些對 React 當前階段來說還是有點痛苦的。

如果你也在學習 Svelte 的話,別忘了跟大家分享。

本文翻譯自 React vs. Svelte: Comparing the Basics (twilio.com):https://www.twilio.com/blog/react-svelte-comparing-basics

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5weU_Zrv56y_AavbOLFNtQ