浅层比较如何起作用

Aja*_*aur 74 javascript reactjs

在React的这个文档中,据说

shallowCompare对当前props和nextProps对象以及当前state和nextState对象执行浅等式检查.

我无法理解的事情是If It浅层比较对象,那么shouldComponentUpdate方法将始终返回true,如

我们不应该改变国家.

如果我们没有改变状态,那么比较将始终返回false,因此shouldComponent更新将始终返回true.我很困惑它是如何工作的,我们将如何覆盖它以提高性能.

And*_*yco 107

浅比较检查是否平等.比较标量值(数字,字符串)时,它会比较它们的值.比较对象时,它不会比较它们的属性 - 只比较它们的引用(例如"它们是否指向同一个对象?").

让我们考虑一下user对象的形状

user = {
  name: "John",
  surname: "Doe"
}
Run Code Online (Sandbox Code Playgroud)

例1:

const user = this.state.user;
user.name = "Jane";

console.log(user === this.state.user); // true
Run Code Online (Sandbox Code Playgroud)

请注意您更改了用户名.即使有这种变化,对象也是相同的.他们的参考完全相同.

例2:

const user = clone(this.state.user);
console.log(user === this.state.user); // false
Run Code Online (Sandbox Code Playgroud)

现在,对对象属性没有任何改变,它们完全不同.通过克隆原始对象,您可以创建具有不同引用的新副本.

克隆函数可能看起来像这样(ES6语法)

const clone = obj => Object.assign({}, ...obj);
Run Code Online (Sandbox Code Playgroud)

浅比较是检测变化的有效方法.它希望你不要改变数据.

  • @AjayGaur虽然这个答案可以帮助你理解JavaScript中的严格相等(===),但它并没有告诉你关于React中的shallowCompare()函数的任何内容(我想回答者误解了你的问题).shallowCompare()的作用实际上在您提供的文档中:迭代要比较的对象的键,并在每个对象中的键值不严格相等时返回true.如果您仍然不理解这个功能以及为什么不应该改变状态,我可以为您写一个答案. (19认同)
  • 这不是真的.看到这个.https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js#L39-L67 (5认同)
  • 该答案描述了JS中的等于(==)和严格等于(===)运算符之间的区别。问题是浅层比较,在React中通过检查两个对象的所有道具之间的相等性来实现。 (4认同)
  • @sunquan 的评论在某种程度上是不正确的。`shallowCompare` 的正确行为是:通过迭代对象上的键来执行相等性,并在任何键的值在参数之间**不严格相等**时返回 **false**。**当所有键的值严格相等时返回 true。** (2认同)

sup*_*upi 18

浅层比较是指所比较的对象的属性是使用"==="或严格相等来完成的,并且不会对属性进行更深入的比较.例如

// a simple implementation of the shallowCompare.
// only compares the first level properties and hence shallow.
// state updates(theoretically) if this function returns true.
function shallowCompare(newObj, prevObj){
    for (key in newObj){
        if(newObj[key] !== prevObj[key]) return true;
    }
    return false;
}
// 
var game_item = {
    game: "football",
    first_world_cup: "1930",
    teams: {
         North_America: 1,
         South_America: 4,
         Europe: 8 
    }
}
// Case 1:
// if this be the object passed to setState
var updated_game_item1 = {
    game: "football",
    first_world_cup: "1930",
    teams: {
         North_America: 1,
         South_America: 4,
         Europe: 8 
    }
}
shallowCompare(updated_game_item1, game_item); // true - meaning the state
                                               // will update.
Run Code Online (Sandbox Code Playgroud)

虽然两个对象看起来都是一样的,game_item.teams但引用的不一样updated_game_item.teams.要使两个对象相同,它们应指向同一个对象.因此,这导致被评估的状态被更新

// Case 2:
// if this be the object passed to setState
var updated_game_item2 = {
    game: "football",
    first_world_cup: "1930",
    teams: game_item.teams
}
shallowCompare(updated_game_item2, game_item); // false - meaning the state
                                               // will not update.
Run Code Online (Sandbox Code Playgroud)

这次,每个属性都返回true进行严格比较,因为新旧对象中的teams属性指向同一个对象.

// Case 3:
// if this be the object passed to setState
var updated_game_item3 = {
    first_world_cup: 1930
}
shallowCompare(updated_game_item3, game_item); // true - will update
Run Code Online (Sandbox Code Playgroud)

