De pie sobre los hombros de gigantes y viendo vue3 - Capítulo 11 Algoritmo de diferencia rápida

De pie sobre los hombros de gigantes y viendo Vue, el diseño y la implementación de Vue de Huo Chunyang. El autor interpreta la implementación de la capa inferior de vue3 paso a paso en forma de pregunta. La idea es muy ingeniosa. Aquí, la lógica principal del libro se conectará en serie, que también es un registro después de leerlo. Espero que a través de esta forma pueda comunicarme y aprender con todos.

apertura

El algoritmo rápido se usa en vue3, basándose en el preprocesamiento en text Diff, procesando primero el pre-nodo y el post-nodo en la misma posición de los nodos antiguo y nuevo. Cuando el nodo frontal y el nodo posterior se procesan, hay tres situaciones:

Después de que se procesan los mismos nodos anteriores y posteriores, todos los nodos antiguos se procesan y los nuevos nodos aún existen. En este momento, deben agregarse, y los nuevos nodos restantes se atraviesan e insertan delante del siguiente nodo.

De la misma manera, después de que se procesan los mismos nodos previos y posteriores, se procesan todos los nodos nuevos y los nodos antiguos todavía están allí. En este momento, debe desinstalar y atravesar los nodos antiguos para desinstalarlos.

Cuando todavía hay nodos sin procesar en los dos grupos, es necesario averiguar cuáles deben agregarse y cuáles deben desinstalarse. Como se mencionó en el capítulo sobre Algoritmo de diferencias simples, recorra el nodo anterior para encontrar el valor de índice máximo. Si la clave del nuevo nodo no aumenta, significa que es un nodo que debe moverse.

En este momento, puede crear un generador de matrices para almacenar la posición del nuevo nodo en el nodo antiguo. Si el valor es -1, significa que se trata de un nuevo nodo.

Luego, encuentre la subsecuencia creciente máxima de sourcer, y atraviese desde la cola.Cuando el valor del nodo de cola y la fuente no es igual, significa que debe moverse, y se llama al parche para que se mueva, de lo contrario, solo es necesario para apuntar a la siguiente posición hasta que se complete todo el recorrido.

Este es el proceso del algoritmo Fast Diff.

11.1 El mismo pre-elemento y post-elemento

Las posiciones de los conjuntos de nodos antiguo y nuevo siguen siendo las mismas en , y no es necesario moverlos. El ciclo while encuentra todos los mismos nodos previos y posteriores, y llama a la función patch para actualizar hasta que las posiciones del nodo tengan diferentes valores clave.

function patchKeyedChildren(newChildren, oldChildren, container) {
  // 相同的前置节点
  let j = 0
  let oldVNode = oldChildren[j]
  let newVNode = newChildren[j]
  while (oldVNode.key === newVNode.key) {
    patch(oldVNode, newVNode, container)
    j++
    oldVNode = oldChildren[j]
    newVNode = newChildren[j]
  }
  // 相同的后置节点
  let newEnd = newChildren.length - 1
  let oldEnd = oldChildren.length - 1
  let newEndVNode = newChildren[newEnd]
  let oldEndVNode = oldChildren[oldEnd]
  while (newEndVNode.key === oldEndVNode.key) {
    patch(oldEndVNode, newEndVNode, container)
    oldEnd--
    newEnd--
    oldEndVNode = oldChildren[oldEnd]
    newEndVNode = newChildren[newEnd]
  }
}
复制代码

Caso 1: después de que se procesan los mismos nodos previos y posteriores, todos los nodos antiguos se procesan y los nuevos nodos siguen siendo (nuevos)

condiciones cumplidas

  1. oldEnd < j contiene. El nodo antiguo se procesa
  2. endEnd >= j está establecido, el nuevo nodo aún no está procesado y debe agregarse

Al agregar, calcule el valor del índice de anclaje AnchorIndex = newEnd + 1. Si es menor que el número del nuevo conjunto de nodos, significa que el elemento de anclaje está en el nuevo nodo, y lo contrario corresponde a la cola del nodo. En este momento, no se requiere anclaje.

if (j > oldEnd && j <= newEnd) {
  const anchorIndex = newEnd + 1
  const anchor = anchorIndex < newChildren.length ? newChildren[anchorIndex].el : null
  while (j <= newEnd) {
    patch(null, newChildren[j++], container, anchor)
  }
}
复制代码

