reactjs React中的核对详细说明

piah890a  于 2023-01-25  发布在  React
关注(0)|答案(2)|浏览(163)

我是新的ReactJS。谁能解释一下和解究竟是如何工作的。我曾试图了解它从React官方网站,但没有得到它。

rdrgkggo

rdrgkggo1#

我是这样理解的:
你会同意react使用组件使事情变得简单和快速。有了JSX,我们可以使用户定义的组件变得更容易。最后,所有这些都被翻译成纯JavaScript(我假设您理解React.CreateElement的工作原理)函数调用将其他函数调用作为其参数/属性保存其他函数调用,等等。无论如何,我们没有什么可担心的,因为react在内部自己做这件事。
但是这是如何给我们一个UI的呢?为什么它比其他UI库更快呢?
〈-- ALL HAIL ReactDOM库和呈现方法--〉
一个普通的ReactDOM调用如下所示:

// I have avoided the usage of JSX as its get transpiled anyway 
ReactDOM.render(
  React.createElement(App, { //if any props to pass or child }),    // "creating" a component
  document.getElementById('#root')              // inserting it on a page
);
Heard about VirtualDOM ? { yes : 'Good'} : { no : 'still Good'} ;

React.createElement构造元素对象,其类型和属性基于我们编写的组件,并将子元素放置在props内的children键下。它递归地执行此操作,并填充最终对象,该对象准备转换为HTML等效项并绘制到浏览器。
这就是VirtualDOM,它驻留在react内存中,react在这个内存上执行所有操作,而不是在实际的浏览器DOM上。

{
  type: 'div',// could be other html'span' or user-diff 'MyComponent'
  props: {
    className: 'cn',
    //other props ...
    children: [
      'Content 1!', // could be a component itself
      'Content 2!', // could be a component itself
      'Content n!', // could be a component itself
    ]
  }
}

构建虚拟DOM对象后,ReactDOM.render会将其转换为DOM节点,浏览器可以根据这些规则绘制UI:
如果type属性包含一个带有标记名的字符串,则创建一个标记,并将所有属性列在props下;如果type下有一个函数或类,则调用它并对结果递归地重复该过程;如果props下有任何子级,则对每个子级逐一重复该过程,并将结果放入父级的DOM节点中。
浏览器将其绘制到UI,这是一个昂贵的任务。React非常聪明地理解了这一点。更新组件意味着创建一个新对象并绘制到UI。即使涉及到一个小的变化,它也会使整个DOM树重新创建。那么我们如何使浏览器不必每次都创建DOM,而只绘制必要的东西呢?
这就是我们需要React的Reconciliation和diffing算法的地方。多亏了React,我们不必自己手动完成,它在内部得到了处理here is a nice article to understand deeper
现在,您甚至可以引用official React docs for Reconsiliation
值得注意的几点:
React基于以下两个假设实现了一个启发式O(n)算法:1)两个不同类型的元素将生成不同的树。2)开发人员可以使用关键属性提示哪些子元素在不同的渲染中可能是稳定的。
在实践中,这些假设几乎适用于所有实际用例,如果不满足这些假设,将导致性能问题。
我只是复制粘贴几个其他点只是为了给予一个想法是如何做到的:
比较:当比较两棵树时,React首先比较两个根元素。根据根元素的类型,行为会有所不同。
场景1:type是一个字符串,type在调用中保持不变,props也不变。

// before update
{ type: 'div', props: { className: 'cn' , title : 'stuff'} }

// after update
{ type: 'div', props: { className: 'cn' , title : 'stuff'} }

这是最简单的情况:DOM保持不变。
场景2:字符串类型仍然相同, prop 不同。

// before update:
{ type: 'div', props: { className: 'cn' } }

// after update:
{ type: 'div', props: { className: 'cnn' } }

因为type仍然表示HTML元素,所以React查看这两者的属性,React知道如何通过标准API调用更改其属性,而无需从DOM树中删除底层DOM节点。
React还知道只更新已更改的属性。例如:

<div style={{color: 'red', fontWeight: 'bold'}} />

<div style={{color: 'green', fontWeight: 'bold'}} />

在这两个元素之间转换时,React知道只修改颜色样式,而不是fontWeight。
///////当组件更新时,示例保持不变,以便在呈现过程中保持状态。React更新基础组件示例的属性以匹配新元素,并调用componentWillReceiveProps()和组件将更新()。接下来,呈现()方法,diff算法在前一个结果和新结果上递归,处理完DOM节点后,React在子节点上递归。
场景3:type已更改为不同的String,或从String更改为组件。

// before update:
{ type: 'div', props: { className: 'cn' } }

// after update:
{ type: 'span', props: { className: 'cn' } }

由于React现在看到类型不同,它甚至不会尝试更新我们的节点:旧元素将与它的所有子元素一起移除(卸载)。
一定要记住,React使用===(三重等于)来比较类型值,因此它们必须是同一个类或同一个函数的相同示例。
场景4:类型是一个组件。

// before update:
{ type: Table, props: { rows: rows } }

// after update:
{ type: Table, props: { rows: rows } }

“但什么都没变!",你可能会说,你会错的。
如果type是一个函数或类的引用(也就是说,您的常规React组件),并且我们启动了树协调过程,那么React将始终尝试查看组件内部,以确保呈现时返回的值没有更改(某种程度上是对副作用的预防)。冲洗并重复树下的每个组件-是的,复杂的呈现可能也会变得昂贵!
为了确保这些事情真相大白:

class App extends React.Component {

  state = {
    change: true
  }

  handleChange = (event) => {
    this.setState({change: !this.state.change})
  }

  render() {
    const { change } = this.state
    return(
      <div>
        <div>
          <button onClick={this.handleChange}>Change</button>
        </div>
        {
          change ? 
          <div>
            This is div cause it's true
            <h2>This is a h2 element in the div</h2>
          </div> :
          <p>
            This is a p element cause it's false
            <br />
            <span>This is another paragraph in the false paragraph</span>
          </p>
        }
      </div>
    )
  }
}

儿童==========================〉
当一个元素有多个子元素时,我们还需要考虑React的行为。假设我们有这样一个元素:

// ...
props: {
  children: [
      { type: 'div' },
      { type: 'span' },
      { type: 'br' }
  ]
},
// ...

我们想让这些孩子们四处走动:

// ...
props: {
  children: [
    { type: 'span' },
    { type: 'div' },
    { type: 'br' }
  ]
},
// ...

然后呢?
如果在"比较"过程中,React看到props. children中的任何数组,它会开始将其中的元素与之前看到的数组中的元素按顺序进行比较:索引0将与索引0进行比较,索引1将与索引1进行比较,等等。对于每一对,React将应用上述规则集。
React有一个built-in way来解决这个问题,如果一个元素有key属性,那么元素将通过key的值来比较,而不是通过索引来比较,只要key是唯一的,React就会移动元素,而不会将它们从DOM树中移除,然后再放回去(这个过程在React中称为挂载/卸载)。
因此Keys应该是稳定的、可预测的和唯一的。不稳定的键(如Math. random()生成的键)会导致许多组件示例和DOM节点被不必要地重新创建,这会导致性能下降和子组件的状态丢失。
因为React依赖于启发式,所以如果不满足启发式背后的假设,性能就会受到影响。
状态改变时:=========================================〉
调用这个. setState也会导致重新呈现,但不是整个页面,而只是组件本身和它的子组件。父组件和兄弟组件都被省略了。当我们有一棵大树,并且只想重绘它的一部分时,这很方便。

bq9c1y66

bq9c1y662#

React上下文中的协调意味着使React的虚拟DOM树与浏览器的真实的DOM树一致。
关键在于,无法保证React虚拟DOM的特定元素在其整个生命周期中引用浏览器的同一DOM节点。这是因为React采用了高效更新DOM的方法。如果组件包含动态或有状态子组件,则可以使用特殊的key属性来解决此问题。

相关问题