diff 알고리즘 활용 기술 요약
이번에는 diff 알고리즘 사용법에 대해 정리해보겠습니다. diff 알고리즘 사용 시 주의사항은 무엇인가요? 실제 사례를 살펴보겠습니다.
Virtual dom
diff 알고리즘은 먼저 diff의 객체가 가상 dom이라는 개념을 명확히 해야 하며 실제 dom을 업데이트하는 것은 diff 알고리즘의 결과입니다
Vnode 기본 클래스
constructor ( 。。。 ) { this.tag = tag this.data = data this.children = children this.text = text this.elm = elm this.ns = undefined this.context = context this.fnContext = undefined this.fnOptions = undefined this.fnScopeId = undefined this.key = data && data.key this.componentOptions = componentOptions this.componentInstance = undefined this.parent = undefined this.raw = false this.isStatic = false this.isRootInsert = true this.isComment = false this.isCloned = false this.isOnce = false this.asyncFactory = asyncFactory this.asyncMeta = undefined this.isAsyncPlaceholder = false }
이 부분은 코드는 주로 업데이트를 위한 것입니다. diff 알고리즘에서 특정 diff 속성의 의미를 알아두면 좋습니다. 물론 vnode 인스턴스를 더 잘 이해할 수도 있습니다.
전체 프로세스핵심 기능은 패치 기능입니다.
- isUndef 판단(정의되지 않았거나 null인지 여부)
- // 빈 마운트(구성 요소로 가능), 새 루트 요소 생성createElm(vnode, 삽입된VnodeQueue) 여기에서 노드 생성이 하나씩 삽입되지 않음을 확인할 수 있습니다. 하나이지만 통합 일괄 처리를 위해 큐에 넣습니다
- 핵심 함수 sameVnode
function sameVnode (a, b) { return ( a.key === b.key && ( ( a.tag === b.tag && a.isComment === b.isComment && isDef(a.data) === isDef(b.data) && sameInputType(a, b) ) || ( isTrue(a.isAsyncPlaceholder) && a.asyncFactory === b.asyncFactory && isUndef(b.asyncFactory.error) ) ) ) }
export interface VNodeData { key?: string | number; slot?: string; scopedSlots?: { [key: string]: ScopedSlot }; ref?: string; tag?: string; staticClass?: string; class?: any; staticStyle?: { [key: string]: any }; style?: object[] | object; props?: { [key: string]: any }; attrs?: { [key: string]: any }; domProps?: { [key: string]: any }; hook?: { [key: string]: Function }; on?: { [key: string]: Function | Function[] }; nativeOn?: { [key: string]: Function | Function[] }; transition?: object; show?: boolean; inlineTemplate?: { render: Function; staticRenderFns: Function[]; }; directives?: VNodeDirective[]; keepAlive?: boolean; }
// destroy old node if (isDef(parentElm)) { removeVnodes(parentElm, [oldVnode], 0, 0) } else if (isDef(oldVnode.tag)) { invokeDestroyHook(oldVnode) }
createComponent(자식이 있는지 확인한 후 재귀적으로 호출)createCommentcreateTextNode생성 후 삽입 기능을 사용하세요그 후에는 다음을 수행해야 합니다. hydrate 함수를 사용하여 가상 DOM과 실제 DOM을 매핑합니다.
function insert (parent, elm, ref) { if (isDef(parent)) { if (isDef(ref)) { if (ref.parentNode === parent) { nodeOps.insertBefore(parent, elm, ref) } } else { nodeOps.appendChild(parent, elm) } } }
core function
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) { if (oldVnode === vnode) { return } const elm = vnode.elm = oldVnode.elm if (isTrue(oldVnode.isAsyncPlaceholder)) { if (isDef(vnode.asyncFactory.resolved)) { hydrate(oldVnode.elm, vnode, insertedVnodeQueue) } else { vnode.isAsyncPlaceholder = true } return } if (isTrue(vnode.isStatic) && isTrue(oldVnode.isStatic) && vnode.key === oldVnode.key && (isTrue(vnode.isCloned) || isTrue(vnode.isOnce)) ) { vnode.componentInstance = oldVnode.componentInstance return } let i const data = vnode.data if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) { i(oldVnode, vnode) } const oldCh = oldVnode.children const ch = vnode.children if (isDef(data) && isPatchable(vnode)) { for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode) if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode) } if (isUndef(vnode.text)) { if (isDef(oldCh) && isDef(ch)) { if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) } else if (isDef(ch)) { if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '') addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue) } else if (isDef(oldCh)) { removeVnodes(elm, oldCh, 0, oldCh.length - 1) } else if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, '') } } else if (oldVnode.text !== vnode.text) { nodeOps.setTextContent(elm, vnode.text) } if (isDef(data)) { if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode) } }
const el = vnode.el = oldVnode.el 이는 vnode.el이 현재 실제 DOM을 참조하도록 하는 매우 중요한 단계입니다. el이 수정되면 vnode.el이 동기적으로 변경됩니다.
- 두 참조가 일치하는지 비교하세요
- asyncFactory가 그 이후에 무엇을 하는지 모르기 때문에 이해하기 어렵습니다.
- 정적 노드가 동일하면 키를 비교합니다. 다시 렌더링하지 말고, componentInstance를 직접 복사하세요. (여기서 명령이 적용되면)
- vnode가 텍스트 노드이거나
annotation 노드이지만 vnode.text != oldVnode.text인 경우 업데이트만 하면 됩니다. vnode.elm의 텍스트 콘텐츠
- children's Compare
- oldVnode에만 하위 노드가 있는 경우 해당 노드를 삭제하세요.
- vnode에만 하위 노드가 있는 경우 해당 하위 노드를 만듭니다. 여기서 oldVnode인 경우 텍스트 노드인 경우 vnode.elm을 입력합니다. 텍스트가 빈 문자열로 설정된 경우( ) updateChildren은 나중에 업데이트됩니다. 이는 나중에 자세히 설명됩니다.
- oldVnode와 vnode 모두 하위 노드가 없지만 oldVnode가 있습니다. 텍스트 노드 또는 주석 노드인 경우 vnode.elm을 추가하십시오. 텍스트는 빈 문자열로 설정됩니다
이 부분의 초점은 여전히 전체 알고리즘에 있습니다
처음 4개 포인터, oldStart, oldEnd , newStart, newEnd, 두 개의 어레이, oldVnode, Vnode.
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx = 0 let newStartIdx = 0 let oldEndIdx = oldCh.length - 1 let oldStartVnode = oldCh[0] let oldEndVnode = oldCh[oldEndIdx] let newEndIdx = newCh.length - 1 let newStartVnode = newCh[0] let newEndVnode = newCh[newEndIdx] let oldKeyToIdx, idxInOld, vnodeToMove, refElm while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (isUndef(oldStartVnode)) { oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left } else if (isUndef(oldEndVnode)) { oldEndVnode = oldCh[--oldEndIdx] } else if (sameVnode(oldStartVnode, newStartVnode)) { patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] } else if (sameVnode(oldEndVnode, newEndVnode)) { patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue) oldEndVnode = oldCh[--oldEndIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue) canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue) canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] } else { if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx) if (isUndef(idxInOld)) { // New element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } else { vnodeToMove = oldCh[idxInOld] if (sameVnode(vnodeToMove, newStartVnode)) { patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue) oldCh[idxInOld] = undefined canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm) } else { // same key but different element. treat as new element createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx) } } newStartVnode = newCh[++newStartIdx] } } if (oldStartIdx > oldEndIdx) { refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else if (newStartIdx > newEndIdx) { removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) } }
루프 비교의 여러 상황 및 처리(다음 ++ - 모두 인덱스의 ++를 참조함 -) 비교는 비교되는 노드 노드입니다. 비교는 엄격하지 않습니다. 이는 사실이 아닙니다. 전체 루프가 끝나지 않는 조건
oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx
- oldStart === newStart, oldStart++ newStart++
- oldEnd === newEnd, oldEnd-- newEnd--
- oldStart === newEnd, oldStart는 대기열의 끝에 삽입됩니다. oldStart++ newEnd--
- oldEnd === newStart, oldEnd는 대기열의 시작 부분에 삽입됩니다. oldEnd- - newStart++
- 남은 상황은 모두 이렇게 처리됩니다. 간단히 말하면 처리 후 newStart++
- 두 가지 프로세스가 있습니다.
newStart在old中发现一样的那么将这个移动到oldStart前
没有发现一样的那么创建一个放到oldStart之前
循环结束后并没有完成
还有一段判断才算完
if (oldStartIdx > oldEndIdx) { refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue) } else if (newStartIdx > newEndIdx) { removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) }相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
위 내용은 diff 알고리즘 활용 기술 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제