updated_game_item3.first_world_cup属性未通过严格评估,因为1930是数字而game_item.first_world_cup字符串.如果比较松散(==),这将会过去.尽管如此,这也将导致状态更新.

补充说明:

  1. 进行深度比较是没有意义的,因为如果状态对象是深度嵌套的话,它会显着影响性能.但是如果它不是太嵌套而你仍然需要深度比较,那么在shouldComponentUpdate中实现它并检查它是否足够.
  2. 您绝对可以直接改变状态对象,但组件的状态不会受到影响,因为它在setState方法流中的react实现了组件更新循环挂钩.如果直接更新状态对象以故意避免组件生命周期挂钩,那么可能您应该使用简单的变量或对象来存储数据而不是状态对象.


akh*_*ary 14

浅比较通过检查基本值(例如字符串,数字)和对象的情况下两个值是否相等来进行工作,它仅检查引用。因此,如果您浅比较深层嵌套的对象,它将只检查引用而不是该对象内部的值。


val*_*lex 9

在React中还有浅析比较的遗留解释:

shallowCompare对当前props和nextProps对象以及当前state和nextState对象执行浅等式检查.

它通过迭代被比较的对象的键来实现这一点,并且当每个对象中的键的值不严格相等时返回true.

UPD:当前的文档说浅浅的比较:

如果React组件的render()函数在给定相同的props和state的情况下呈现相同的结果,则在某些情况下可以使用React.PureComponent来提高性能.

React.PureComponent的shouldComponentUpdate()只是浅浅地比较对象.如果这些包含复杂的数据结构,则可能会产生更深层次差异的假阴性.只有当你希望有简单的道具和状态时才扩展PureComponent,或者当你知道深层数据结构已经改变时使用forceUpdate()

UPD2:我认为和解也是浅析比较理解的重要主题.


小智 7

对于某些人来说,接受的答案可能有点误导。

user = {
  name: "John",
  surname: "Doe"
}

const user = this.state.user;
user.name = "Jane";

console.log(user === this.state.user); // true
Run Code Online (Sandbox Code Playgroud)

该语句特别是“注意您更改了用户名。即使进行此更改,对象也是相等的。它们的引用完全相同。”

当您对 javascript 中的对象执行以下操作时:

const a = {name: "John"};
const b = a;
Run Code Online (Sandbox Code Playgroud)

改变两个变量中的任何一个都会改变它们,因为它们具有相同的引用。这就是为什么它们总是==, ===, Object.is()彼此相等 ( ) 的原因。

现在对于React,以下是浅层比较函数: https://github.com/facebook/fbjs/blob/master/packages/fbjs/src/core/shallowEqual.js

/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 */
function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  return
Run Code Online (Sandbox Code Playgroud)

对于非基元(对象),它检查:

  1. 如果第一个对象等于(使用Object.is())第二个对象。
  2. 如果不是,它会检查第一个对象中的每个键值对是否等于(使用Object.is())第二个对象中的键值对。这是针对第一级密钥完成的。如果该对象的键的值是另一个对象,则此函数不会检查该对象深度的相等性。


win*_*mao 6

我花了一段时间才真正知道shallow compare===是两个不同的东西,特别是在阅读下面的 redux 文档时。

但是,当将操作分派到 Redux 存储时,useSelector() 仅在选择器结果与最后结果不同时强制重新渲染。从 v7.1.0-alpha.5 开始,默认比较是严格的 === 引用比较。这与 connect() 不同,后者对 mapState 调用的结果使用浅层相等检查来确定是否需要重新渲染。这对您应该如何使用 useSelector() 有几个影响。

严格平等

所以一步一步地,严格相等===是由Javascript语言非常一致地定义的,https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality

它的作用是,如果两个项是原始项,则按值进行比较;如果它们是对象,则按引用进行比较。当然,如果两个对象的类型不同,它们将永远不会匹配。

浅比较

浅层可能不是该语言的内置功能。这里的几个答案向我们指出了实现的一些变化,https://github.com/facebook/fbjs/blob/main/packages/fbjs/src/core/shallowEqual.js

这个想法是通过值比较两个项目(如果它们是原始的)。但对于非基元,我们降低一级。对于对象来说,如果两个对象的键不同,我们就说它们不相同。如果某个键下的值不同,我们说它们也不相同。

概括

这意味着,浅比较检查比严格 equal 更多===,特别是当涉及到对象时。快速浏览一下可能会表明,===不需要做太多猜测工作。