了解反应的虚拟dom,并使用此知识加快应用程序。在这个全面入门的框架内部入门中,我们将揭开JSX的神秘化,让您展示如何做出反应,解释如何找到瓶颈,并分享一些避免常见错误的提示。
反应的原因之一一直动摇着前端世界,并没有下降的迹象,它平易近人的学习曲线:在你绕着头后,学习曲线。n.JSX还有整个“国家vs.道具“概念,你可以走了。
如果你已经熟悉了反应工作的方式,你可以直接跳到“修理东西”.
但是要真正掌握自己的反应,你需要思考反应。这篇文章是想帮你解决这个问题。看看所做的反应表我们的项目之一:
一个巨大的反应表ebay业务.
使用数百条动态的、多层的行,理解框架的细点对于保证用户体验的顺利进行至关重要。
当事情发生的时候你肯定会感觉到。输入字段会得到laggy,复选框会先检查一下,情态动词会出现困难的时候。
为了解决这些问题,我们需要覆盖整个旅程,一个反应组件从定义到您定义(然后更新)页面上。系好安全带!
过程中已知的前端圈为“transpiling”,即使“编译”将是一个更正确的术语。
反应开发人员敦促您在编写组件时使用名为JSX的html和javascript组合。然而,浏览器对于JSX及其语法没有任何线索。浏览器只理解简单javascript,所以必须将JSX转换成它。下面是一个div
它有一个类和一些内容:
className='cn'>
Content!
“正式”javascript中的相同代码只是一个带有若干参数的函数调用:
React.createElement(
'div',
{ className: 'cn' },
'Content!'
);
让我们仔细看看这些论点。二是一个元素类型。对于html标记,它将是一个带有标记名的字符串。第二个参数是一个对象,它包含所有元素属性。如果没有空对象,它也可以是一个空对象。下面所有的论点都是元素的孩子。元素中的文本也作为子元素计数,因此字符串“内容!”作为函数调用的第三个参数放置。
你已经可以想象当我们有更多孩子时会发生什么:
className='cn'>
Content 1!
/>
Content 2!
React.createElement(
'div',
{ className: 'cn' },
'Content 1!', // 1st child
React.createElement('br'), // 2nd child
'Content 2!' // 3rd child
)
我们的函数现在有五个参数:元素类型、属性对象和三个子元素。因为我们的一个孩子也是一个众所周知的反应,它将被描绘成一个函数调用。
现在,我们已经覆盖了两种类型的儿童:平原String
或者另一个电话React.createElement
。然而,其他价值也可以作为论据:
false
, null
, undefined
以及true
数组是用来作为一个参数分组并传递的子元素:
React.createElement(
'div',
{ className: 'cn' },
['Content 1!', React.createElement('br'), 'Content 2!']
)
当然,反应的力量来自于html规范中描述的标签,但是来自用户创建的组件,例如:
function Table({ rows }) {
return (
<table>
{rows.map(row => (
<tr key={row.id}>
<td>{row.title}</td>
</tr>
))}
</table>
);
}
组件允许我们将模板破坏成可重用的块。在一个示例中,“功能”上面的组件接受数组行数据的对象数组,并返回单个 每当我们把组件放置到这样的布局中: 从浏览器的角度来看,我们写了这篇文章: 注意,这次我们的第一个参数不是 所以,我们已经将所有的JSX组件都转换成纯javascript,现在我们有了一系列函数调用,其中还有其他函数调用,还有其他函数调用…如何将它们转换成构成web页面的dom元素? 为了这个,我们有一个 何时 这些对象构成了虚拟dom在反应上的意义。 它们将在所有进一步渲染中相互比较,最终转换为实dom(与虚拟). 下面是另一个例子:这次使用 变成: 注意,过去使用的是单独的参数 此外,我们可以将孩子直接添加到在代码中,结果仍然是一样的: 构建了虚拟dom对象之后, 如果 如果我们有一个函数或类 如果有什么 因此,我们得到以下html(对于我们的表示例): 在实践中, 注意:“重新”在标题中!当我们想要做的时候,真正的反应就开始了更新一页没有取代一切。我们怎么能做到这一点也没有什么办法。让我们从最简单的一个调用开始 这次,上面的代码将与我们已经看到的不同。响应将从零开始创建所有Dom节点,并将它们放到页面上,而响应将开始和解(或“diffing”)算法来确定必须更新节点树的哪些部分,并且可以保持未受影响。 那么,它是如何工作的呢?只有几个简单的场景和理解他们我们的优化将会帮助我们很多。请记住,我们现在正在查看作为响应虚拟dom中节点的表示形式的对象。 这是最简单的例子:dom保持不变。 作为我们 当响应现在看到类型不同时,它甚至不会尝试更新我们的节点:旧元素将会被删除(下装) 和所有的孩子一起。因此,对于完全不同的高级别dom树的元素替换一个元素可能非常昂贵。幸运的是,在现实世界里很少发生这种事。 记住反应用途是很重要的 接下来的场景更有趣,因为这就是我们经常使用反应的方式。 “可是什么都没变!”你也许会说,你会错的。 注意组件的 如果 除了上面描述的四个常见场景外,我们还需要考虑当元素有多个子时的响应行为。我们假设我们有这样一个元素: 我们想把这些孩子们洗牌: 然后呢? 如果“diffing”,反应就会看到任何内部阵列 幸运的是,反应有内建来解决这个问题。如果元素具有 直到现在我们才接触到 所以,我们有一个 呼叫 我们已经准备好了小演示应用程序所以在我们去修它们之前,你可以看到野外最常见的问题。您可以查看它的源代码。这里。你也需要反应开发工具所以请确保您安装了它们为您的浏览器。 我们首先要看的是哪些元素和何时使虚拟dom被更新。导航到浏览器的dev工具中的响应面板,并选择“突出更新”复选框: 在chrome中使用“突出更新”复选框进行响应 现在尝试将一行添加到表中。正如您所看到的,在页面上每个元素周围都出现了边框。这意味着每次添加一行时,响应都是计算和比较整个虚拟dom树。现在尝试在一行中打一个计数器按钮。您可以看到虚拟dom在更改时如何更新 对问题可能发生的地方做出反应,但告诉我们细节:尤其是更新问题意味着“diffing”元素或挂载/重新设置它们。为了找到更多的信息,我们需要使用反应的内置探查器(注意它不会在生产模式中工作)。 加 反应DevTools‘性能’选项卡 在所产生的输出中,我们对“用户计时”感兴趣。缩放到时间线直到看到“反应树协调”组及其孩子。这些都是我们的组件的名称[最新情况]或[山]在他们旁边。 我们的大部分业绩问题都属于这两类。 无论是组件(以及来自它的所有分支)都是出于某些原因重新安装在每个更新上,我们不希望它(重新安装慢),或者我们正在执行昂贵的和解,大型分支,尽管没有任何改变。 现在,当我们发现了一些关于如何做出反应决定更新虚拟dom并了解如何查看幕后发生的事情时,我们终于准备好修复事情了!首先,让我们来处理坐骑/unmounts。 如果您仅仅考虑到任何元素/组件的多个子元素都表示为列阵内部。 考虑到这一点:React.createElement
请呼叫元素及其行作为子。
rows={rows} />
React.createElement(Table, { rows: rows });
String
描述一个html元素,但是对我们定义的函数的引用当我们编码我们的组件时。我们的属性现在是我们的props
.将组件放在页面上
ReactDOM
图书馆及其render
方法:function Table({ rows }) { /* ... */ } // defining a component
// rendering a component
ReactDOM.render(
React.createElement(Table, { rows: rows }), // "creating" a component
document.getElementById('#root') // inserting it on a page
);
ReactDOM.render
被称为React.createElement
最后调用了它,它返回以下对象:// There are more fields, but these are most important to us
{
type: Table,
props: {
rows: rows
},
// ...
}
div
具有类属性和若干子属性:React.createElement(
'div',
{ className: 'cn' },
'Content 1!',
'Content 2!',
);
{
type: 'div',
props: {
className: 'cn',
children: [
'Content 1!',
'Content 2!'
]
}
}
React.createElement
函数在a/s之下找到了它们的位置children
内钥匙props
。所以它无所谓如果孩子作为数组或参数列表传递-在生成的虚拟dom对象中,它们最终都会一起结束。ReactDOM.render
将尝试将其转换为我们浏览器可以根据这些规则显示的Dom节点:
type
属性持有弦使用标记名称-创建一个标记,其中列出了下面列出的所有属性props
.type
-调用它并递归地重复一个结果。children
下props
-逐个重复这个过程,并将结果放置在父节点的Dom节点内。
...
Title
重建在
render
通常会在根元素上调用一次,并且进一步更新通过state
.ReactDOM.render
对于同一个节点再一次.// Second call
ReactDOM.render(
React.createElement(Table, { rows: rows }),
document.getElementById('#root')
);
type
是一个字符串,type
在电话里保持相同的距离props
也没有改变。// before update
{ type: 'div', props: { className: 'cn' } }
// after update
{ type: 'div', props: { className: 'cn' } }
type
仍然是同一个字符串,props
是不同的。// before update:
{ type: 'div', props: { className: 'cn' } }
// after update:
{ type: 'div', props: { className: 'cnn' } }
type
仍然表示html元素响应知道如何通过标准的Dom调用更改其属性,而无需从一种树中删除节点。
type
已经变了不同String
或从String
到组件上。// before update:
{ type: 'div', props: { className: 'cn' } }
// after update:
{ type: 'span', props: { className: 'cn' } }
===
(三倍等于)比较type
值,所以它们必须是相同的实例同一类或同功能。
type
是一个组件。// before update:
{ type: Table, props: { rows: rows } }
// after update:
{ type: Table, props: { rows: rows } }
render
(只有类组件已显式定义了此方法),但与ReactDOM.render
。“渲染”这个词的确在反应世界中确实被过度使用了。type
是对函数或类(即正则反应组件)的引用,并且我们开始了树的调整过程,然后反应总是试图看内组件以确保返回的值返回render
没有改变(预防副作用的一种预防)。冲洗和重复每一个组件下树-是的,复杂的渲染也可能变得昂贵!照顾孩子
// ...
props: {
children: [
{ type: 'div' },
{ type: 'span' },
{ type: 'br' }
]
},
// ...
// ...
props: {
children: [
{ type: 'span' },
{ type: 'div' },
{ type: 'br' }
]
},
// ...
props.children
它开始比较它中的元素与它之前看到的数组中的元素,然后依次查看它们:索引0将与索引0、索引1和索引1等进行比较。对于每一对,反应将应用上面描述的规则集。在我们的例子中,它看到div
变成了一个span
所以设想3将被应用。这并不是非常有效的:想象一下,我们已经从1000行表中删除了第一行。反应将不得不“更新”剩余999名儿童,因为他们的内容现在不会相等,如果与先前的代表指数相比,则是相等的。key
属性将比较元素的值。key
不是按指数来的。只要钥匙是独一无二的,反应就会围绕着元素移动无将它们从Dom树中移除然后将它们放到后面(在响应中已知的过程为安装/卸载).// ...
props: {
children: [ // Now React will look on key, not index
{ type: 'div', key: 'div' },
{ type: 'span', key: 'span' },
{ type: 'br', key: 'bt' }
]
},
// ...
当状态发生变化时
props
反应哲学的一部分,但忽略了state
。下面是一个简单的“有状态”组件:class App extends Component {
state = { counter: 0 }
increment = () => this.setState({
counter: this.state.counter + 1,
})
render = () => (<button onClick={this.increment}>
{'Counter: ' + this.state.counter}
</button>)
}
counter
密钥在我们的状态对象中。单击按钮时会增加其值并更改按钮文本。但是当我们这么做时,在一个Dom里发生了什么?其中哪些部分将重新计算和更新?this.setState
也会导致重新渲染,但不会导致整个页面,但是只有一个组件本身及其孩子。父母和兄弟姐妹都是免费的。当我们拥有一棵大树时,这很方便,我们只想重绘它的一部分。钉住问题
state
-只有有关因素及其子女受到影响。?react_perf
对于您的应用程序的任何url,并进入chrome浏览器中的“性能”选项卡。点击录制按钮并点击桌子周围。添加一些行,更改一些计数器,然后点击“停止”。
修理东西:安装/安装
/>