Linux에서의 system() 함수 요약 Linux 시스템에서 system() 함수는 명령줄 명령을 실행하는 데 사용할 수 있는 매우 일반적으로 사용되는 함수입니다. 이 기사에서는 system() 함수를 자세히 소개하고 몇 가지 구체적인 코드 예제를 제공합니다. 1. system() 함수의 기본 사용법은 다음과 같습니다. intsystem(constchar*command) 여기서 명령 매개변수는 문자입니다.

현대 금융 분야에서는 데이터과학과 인공지능 기술의 발달로 양적 금융이 점차 중요한 방향으로 자리잡고 있습니다. Go 언어는 효율적으로 데이터를 처리하고 분산 시스템을 배포할 수 있는 정적인 유형의 프로그래밍 언어로 양적 금융 분야에서 점차 주목을 받고 있습니다. 이 글에서는 Go 언어를 사용하여 정량적 재무 분석을 수행하는 방법을 소개합니다. 구체적인 내용은 다음과 같습니다. 재무 데이터 얻기 먼저 재무 데이터를 얻어야 합니다. Go 언어의 네트워크 프로그래밍 기능은 매우 강력하며 다양한 금융 데이터를 얻는 데 사용할 수 있습니다. 비교하다

빅데이터와 데이터 마이닝의 등장으로 데이터 마이닝 기능을 지원하는 프로그래밍 언어가 점점 더 많아지기 시작했습니다. 빠르고 안전하며 효율적인 프로그래밍 언어인 Go 언어는 데이터 마이닝에도 사용할 수 있습니다. 그렇다면 데이터 마이닝에 Go 언어를 사용하는 방법은 무엇입니까? 다음은 몇 가지 중요한 단계와 기술입니다. 데이터 획득 먼저 데이터를 획득해야 합니다. 이는 웹페이지 정보 크롤링, API를 사용하여 데이터 가져오기, 데이터베이스에서 데이터 읽기 등 다양한 수단을 통해 달성할 수 있습니다. Go 언어에는 풍부한 HTTP가 제공됩니다.

