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

时间:2015-11-11 16:48:12

标签: reactjs

最近我一直在研究很多关于使用Facebook JavaScript库React.js的功能和方法。在谈到它与JavaScript世界其他部分的差异时,通常会提到两种编程样式declarativeimperative

两者之间有什么区别?

14 个答案:

答案 0 :(得分:122)

声明式样式,就像反应一样,允许您通过说“它应该看起来像这样”来控制应用程序中的流和状态。一种命令式的风格转过来,让你通过说“这就是你应该做的事情”来控制你的应用程序。

声明性的好处是你不会陷入代表状态的实现细节中。您正在委派保持应用程序视图一致的组织组件,因此您只需担心状态。

想象一下,你有一个管家,他是一个框架的隐喻。你想做晚饭。在一个命令性的世界里,你会告诉他们如何做晚餐。您必须提供以下说明:

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

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

I want dinner with chicken.

如果您的管家不知道如何制作鸡肉,那么您就无法以陈述式进行操作。就像Backbone不知道如何改变自己来完成某项任务一样,你不能只是告诉它去做那项任务。 React能够声明,因为它“知道如何制作鸡”,例如。与Backbone相比,后者只知道如何与厨房接口。

能够描述状态会显着减少虫子的表面积,这是一个好处。另一方面, 事件发生的可能性较小,因为您委派或抽象出如何实现状态。

答案 1 :(得分:57)

想象一个简单的UI组件,例如" Like"按钮。点击它时,如果以前是灰色,则变为蓝色;如果之前是蓝色,则变为灰色。

这样做的必要方法是:

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

基本上,你必须检查屏幕上当前的内容 处理使用当前状态重绘它所需的所有更改,包括撤消先前状态的更改。你可以想象这在现实场景中会有多复杂。

相反,声明性方法是:

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

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

答案 2 :(得分:14)

这是一个很好的类比:

*强制性回应:走出停车场的北出口然后向左走。沿着I-15向南行驶,直到您到达Bangerter高速公路出口。就像你要去宜家一样从出口右转。直行并在第一盏灯右转。继续通过下一个灯,然后在下一个左侧。我的房子是#298。

陈述性回复:我的地址是298 West Immutable Alley,Draper Utah 84020 *

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

答案 3 :(得分:9)

势在必行的代码:

当强制编写JavaScript代码时,我们会告诉JavaScript究竟该做什么以及如何做。可以把它想象成我们在它应该采取的步骤上给出JavaScript命令。

例如,我给你一个简单的循环:

const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
const excitedPeople = []

for (let i = 0; i < people.length; i++) {
  excitedPeople[i] = people[i] + '!'
}

这是必要的代码。我们指导JavaScript在每一步都要做什么。我们必须给它命令:

  1. 设置迭代器的初始值 - (let i = 0)
  2. 告诉for循环何时需要停止 - (i&lt; people.length)
  3. 让当前位置的人加上感叹号 - (人[i] +&#39;!&#39;)
  4. 将数据存储在另一个数组中的第i个位置 - (兴奋的人[i])
  5. 将i变量递增1 - (i ++)
  6. 声明代码:

    使用声明性代码,我们不会编写所有步骤以使我们达到最终结果。相反,我们声明了我们想要完成的任务,JavaScript将负责这样做。这个解释有点抽象,所以让我们看一个例子。让我们来看看我们刚刚看到的循环代码的命令,并将其重构为更具声明性。

    使用命令式代码,我们执行了所有步骤以获得最终结果。但是,我们真正想要的最终结果是什么?好吧,我们的出发点只是一系列名称:

    const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
    

    我们想要的最终目标是一个相同名称的数组,但每个名称都以感叹号结尾:

    ["Amanda!", "Geoff!", "Michael!", "Richard!", "Ryan!", "Tyler!"]
    

    为了让我们从起点到终点,我们只需使用JavaScript的.map()函数来声明我们想要完成的任务。

    const excitedPeople = people.map(name => name + '!')
    

    那就是它!请注意,使用此代码我们还没有:

    创建了一个迭代器对象 告诉代码何时应该停止运行 使用迭代器访问people数组中的特定项 将每个新字符串存储在excitedPeople数组中 ......所有这些步骤都由JavaScript的.map()数组方法处理。

答案 4 :(得分:6)

最好比较React(声明性)和JQuery(命令式)来向您展示差异。

在React中,您只需要在render()方法中描述UI的最终状态,而不必担心如何从先前的UI状态转换到新的UI状态。如,

render() {
    ...
    <Label value={this.state.lastPrice} ... />
    <Label value={this.state.askPrice} ... />
}

另一方面,JQuery要求您强制转换UI状态,例如选择标签元素并更新其文本。

update() {
    ...
    $("#last-price-label").val(lastPrice);
    $("#ask-price-label").val(askPrice);
}

在现实世界的场景中,将会有更多的UI元素需要更新,以及它们的属性(例如,CSS样式和事件监听器)等。如果你使用JQuery强制执行此操作,它将变得复杂和乏味;很容易忘记更新UI的某些部分,或者忘记删除旧的偶数监听器(内存泄漏)等。这就是错误发生的地方,即UI状态和模型状态不同步。

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

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

您也可以阅读我对What is the difference between declarative and imperative programming?的回答。

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

答案 5 :(得分:1)

这是我目前的理解:

声明性代码(几乎?)始终是代码之上的抽象层,本质上更具有命令性。

React 允许您编写声明性代码,该代码是直接与 DOM 交互的命令式代码(例如 the diffing algorithm)之上的抽象层。如果您需要编写命令式代码(即直接与 DOM 交互),React 提供了 Refs as an escape hatch

答案 6 :(得分:1)

声明式编程是一种编程风格,其中应用程序以优先级的方式构建 描述应该发生什么,而不是定义它应该如何发生。

为了理解声明式编程,让我们将它与命令式编程(编程风格只是 关心如何用代码实现结果)。

示例:使字符串对 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 是 一种描述应该发生的事情的方式:字符串中的空格 应该更换。如何处理空间的细节是 在替换函数内部抽象出来。

在声明式程序中,语法本身描述应该发生的事情,并且抽象出事情如何发生的细节。

本质上,声明式编程生成的应用程序更易于推理,当应用程序更易于推理时,该应用程序更易于扩展。有关声明式编程范式的其他详细信息,请访问 Declarative Programming 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 行代码 命令式构造。

现在让我们来看看如何以声明方式构建 DOM 使用 React 组件:

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 为 目标

来源:Modern Patterns for Developing React Apps

答案 7 :(得分:0)

  • 声明式允许您控制所有视图。 (例如状态管理)
  • 命令式允许您控制视图。 (如$(this))

答案 8 :(得分:0)

命令性代码指示JavaScript如何执行每个步骤。使用声明性代码,我们可以告诉JavaScript我们要做的事情,然后让JavaScript负责执行这些步骤。

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

答案 9 :(得分:0)

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

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

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

我们声明所需的内容,而不是使用.addEventListener()来设置事件处理。单击该按钮后,它将运行activateTeleporter函数。

答案 10 :(得分:0)

命令式世界中的现实生活将进入啤酒酒吧,并向调酒师提供以下说明: -从架子上拿杯 -将玻璃杯放在草稿的前面 -拉下把手直到玻璃杯装满 -把玻璃杯递给我。

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

以声明式方式询问啤酒的假设是,酒保知道如何提供啤酒,这是声明式编程工作方式的重要方面。

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

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

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

答案 11 :(得分:0)

声明性编程是一种编程范式…,它表示计算的逻辑而不描述其控制流程。

命令式编程是一种编程范式,它使用可更改程序状态的语句。

参考链接:-https://codeburst.io/declarative-vs-imperative-programming-a8a7c93d9ad2

答案 12 :(得分:0)

声明式与命令式

声明式编程就像要求您的朋友粉刷您的房屋。您不在乎他们如何清洁它,他们用来绘画的颜色,他们用来完成它的资源有多少。”

//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

答案 13 :(得分:0)

解释每一步是势在必行的方法,例如我们必须用 Hello World 创建段落标签!里面的文字。

//Imperative 

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

定义所需的最终目标状态而不指定确切的程序。即带有 text 的 p 标签,不告诉 createElement 或 innerText

 //Delcartive 

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

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