html列表中的key属性有什么用-亚博电竞手机版

html列表中的key属性有什么用

这篇文章给大家分享的是有关html列表中的key属性有什么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

diff 算法的时间复杂度是 o(n), 它的实现是基于以下两个假设:

两个 type 不同的元素生成不同的 dom 树;

在两次渲染中,react 能通过 key 属性判断哪一个子元素保持不变;

本文内容围绕「假设 2」展开说明 diff 算法是怎么做到 o(n) 的时间复杂度的。

如果没有 key 属性

当遍历子元素列表的时候,react 会同时遍历新旧子元素列表,一旦遇到两个列表有不同的地方,就会生成一个 mutation。

  • duke
  • villanova
  • connecticut
  • duke
  • villanova

例如,当在子元素列表之前添加一个元素的时候,如果没有 key 属性,react 会同时遍历新旧子元素数组:

第一个 li 标签有变化,更新;

第二个 li 标签有变化,更新;

最后插入第三个 li 标签;

这个算法的时间复杂度是 o(n),但是会导致很多不必要的 dom 操作,性能低下。如果通过一种列表对比算法能避免掉不必要的 dom 操作,就能优化性能。

levenshtein distance 算法

为了使用算法优化性能,让我们对上面的问题做一个抽象,旧的子元素列表:

[1, 2, 3, 4, 5]

进行了一系列 dom 节点的删除、插入、修改的操作之后,得到新的列表:

[6, 3, 1, 2, 5, 4]

知道了新旧的顺序求最小的插入、删除、修改操作,这是数组的最小编辑距离问题。最常见的是 levenshtein distance 算法。

举一个例子:求 beaut( [公式] ) 和 ebau( [公式] ) 的最小编辑距离,其中 [公式] 是未知字符。动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。定义长度为「i - 1」的字符串 str1 和长度为「j - 1」的字符串 str2 的最小编辑距离为 [公式] ,那么 beaut( [公式] )和 ebau( [公式] ) 的最小编辑记 [公式] ,[公式] 能且只能基于下面三个状态:

「状态 1」 :beaut( [公式] ) 和 ebau 的最小编辑距离已知,记为 [公式] ;

「状态 2」:beaut 和 ebau( [公式] ) 的最小编辑距离已知,记为 [公式] ;

「状态 3」:beaut 和 ebau 的最小编辑距离已知,记为 [公式] ;

[公式] 和三个状态之间的关系分别为下面三个公式:

[公式]

[公式]

[公式]

解释一下第三个公式,在 [公式] 的情况下, [公式] ;如果 [公式] ,那么 [公式] 。那么上面三个公式值最小的那个就是 [公式] 的解。对照着下图看,也就是 [公式] 的值可以通过其上方,左边,左上对角线的值确定,公式如下:

[公式]

[公式]

[公式]

function mindistance(s1, s2) {

const len1 = s1.length

const len2 = s2.length

let matrix = []

for (let i = 0; i <= len1; i ) {

// 构造二维数组

matrix[i] = new array()

for (let j = 0; j <= len2; j ) {

// 初始化

if (i == 0) {

matrix[i][j] = j

} else if (j == 0) {

matrix[i][j] = i

} else {

// 进行最小值分析

let cost = 0

if (s1[i - 1] != s2[j - 1]) { // 相同为0,不同置1

cost = 1

}

const temp = matrix[i - 1][j - 1] cost

matrix[i][j] = math.min(matrix[i - 1][j] 1, matrix[i][j - 1] 1, temp)

}

}

}

return matrix[len1][len2] //返回右下角的值

}

从上面的代码可以看出,ld 算法有两个嵌套的 for 循环,所以时间复杂度是 o(n*m),其中 n 为 str1 的长度,m 为 str2 的长度。如果采用 ld 算法,虽然能避免掉不必要的 dom 操作,但是 diff 算法的时间复杂度就达不到线性了。

key 属性的引入思路

