簡體   English   中英

React.js 中聲明式和命令式的區別?

[英]Difference between declarative and imperative in React.js?

最近我一直在研究 Facebook JavaScript 庫 React.js 的功能和使用方法。 當談到它與 JavaScript 世界其他部分的區別時,通常會提到declarative式和imperative兩種編程風格。

兩者有什么區別?

聲明式風格,就像 react 一樣,允許您通過說“它應該看起來像這樣”來控制應用程序中的流程和狀態。 命令式風格可以扭轉這種局面,並允許您通過說“這是您應該做的”來控制您的應用程序。

聲明式的好處是您不會陷入表示狀態的實現細節中。 您正在委派保持應用程序視圖一致的組織組件,因此您只需要擔心狀態。

想象一下,你有一個管家,他是一種框架的隱喻。 你想做晚飯。 在一個命令式的世界里,你會一步一步地告訴他們如何做晚餐。 您必須提供以下說明:

Go to the kitchen
Open fridge
Remove chicken from fridge
...
Bring food to the table

在聲明式世界中,您只需描述您想要的內容

I want dinner with chicken.

如果您的管家不知道如何制作雞肉,那么您就不能以聲明式的方式進行操作。 就像如果 Backbone 不知道如何變異自己來完成某項任務一樣,您不能只告訴它執行該任務。 例如,React 能夠是聲明性的,因為它“知道如何制作雞肉”。 與只知道如何與廚房交互的 Backbone 相比。

能夠描述狀態極大地減少了錯誤的表面積,這是一個好處。 另一方面,您可能在事情發生方式上的靈活性較低,因為您正在委派或抽象出您如何實現狀態。

想象一個簡單的 UI 組件,例如“Like”按鈕。 當你點擊它時,如果它以前是灰色的,它會變成藍色,如果它以前是藍色的,它就會變成灰色。

這樣做的必要方法是:

if( user.likes() ) {
        if( hasBlue() ) {
            removeBlue();
            addGrey();
        } else {
            removeGrey();
            addBlue();
        }
    }

基本上,您必須檢查屏幕上當前的內容並處理所有必要的更改,以使用當前狀態重繪它,包括撤消對先前狀態的更改。 您可以想象在現實世界中這可能是多么復雜。

相比之下,聲明性方法將是:

return this.state.liked ? <blueLike /> : <greyLike />;

因為聲明式方法分離了關注點,這部分只需要處理 UI 在特定狀態下的外觀,因此更容易理解。

最好比較 React(聲明式)和 JQuery(命令式)以向您展示差異。

在 React 中,你只需要在render()方法中描述你的 UI 的最終狀態,而不用擔心如何從之前的 UI 狀態轉換到新的 UI 狀態。 例如,

render() {
  const { price, volume } = this.state;
  const totalPrice = price * volume;

  return (
    <div>
      <Label value={price} className={price > 100 ? 'expensive' : 'cheap'} ... />
      <Label value={volume} className={volume > 1000 ? 'high' : 'low'} ... />
      <Label value={totalPrice} ... />
      ...
    </div>
  )
}

另一方面,JQuery 要求您強制轉換 UI 狀態,例如,選擇標簽元素並更新它們的文本和 CSS:

updatePrice(price) {
  $("#price-label").val(price);
  $("#price-label").toggleClass('expansive', price > 100);
  $("#price-label").toggleClass('cheap', price < 100);

  // also remember to update UI depending on price 
  updateTotalPrice();
  ... 
}

updateVolume(volume) {
  $("#volume-label").val(volume);
  $("#volume-label").toggleClass('high', volume > 1000);
  $("#volume-label").toggleClass('low', volume < 1000);
  
  // also remember to update UI depending on volume
  updateTotalPrice();
  ... 
}

updateTotalPrice() {
  const totalPrice = price * volume;
  $("#total-price-label").val(totalPrice);
  ...
}

在現實世界的場景中,會有更多的 UI 元素需要更新,以及它們的屬性(例如 CSS 樣式和事件偵聽器)等。如果您使用 JQuery 強制執行此操作,則會變得復雜和乏味; 很容易忘記更新 UI 的某些部分,或者忘記刪除舊的事件處理程序(導致內存泄漏或處理程序多次觸發)等。這就是 bug 發生的地方,即 UI 狀態和模型狀態不在同步。