PHP를 사용하여 간단한 SEO 최적화 기능을 개발하는 방법 SEO(SearchEngineOptimization) 또는 검색 엔진 최적화는 웹 사이트의 구조와 콘텐츠를 개선하여 더 많은 유기적인 트래픽을 확보함으로써 검색 엔진에서 웹 사이트의 순위를 높이는 것을 의미합니다. 웹사이트 개발에서 PHP를 사용하여 간단한 SEO 최적화 기능을 구현하는 방법은 무엇입니까? 이 기사에서는 개발자가 PHP 프로젝트에서 SEO 최적화를 구현하는 데 도움이 되는 몇 가지 일반적으로 사용되는 SEO 최적화 기술과 특정 코드 예제를 소개합니다. 1. 사용하기 편리하다

C#을 사용하여 최소 스패닝 트리 알고리즘을 작성하는 방법 최소 스패닝 트리 알고리즘은 그래프의 연결 문제를 해결하는 데 사용되는 중요한 그래프 이론 알고리즘입니다. 컴퓨터 과학에서 최소 스패닝 트리(Minimum Spanning Tree)는 스패닝 트리의 모든 간선의 가중치의 합이 가장 작은 연결된 그래프의 스패닝 트리를 의미합니다. 이 문서에서는 C#을 사용하여 최소 스패닝 트리 알고리즘을 작성하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 먼저, 문제를 표현하기 위해 그래프 데이터 구조를 정의해야 합니다. C#에서는 인접 행렬을 사용하여 그래프를 나타낼 수 있습니다. 인접 행렬은 각 요소가 나타내는 2차원 배열입니다.

인터넷의 대중화로 인해 사진, 비디오 및 기타 리소스에 대한 외부 링크 기능을 제공하는 웹사이트가 점점 더 많아지고 있습니다. 그러나 이 외부 링크 기능은 도난당하기 쉽습니다. 핫링크는 다른 웹사이트가 귀하의 웹사이트에 있는 사진, 비디오 및 기타 리소스를 사용하여 이러한 리소스를 자체 서버에 다운로드하는 대신 참조 주소를 통해 자체 웹사이트에 직접 표시하는 것을 의미합니다. 이러한 방식으로 핫링크 웹사이트는 웹사이트의 트래픽과 대역폭 리소스를 무료로 사용할 수 있으며, 이는 리소스를 낭비하고 웹사이트 속도에 영향을 미칩니다. 이 문제를 해결하기 위해 Nginx를 사용하여 핫링크를 방지할 수 있습니다. 엔진엑스는

Linux에서는 svndiff 명령을 직접 사용하여 코드 수정 사항을 보는 것이 매우 어려우므로 인터넷에서 더 나은 솔루션을 검색했습니다. 특히 vimdiff 사용에 익숙한 사람들을 위해 svndiff의 코드 보기 도구로 vimdiff를 사용하는 것입니다. vim. 매우 편리합니다. svndiff 명령을 사용하여 특정 파일의 수정 사항을 비교할 때, 예를 들어 $svndiff-r4420ngx_http_limit_req_module.c 명령을 실행하면 다음 명령이 실제로 기본 diff 프로그램으로 전송됩니다. -u-Lngx_http_limit_req_module.c (개정4420)-Lngx_

원클릭 솔루션: pip 미러 소스의 사용 기술을 빠르게 익히십시오. 소개: pip는 Python 패키지를 쉽게 설치, 업그레이드 및 관리할 수 있는 가장 일반적으로 사용되는 Python용 패키지 관리 도구입니다. 그러나 잘 알려진 이유로 인해 기본 미러 소스를 사용하여 설치 패키지를 다운로드하는 것이 더 느립니다. 이 문제를 해결하려면 국내 미러 소스를 사용해야 합니다. 이 기사에서는 pip 미러 소스의 사용 기술을 빠르게 익히는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 시작하기 전에 pip 미러 소스의 개념을 이해하세요.