情况2:相同的前后置节点处理完之后之后,新的节点都完全处理了,旧的节点还有(删除)

满足的条件

  1. j ≤ oldEnd 成立。 旧节点走还有,需要删除
  2. j > newEnd 成立 新节点处理完毕
if (j > oldEnd && j <= newEnd) { // 新增
    const anchorIndex = newEnd + 1
    const anchor = anchorIndex < newChildren.length ? newChildren[anchorIndex].el : null
    while (j <= newEnd) {
      patch(null, newChildren[j++], container, anchor)
    }
  } else if (j > newEnd && j <= oldEnd) { // 删除
    while (j <= oldEnd) {
      unmount(oldChildren[j++])
    }
  }
复制代码

11.2、判断是否需要进行DOM移动操作

之前都是两组节点终会有一组节点全部被处理,这种情况只需要简单挂载、卸载节点即可。

情况3:在新旧节点种有新增和需要删除的情况

  1. 构造一个数组source,长度等于新节点预处理过之后的节点的数量,初始值都是 -1,用来存储新节点在旧节点中的位置索引
  2. 为新节点构建一张索引表keyIndex,存储节点的key和位置索引之间的映射
  3. 遍历旧的节点,旧节点的k去索引表中查找新节点的位置,存储在k中
  4. 如果有k说用该节点可以复用,调用patch更新,填充source数组,否则需要卸载
let newStart = j
let oldStart = j
const count = newEnd - j + 1
const source = new Array(count).fill(-1)
const keyIndex = {} // 索引表
for (let i = newStart; i <= newEnd; i++) {
  keyIndex[newChildren[i].key] = i
}
for (let i = oldStart; i <= oldEnd; i++) {
  oldVNode = oldChildren[i]
  const k = keyIndex[oldVNode.key]
  if (typeof k !== 'undefined') {
    let newVNode = newChildren[k]
    patch(oldVNode, newVNode, container)
    source[k - newStart] = i
  } else {
    unmount(oldVNode)
  }
}
复制代码
  1. 如何判断节点需要移动

遍历旧节点过程遇到的索引值呈现递增趋势,说明不需要移动节点,反之需要。定义一个pos,当k< pos 则需要移动,相反不需要移动,更新pos值

let moved = false
let pos = 0
for (let i = oldStart; i <= oldEnd; i++) {
  oldVNode = oldChildren[i]
  const k = keyIndex[oldVNode.key]
  if (typeof k !== 'undefined') {
    let newVNode = newChildren[k]
    patch(oldVNode, newVNode, container)
    source[k - newStart] = i
    if (k < pos) {  // 判断节点需要移动
      moved = true
    } else {
      pos = k
    }
  } else {
    unmount(oldVNode)
  }
}
复制代码
  1. 新增一个数量标识,代表已经更新过的节点数量,如果更新过的节点数量比新节点需要更新的数量多时,说明有多余节点需要删除

11.3、如何移动元素

寻找source最长递增子系列,找到不需要移动的节点。const seq = getSequence(sources)

seq 存储的是source的索引。

seq  source    KeyIndex     新节点     旧节点
                            
0      2          3:1         p-3       p-2
1 (s)  3          4:2         p-4       p-3
       1          2:3         p-2       p-4
      -1          7:4         p-7  (i)  p-6
                             
复制代码

seq表示在新节点中索引值不需要移动,3跟4节点不需要移动,只需要移动2和7

创建两个变量i和s,遍历新节点,如果seq[s] != i 则说明需要移动,否则只需要让s指向一下一个位置

如果source[i] === -1 直接新增,挂载在新节点下一个节点

const seq = getSequence(source)
let s = seq.length - 1
let i = count - 1
for (i; i >= 0; i--) {
  if (source[i] === -1) {
    const pos = i + newStart
    const newVNode = newChildren[pos]
    const nextPos = pos + 1
    const anchor = nextPos < newChildren.length ? newChildren[nextPos].el : null
    insert(null, newVNode, container, anchor)
  } else if (i !== seq[s]) {
    const pos = i + newStart
    const newVNode = newChildren[pos]
    const nextPos = pos + 1
    const anchor = nextPos < newChildren.length ? newChildren[nextPos].el : null
    patch(newVNode.el, container, anchor)
  } else {
    s--
  }
}
复制代码

Supongo que te gusta

Origin juejin.im/post/7080449431014735908
Recomendado
Clasificación