React.js中的声明式和命令式之间的区别?


97

最近,我一直在研究有关Facebook JavaScript库React.js的功能和使用方法。当其差异说话的JavaScript的世界往往是两种编程风格的休息declarativeimperative被mentionned。

两者有什么区别?


22
latentflip.com/imperative-vs-declarative Imperative programming: telling the "machine" how to do something, and as a result what you want to happen will happen. Declarative programming: telling the "machine"1 what you would like to happen, and let the computer figure out how to do it.
rickyduck

4
泰勒·麦金尼斯(Tyler McGinnis)撰写了一篇长篇文章,并列举了一些很好的例子。
伊恩·邓恩

为什么要添加长答案作为评论?..
Alex

1
上面的链接是正确的,但是链接中包含的斜杠导致404。latentflip.com/imperative-vs-declarative
James Yoo

Answers:


166

声明式的样式(如react所具有的样式)允许您通过说“它看起来像这样”来控制应用程序中的流程和状态。命令式风格可以解决这个问题,并允许您通过说“这是您应该做的”来控制应用程序。

声明式的好处是您不会陷入表示状态的实现细节中。您要委派保持应用程序视图一致的组织组成部分,因此您只需要担心状态。

想象您有一个管家,他是一个框架的隐喻。而且你想做晚饭。在当务之急,您会逐步告诉他们如何做饭。您必须提供以下说明:

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

在声明性世界中,您只需描述您想要的内容

I want dinner with chicken.

如果您的管家不知道怎么做鸡,那么您就不能以声明式的方式进行操作。就像Backbone不知道如何变异自己来完成某项任务一样,您也不能仅仅告诉它去完成该任务。React之所以具有声明性,是因为它“知道如何做鸡肉”。与Backbone相比,后者只知道如何与厨房交互。

能够描述状态可以大大减少bug的表面积,这是一个好处。另一方面,由于委派或抽象化了实现状态的方式,因此事情发生的灵活性可能较低。


78

想象一个简单的UI组件,例如“ Like”按钮。当您点击它时,如果它先前是灰色的,它将变成蓝色,如果它先前是蓝色的,则变成灰色。

这样做的必要方法是:

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

基本上,您必须检查屏幕上当前显示的内容,并处理将其重绘为当前状态所需的所有更改,包括撤消先前状态的更改。您可以想象在现实情况下这可能有多么复杂。

相反,声明性方法将是:

if( this.state.liked ) {
    return <blueLike />;
} else {
    return <greyLike />;
}

因为声明式方法将关注点分开,所以它的这一部分仅需要处理UI在单独状态下的外观,因此更容易理解。


21

这是一个很好的类比:

*紧急命令:从停车场北出口出来,左拐。在I-15州际公路上向南行驶,直到到达Bangerter Highway出口。就像您要去宜家一样,在出口处右转。一直走并在第一个路口右转。继续穿过下一盏灯,然后左下。我的房子是#298。

声明性答复:我的地址是犹他州德拉珀84020,西不可变胡同298

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


18

最好将React(声明式)和JQuery(命令式)进行比较,以向您展示差异。

在React中,您只需要在方法中描述UI的最终状态render(),而不必担心如何从以前的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的某些部分,或者忘记删除旧的事件处理程序(导致内存泄漏或处理程序多次触发),等等。这就是发生错误的地方,即UI状态和模型状态超出了同步。

不同步的状态永远不会发生在React的声明式方法中,因为我们只需要更新模型状态,而React负责使UI和模型状态保持同步。

  • 在该钩子下,React将使用命令性代码更新所有更改的DOM元素。

您也可以阅读我的回答:声明式和命令式编程之间有什么区别?

PS:从上面的jQuery示例中,您可能会想,如果我们将所有DOM操作放入一个updateAll()方法中,并在每次模型状态发生任何变化时都调用它,并且UI永远不会不同步。没错,这实际上是React所做的,唯一的区别是jQueryupdateAll()会导致许多不必要的DOM操作,但是React只会使用其Virtual DOM Diffing Algorithm更新虚拟元素。


6

命令式代码指示JavaScript如何执行每个步骤。使用声明性代码,我们告诉JavaScript我们要完成的工作,然后让JavaScript负责执行这些步骤。

React是声明性的,因为我们编写了所需的代码,而React负责获取声明的代码并执行所有JavaScript / DOM步骤以使我们获得所需的结果。


5

当务之急是在现实生活中进入啤酒酒吧,并向酒保提供以下说明:

-从架子上拿杯

-将玻璃杯放在草稿的前面

-拉下把手直到玻璃杯装满

-把玻璃杯递给我。

相反,在声明性世界中,您只会说:“请啤酒。”

要求啤酒的声明式方法假定调酒师知道如何为某人服务,这是声明式编程工作方式的重要方面。

在声明式编程中,开发人员仅描述他们想要实现的目标,因此无需列出使之工作的所有步骤。

React提供了一种声明式方法,这一事实使它易于使用,因此,生成的代码很简单,这通常导致更少的错误和更多的可维护性。

由于React遵循声明性范式,因此无需告诉它如何与DOM交互;您只需声明要在屏幕上看到的内容,然后React为您完成工作。



0

我以一个类比开始:我有两辆车,在我的两辆车中,我希望车内的温度为正常的室温〜72°F。在第一辆(较旧的)汽车中,有两个用于控制温度的旋钮(一个用于控制温度的旋钮和一个用于控制气流的旋钮)。天气太热时,我必须调节第一个旋钮以降低温度,并可能改变气流),反之亦然。这是当务之急!我必须自己控制旋钮。在我的第二辆(较新的)汽车中,我可以设置/声明温度。这意味着我不必费力调节旋钮即可调节温度,因为我知道汽车声明/将其设置为72°F,并且汽车将必须完成该状态。

React是相同的,您声明标记/模板和stat,然后React进行必要的工作以保持DOM与您的应用程序同步。

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

无需使用.addEventListener()事件处理程序,而是声明所需的内容。单击该按钮后,它将运行该activateTeleporter功能。


-1
  • 声明式允许您控制所有视图。(例如状态管理)
  • 命令式允许您控制视图。(例如$(this))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.