狀態不同步不會發生在 React 的聲明式方法中,因為我們只需要更新模型狀態,而 React 負責保持 UI 和模型狀態同步。

  • 在這個鈎子下,React 將使用命令式代碼更新所有更改的 DOM 元素。

您還可以閱讀我對編程中聲明式范式和命令式范式有什么區別的回答? .

PS:從上面的 jQuery 示例中,您可能會想,如果我們將所有 DOM 操作放在一個updateAll()方法中,並且每次模型狀態發生變化時調用它,UI 將永遠不會不同步。 你是對的,這實際上是 React 所做的,唯一的區別是 jQuery updateAll()會導致許多不必要的 DOM 操作,但 React 只會使用其Virtual DOM Diffing Algorithm更新更改的 DOM 元素。

這是一個很好的類比:

*當務之急:從停車場北口出來左轉。 沿 I-15 向南行駛,直到您到達 Bangerter 高速公路出口。 就像你要去宜家一樣,從出口右轉。 直走並在第一個紅綠燈處右轉。 繼續通過下一個燈,然后左轉。 我的房子是#298。

聲明性回復:我的地址是 298 West Immutable Alley, Draper Utah 84020*

https://tylermcginnis.com/imperative-vs-declarative-programming/

命令式代碼指示 JavaScript 如何執行每個步驟。 使用聲明性代碼,我們告訴 JavaScript 我們想要做什么,並讓 JavaScript 負責執行這些步驟。

React 是聲明性的,因為我們編寫了我們想要的代碼,而 React 負責獲取我們聲明的代碼並執行所有 JavaScript/DOM 步驟以使我們達到我們想要的結果。

在命令式世界中,現實生活中的一個相似之處是進入酒吧喝啤酒,並向調酒師發出以下指示:

--從架子上拿一杯

--把玻璃放在草稿前

--拉下把手直到杯子滿

——把杯子遞給我。

相反,在聲明式世界中,您只需說:“請喝啤酒。”

要求啤酒的聲明式方法假設調酒師知道如何為啤酒服務,這是聲明式編程工作方式的一個重要方面。

在聲明式編程中,開發人員只描述他們想要實現的目標,而無需列出使其工作的所有步驟。

React 提供聲明式方法這一事實使其易於使用,因此生成的代碼很簡單,這通常會導致更少的錯誤和更高的可維護性。

由於 React 遵循聲明式范式,因此無需告訴它如何與 DOM 交互; 你只需聲明你想在屏幕上看到的內容,React 就會為你完成這項工作。

聲明式編程是一種編程風格,其中應用程序的結構方式優先於描述應該發生的事情,而不是定義應該如何發生。

為了理解聲明式編程,讓我們將其與命令式編程(只關心如何用代碼實現結果的編程風格)進行比較。

示例:使字符串 URL 友好。 通常,這可以通過用連字符替換字符串中的所有空格來完成,因為空格不是 URL 友好的。 首先,這個任務的命令式方法:

const string = "difference between declarative and imperative in react.js";
const urlFriendly = "";
for (var i = 0; i < string.length; i++) {
    if (string[i] === " ") {
      urlFriendly += "-";
    } else {
      urlFriendly += string[i];
    }
}
console.log(urlFriendly); // "difference-between-declarative-and-imperative-in-react-js"

在此示例中,我們遍歷字符串中的每個字符,並在出現空格時替換它們。 這個程序的結構只關心如何完成這樣的任務。 我們使用 for 循環和 if 語句並使用相等運算符設置值。 僅僅看代碼並不能告訴我們太多,因為命令式程序需要大量注釋才能理解發生了什么。

現在讓我們看一下針對同一問題的聲明性方法:

const string = "Difference between declarative and imperative in React.js?";
const urlFriendly = string.replace(/ /g, "-");
console.log(urlFriendly);

在這里,我們使用string.replace和正則表達式來用連字符替換所有空格實例。 使用string.replace是描述應該發生的事情的一種方式:字符串中的空格應該被替換。 如何處理空間的細節在替換函數中被抽象出來。

在聲明式程序中,語法本身描述了應該發生的事情,而事情發生的細節被抽象掉了。

從本質上講,聲明式編程產生的應用程序更容易推理,當對應用程序更容易推理時,該應用程序更容易擴展。 有關聲明式編程范例的更多詳細信息,請參見聲明式編程 wiki