直接应用 lp 算法不能解决我们的问题。我们并不需要找到真正的最小编辑距离,而是需要找到一种算法,这个算法的时间复杂度必须是 o(n),并且能避免掉大部分的 dom 操作,而前端的列表大部分的操作是子元素的移动。通过引入 key 属性唯一标识子元素,我们可以把最小编辑距离转化成:key 属性是对这个元素的唯一标识,在这个条件下,求新旧子元素列表的最小插入、删除、移动操作。这是一种启发式算法。

旧的子元素列表:

[{ key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 }]

新的子元素列表:

[{ key: 4, val: 4 }, { key: 1, val: 1 }, { key: 3, val: 3 }, { key: 2, val: 2 }]

react 子元素近似最小编辑距离算法

为了寻找上面两个数组的近似最小编辑距离,react 的做法为,正向遍历新的子元素,用新的子元素的 key 值去旧的子元素中查找,如果没找到,就做插入;如果找到了就做移动操作;如果遇到旧的子元素在新的列表中找不到的情况,删除旧的子元素。算法的时间复杂度是 o(n)。 举一个例子,如下图,上一排是旧的子元素列表,下一排是新的子元素列表:

代码如下,正向遍历 nextchildren:

「元素 6」: 是新增元素,新增到 index = 0 的位置;

「元素 3」: 不变;

「元素 1」: 元素 1 在原数组中的位置在元素 3 之前,所以需要移动到元素 3 的后面;

「元素 2」: 元素 2 在原数组中的位置也位于 3 之前,移动到元素 1 的后面;

「元素 5」: 不变;

「元素 4」: 移动到 5 后面;

updatechildren() {

// find removed

const removednodes = findremoved();

var updates = [];

var lastindex = 0;

var nextindex = 0;

var lastplacednode = null;

for (const name in nextchildren) {

var prevchild = prevchildren && prevchildren[name];

var nextchild = nextchildren[name];

if (prevchild === nextchild) {

// 移动子元素

if (prevchild._mountindex < lastindex) {

updates.push({

type: "move_existing",

fromindex: prevchild._mountindex,

toindex: nextindex,

afternode: afternode

})

}

lastindex = math.max(prevchild._mountindex, lastindex);

prevchild._mountindex = nextindex;

} else {

if (prevchild) {

lastindex = math.max(prevchild._mountindex, lastindex);

}

// 新增子元素

updates.push({

type: "insert_markup",

toindex: nextindex,

content: nextchild

});

}

nextindex ;

lastplacednode = getnativenode(nextchild); // 获取 nextchild 对应的 dom 节点

}

// 删除

for (const name in removednodes) {

updates.push({

type: "remove_node",

content: null,

fromindex: prevchildren[name]._mountindex,

fromnode: removednodes[name]

})

}

}

和 vue 的比较

vue 对子元素的 diff 的思路和 react 一样,都引入了「key 是子元素的唯一标识」这一先决条件。在引入这一条件后,比 react 做了更多的优化,vue 是从新老子元素列表的两头向中间遍历,并多做了一些特殊判断。就列表更新这一块,vue 的性能高于 react。而在 fiber 架构中,由于没有反向指针,react 不容易做到通过双向遍历优化子元素 diff 算法。

key 属性最佳实践

key 属性帮助 react 识别哪些元素改变了,哪些元素是新增的,哪些被删除了。元素列表中的元素的 key 值应该是稳定的,能起到唯一标识的作用。下面列举一些最佳实践:

1. 使用能在子元素列表之间能唯一标识这个子元素的字符串做为其 key 属性。用数组的 index 做为 key 值是一种反模式,需要避免。

const todoitems = todos.map((todo) =>

  • {todo.text}

  • );

    2. key 会传递信息给 react ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值。

    const content = posts.map((post) =>

    key={post.id}

    id={post.id}

    title={post.title} />

    );

    感谢各位的阅读!关于“html列表中的key属性有什么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

    展开全文
    内容来源于互联网和用户投稿,文章中一旦含有亚博电竞手机版的联系方式务必识别真假,本站仅做信息展示不承担任何相关责任,如有侵权或涉及法律问题请联系亚博电竞手机版删除

    最新文章

    网站地图