現在,讓我們考慮構建文檔對象模型的任務。 一種命令式方法將關注 DOM 的構造方式:

const target = document.getElementById("target");
const wrapper = document.createElement("div");
const headline = document.createElement("h1");
wrapper.id = "welcome";
headline.innerText = "Hello World";
wrapper.appendChild(headline);
target.appendChild(wrapper);

此代碼涉及創建元素、設置元素並將它們添加到文檔中。 在強制構建 DOM 的情況下,很難進行更改、添加功能或擴展 10,000 行代碼。

現在讓我們看看如何使用 React 組件以聲明方式構造 DOM:

const { render } = ReactDOM;
const Welcome = () => (
   <div id="welcome">
      <h1>Hello World</h1>
   </div>
);

render(<Welcome />, document.getElementById("target"));

React 是聲明式的。 在這里,Welcome 組件描述了應該呈現的 DOM。 渲染函數使用組件中聲明的指令來構建 DOM,抽象出如何渲染 DOM 的細節。 我們可以清楚地看到我們想要將 Welcome 組件渲染到 ID 為 target 的元素中

來源: 開發 React 應用程序的現代模式

聲明式編程是一種編程范式……它表達了計算的邏輯而不描述其控制流。

命令式編程是一種編程范式,它使用改變程序狀態的語句。

參考鏈接:- https://codeburst.io/declarative-vs-imperative-programming-a8a7c93d9ad2

  • 聲明式允許您控制所有視圖。 (就像狀態管理一樣)
  • 命令式允許您控制視圖。 (就像 $(this) 一樣)

我將從一個類比開始:我有兩輛車,在我的兩輛車中,我希望車內的溫度為正常室溫~72°F。 在第一輛(較舊的)汽車中,有兩個旋鈕用於控制溫度(一個旋鈕用於控制溫度,一個旋鈕用於控制氣流)。 當它變得太熱時,我必須調整第一個旋鈕以降低溫度並可能改變氣流)如果太冷則反之亦然。 這是一項勢在必行的工作! 我必須自己管理旋鈕。 在我的第二輛(較新)汽車中,我可以設置/聲明溫度。 這意味着我不必擺弄旋鈕來調整我的汽車知道的溫度,我宣布/將其設置為 72°F,我的汽車將完成必要的工作以達到該狀態。

React 是一樣的,你聲明標記/模板和 stat 然后 React 做必要的工作來保持 DOM 與你的應用程序同步。

<button onClick={activateTeleporter}>Activate Teleporter</button>

我們不使用.addEventListener()來設置事件處理,而是聲明我們想要的。 單擊按鈕時,它將運行activateTeleporter函數。

聲明式與命令式

聲明式編程就像讓你的朋友畫你的房子。 你不在乎他們如何清潔它,他們用什么顏色來繪畫,他們用了多少資源來完成它`。

//Declarative For Searching element from an array
  array.find(item)

聲明式的反面是命令式的。 命令式方法的一個常見例子是你告訴你的朋友確切地做什么來粉刷你的房子。

  • 用清潔劑清洗房子。
  • 使用 Narolac 油漆或亞洲油漆
  • 用綠色油漆屋頂。
  • 獲得3名成員簽約等。

//命令式算法

def imperative_search(array, item)
  for i in array do
    if i == item
      return item
    end
  end
  return false
end

這是我目前的理解:

聲明性代碼(幾乎?)始終是代碼之上的抽象層,本質上更具有命令性。

React 允許您編寫聲明性代碼,它是直接與 DOM 交互的命令式代碼(例如diffing 算法)之上的抽象層。 如果您需要編寫命令式代碼(即直接與 DOM 交互),React 提供Refs 作為逃生艙口

解釋每一步步驟是一種命令式方法,例如我們必須使用 Hello World 創建一個段落標簽! 里面的文字。

//Imperative 

const para = document.createElement('p');
para.innerText = 'Hello World !';
document.querySelector('#root').appendChild(para);

在不指定確切過程的情況下定義所需的最終目標狀態。 即帶有文本的 p 標記,無需告訴 createElement 或 innerText

 //Declarative 

 import React from "react"; 
 import ReactDOM from "react-dom"; 
 
 const App = () =>{
  return(<p>Hello World !</p>);
 }

 ReactDOM.render(<App />, document.getElementById("root"));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM