이 튜토리얼은 이 튜토리얼을 기반으로 하지만 JSX, TypeScript 및 구현하기 더 쉬운 접근 방식을 사용합니다. 내 GitHub 저장소에서 메모와 코드를 확인할 수 있습니다.
이제 반응성에 대해 이야기해 보겠습니다.
오래된 섬유를 저장하세요
새 광케이블과 비교할 수 있도록 기존 광케이블을 보관해야 합니다. 우리는 광섬유에 필드를 추가하여 이를 수행할 수 있습니다. 또한 나중에 유용하게 사용될 전용 필드가 필요합니다.
export interface Fiber { type: string props: VDomAttributes parent: Fiber | null child: Fiber | null sibling: Fiber | null dom: HTMLElement | Text | null alternate: Fiber | null committed: boolean }
여기서 커밋된 상태를 설정합니다.
function commit() { function commitChildren(fiber: Fiber | null) { if(!fiber) { return } if(fiber.dom && fiber.parent?.dom) { fiber.parent.dom.appendChild(fiber.dom) fiber.committed = true } if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) { let parent = fiber.parent // find the first parent that is not a fragment while(parent && isFragment(parent.vDom)) { // the root element is guaranteed to not be a fragment has has a non-fragment parent parent = parent.parent! } parent.dom?.appendChild(fiber.dom!) fiber.committed = true } commitChildren(fiber.child) commitChildren(fiber.sibling) } commitChildren(wip) wipParent?.appendChild(wip!.dom!) wip!.committed = true wip = null }
오래된 섬유나무도 살려야 합니다.
let oldFiber: Fiber | null = null function commit() { function commitChildren(fiber: Fiber | null) { if(!fiber) { return } if(fiber.dom && fiber.parent?.dom) { fiber.parent.dom.appendChild(fiber.dom) fiber.committed = true } commitChildren(fiber.child) commitChildren(fiber.sibling) } commitChildren(wip) wipParent?.appendChild(wip!.dom!) wip!.committed = true oldFiber = wip wip = null }
이제 반복 중에 기존 Fiber와 새 Fiber를 비교해야 합니다. 이것을 화해 과정이라고 합니다.
화해
오래된 광섬유와 새 광섬유를 비교해 볼 필요가 있습니다. 초기 작업에서는 먼저 오래된 Fiber를 투입합니다.
export function render(vDom: VDomNode, parent: HTMLElement) { wip = { parent: null, sibling: null, child: null, vDom: vDom, dom: null, committed: false, alternate: oldFiber, } wipParent = parent nextUnitOfWork = wip }
그런 다음 새로운 섬유의 생성을 새로운 기능으로 분리합니다.
function reconcile(fiber: Fiber, isFragment: boolean) { if (isElement(fiber.vDom)) { const elements = fiber.vDom.children ?? [] let index = 0 let prevSibling = null while (index <p>단, 기존 광케이블을 새 광케이블에 장착해야 합니다.<br> </p> <pre class="brush:php;toolbar:false">function reconcile(fiber: Fiber, isFragment: boolean) { if (isElement(fiber.vDom)) { const elements = fiber.vDom.children ?? [] let index = 0 let prevSibling = null let currentOldFiber = fiber.alternate?.child ?? null while (index <p>이제 기존 Fiber를 새 Fiber에 장착했습니다. 하지만 재렌더링을 트리거할 항목이 없습니다. 지금은 버튼을 추가하여 수동으로 트리거합니다. 아직 상태가 없기 때문에 vDOM을 변경하기 위해 props를 사용합니다.<br> </p> <pre class="brush:php;toolbar:false">import { render } from "./runtime"; import { createElement, fragment, VDomAttributes, VDomNode } from "./v-dom"; type FuncComponent = (props: VDomAttributes, children: VDomNode[]) => JSX.Element const App: FuncComponent = (props: VDomAttributes, __: VDomNode[]) => { return <div> <h1 id="H">H1</h1> <h2 id="props-example-toString">{props["example"]?.toString()}</h2> { props["show"] ? <p>show</p> : > } <h1 id="H">H1</h1> > </div> } const app = document.getElementById('app') const renderButton = document.createElement('button') renderButton.textContent = 'Render' let cnt = 0 renderButton.addEventListener('click', () => { const vDom: VDomNode = App({ "example": (new Date()).toString(), "show": cnt % 2 === 0 }, []) as unknown as VDomNode cnt++ render(vDom, app!) }) document.body.appendChild(renderButton)
이제 renderButton을 클릭하면 렌더링된 결과가 한 번 반복됩니다. 현재의 모든 로직은 단순히 렌더링된 vDOM을 문서에 넣는 것뿐입니다.
Commit 기능에 console.log를 추가하면 Alternative Fiber가 출력되는 것을 볼 수 있습니다.
이제 기존 Fiber와 새 Fiber를 처리하는 방법을 정의하고 정보를 기반으로 DOM을 변경해야 합니다. 규칙은 다음과 같습니다.
새로운 섬유 각각에 대해
- 오래된 Fiber가 있는 경우 기존 Fiber의 내용을 새 Fiber와 비교하고, 서로 다른 경우 이전 DOM 노드를 새 DOM 노드로 교체하거나 이전 DOM 노드를 새로운 DOM 노드. 두 개의 vDOM이 동일하다는 것은 해당 태그와 모든 속성이 동일하다는 것을 의미합니다. 그들의 아이들은 다를 수 있습니다.
- 오래된 Fiber가 없으면 새 DOM 노드를 생성하여 상위 노드에 추가합니다.
- 새 파이버의 경우 하위 또는 형제 자매가 없지만 이전 파이버에는 하위 또는 형제 자매가 있는 경우 기존 하위 또는 형제 자매를 재귀적으로 제거합니다.
좀 헷갈리시나요? 음, 코드를 보여드리겠습니다. 먼저 이전 DOM 생성을 삭제합니다. 그런 다음 위의 규칙을 적용하세요.
첫 번째 규칙, 오래된 섬유가 있으면 오래된 섬유와 새로운 섬유의 함량을 비교합니다. 서로 다른 경우 이전 DOM 노드를 새 DOM 노드로 바꾸거나 이전 DOM 노드를 새 DOM 노드에 복사합니다.
export function vDOMEquals(a: VDomNode, b: VDomNode): boolean { if (isString(a) && isString(b)) { return a === b } else if (isElement(a) && isElement(b)) { let ret = a.tag === b.tag && a.key === b.key if (!ret) return false if (a.props && b.props) { const aProps = a.props const bProps = b.props const aKeys = Object.keys(aProps) const bKeys = Object.keys(bProps) if (aKeys.length !== bKeys.length) return false for (let i = 0; i <p>그런 다음 작은 리팩토링을 했습니다.<br> </p><pre class="brush:php;toolbar:false">export interface Fiber { type: string props: VDomAttributes parent: Fiber | null child: Fiber | null sibling: Fiber | null dom: HTMLElement | Text | null alternate: Fiber | null committed: boolean }
이제 커밋에 있어서 기존 Fiber와 새 Fiber를 비교할 수 있는 추가 대안 필드가 생겼습니다.
원래 커밋 기능입니다
function commit() { function commitChildren(fiber: Fiber | null) { if(!fiber) { return } if(fiber.dom && fiber.parent?.dom) { fiber.parent.dom.appendChild(fiber.dom) fiber.committed = true } if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) { let parent = fiber.parent // find the first parent that is not a fragment while(parent && isFragment(parent.vDom)) { // the root element is guaranteed to not be a fragment has has a non-fragment parent parent = parent.parent! } parent.dom?.appendChild(fiber.dom!) fiber.committed = true } commitChildren(fiber.child) commitChildren(fiber.sibling) } commitChildren(wip) wipParent?.appendChild(wip!.dom!) wip!.committed = true wip = null }
이름을 조금 바꾸겠습니다. 예전 이름이 틀렸을 뿐이에요(죄송합니다).
let oldFiber: Fiber | null = null function commit() { function commitChildren(fiber: Fiber | null) { if(!fiber) { return } if(fiber.dom && fiber.parent?.dom) { fiber.parent.dom.appendChild(fiber.dom) fiber.committed = true } commitChildren(fiber.child) commitChildren(fiber.sibling) } commitChildren(wip) wipParent?.appendChild(wip!.dom!) wip!.committed = true oldFiber = wip wip = null }
추가, 복사 및 교체
그럼 우리는 어떻게 해야 할까요? 기존 로직은 추가만 하므로 이를 추출합니다.
export function render(vDom: VDomNode, parent: HTMLElement) { wip = { parent: null, sibling: null, child: null, vDom: vDom, dom: null, committed: false, alternate: oldFiber, } wipParent = parent nextUnitOfWork = wip }
더 많은 유연성을 제공하려면 커밋 단계까지 DOM 구성을 연기해야 합니다.
function reconcile(fiber: Fiber, isFragment: boolean) { if (isElement(fiber.vDom)) { const elements = fiber.vDom.children ?? [] let index = 0 let prevSibling = null while (index <p>첫 번째와 두 번째 규칙에 따라 다음 코드로 리팩토링합니다.<br> </p> <pre class="brush:php;toolbar:false">function reconcile(fiber: Fiber, isFragment: boolean) { if (isElement(fiber.vDom)) { const elements = fiber.vDom.children ?? [] let index = 0 let prevSibling = null let currentOldFiber = fiber.alternate?.child ?? null while (index <p>자바스크립트에서는 모든 값이 참조라는 점을 항상 명심하세요. fibre.dom = fibre.alternate.dom이면 fibre.dom과 fibre.alternate.dom은 동일한 개체를 가리킵니다. Fiber.dom을 변경하면 Fiber.alternate.dom도 변경되고 그 반대도 마찬가지입니다. 그렇기 때문에 교체할 때 단순히 Fiber.alternate.dom?.replaceWith(섬유.dom)를 사용했습니다. 그러면 이전 DOM이 새 DOM으로 대체됩니다. 이전 상위 항목을 복사하면 DOM에 대해 Fiber.alternate.dom이 있지만 DOM도 교체됩니다.</p> <p>단, 아직 삭제 처리는 하지 않았습니다.</p> <h3> 일부 사고 </h3> <p>알겠습니다. 이전 코드에는 좀 더 복잡한 jsx를 작성하면서 발견한 몇 가지 버그가 포함되어 있으므로 삭제를 구현하기 전에 수정해 보겠습니다.</p> <p>이전에 버그가 있었습니다. 목록을 props에 전달할 수 없습니다. 이번 기회에 수정해 보겠습니다.<br> </p> <pre class="brush:php;toolbar:false">import { render } from "./runtime"; import { createElement, fragment, VDomAttributes, VDomNode } from "./v-dom"; type FuncComponent = (props: VDomAttributes, children: VDomNode[]) => JSX.Element const App: FuncComponent = (props: VDomAttributes, __: VDomNode[]) => { return <div> <h1 id="H">H1</h1> <h2 id="props-example-toString">{props["example"]?.toString()}</h2> { props["show"] ? <p>show</p> : > } <h1 id="H">H1</h1> > </div> } const app = document.getElementById('app') const renderButton = document.createElement('button') renderButton.textContent = 'Render' let cnt = 0 renderButton.addEventListener('click', () => { const vDom: VDomNode = App({ "example": (new Date()).toString(), "show": cnt % 2 === 0 }, []) as unknown as VDomNode cnt++ render(vDom, app!) }) document.body.appendChild(renderButton)
그럼 유형을 수정하세요. 오류가 하나뿐이니까 직접 해 보세요.
그러나 다음과 같은 코드가 있다면
export function vDOMEquals(a: VDomNode, b: VDomNode): boolean { if (isString(a) && isString(b)) { return a === b } else if (isElement(a) && isElement(b)) { let ret = a.tag === b.tag && a.key === b.key if (!ret) return false if (a.props && b.props) { const aProps = a.props const bProps = b.props const aKeys = Object.keys(aProps) const bKeys = Object.keys(bProps) if (aKeys.length !== bKeys.length) return false for (let i = 0; i <p>우리 일이 또 망가졌어요... </p> <p>그렇습니다. 위의 경우 하위 항목이 중첩 배열이 될 수 있기 때문에 이들을 평면화해야 합니다.</p> <p>하지만 그것만으로는 충분하지 않습니다. 으, 우리 createDom은 정수가 아닌 문자열이나 요소만 인식하므로 숫자를 문자열로 묶어야 합니다.<br> </p> <pre class="brush:php;toolbar:false">function buildDom(fiber: Fiber, fiberIsFragment: boolean) { if(fiber.dom) return if(fiberIsFragment) return fiber.dom = createDom(fiber.vDom) } function performUnitOfWork(nextUnitOfWork: Fiber | null): Fiber | null { if(!nextUnitOfWork) { return null } const fiber = nextUnitOfWork const fiberIsFragment = isFragment(fiber.vDom) reconcile(fiber) buildDom(fiber, fiberIsFragment); if (fiber.child) { return fiber.child } let nextFiber: Fiber | null = fiber while (nextFiber) { if (nextFiber.sibling) { return nextFiber.sibling } nextFiber = nextFiber.parent } return null }
좋아, 이제 일이 잘 풀리네요.
렌더링 버튼을 누르면 목록이 업데이트되지만 이전 요소는 여전히 남아 있습니다. 이전 요소를 삭제해야 합니다.
제거하다
여기서 규칙을 다시 설명합니다. 새 파이버에 대해 하위 또는 형제 자매가 없지만 기존 파이버에 하위 또는 형제 자매가 있는 경우 기존 하위 또는 형제 자매를 재귀적으로 제거합니다.
function commit() { function commitChildren(fiber: Fiber | null) { if(!fiber) { return } if(fiber.dom && fiber.parent?.dom) { fiber.parent?.dom?.appendChild(fiber.dom) fiber.committed = true } if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) { let parent = fiber.parent // find the first parent that is not a fragment while(parent && isFragment(parent.vDom)) { // the root element is guaranteed to not be a fragment has has a non-fragment parent parent = parent.parent! } parent.dom?.appendChild(fiber.dom!) fiber.committed = true } commitChildren(fiber.child) commitChildren(fiber.sibling) } commitChildren(wip) wipParent?.appendChild(wip!.dom!) wip!.committed = true oldFiber = wip wip = null }
재귀 제거를 수행하지 않으면 삭제가 필요한 항목이 여러 개 있을 때 일부 오래된 요소가 매달려 있게 됩니다.
로 변경할 수 있습니다.
function commit() { function commitToParent(fiber: Fiber | null) { if(!fiber) { return } if(fiber.dom && fiber.parent?.dom) { fiber.parent?.dom?.appendChild(fiber.dom) fiber.committed = true } if(fiber.dom && fiber.parent && isFragment(fiber.parent.vDom) && !fiber.committed) { let parent = fiber.parent // find the first parent that is not a fragment while(parent && isFragment(parent.vDom)) { // the root element is guaranteed to not be a fragment has has a non-fragment parent parent = parent.parent! } parent.dom?.appendChild(fiber.dom!) fiber.committed = true } commitToParent(fiber.child) commitToParent(fiber.sibling) } commitToParent(wip) wipParent?.appendChild(wip!.dom!) wip!.committed = true oldFiber = wip wip = null }
참고용.
요약
이것은 어려운 장이지만 솔직히 말해서 꽤 전통적인 코딩입니다. 그러나 지금까지 React가 아래에서 위로 어떻게 작동하는지 이해했습니다.
사실 이제 모든 것이 이미 작동할 수 있습니다. 소품을 변경할 때마다 수동으로 다시 렌더링을 실행할 수 있습니다. 하지만 이렇게 답답한 수작업은 우리가 원하는 것이 아닙니다. 우리는 반응이 자동이기를 원합니다. 그래서 다음 장에서 Hook에 대해 이야기해보겠습니다.
위 내용은 작은 React Chpdating vDOM 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

실제 세계에서 JavaScript의 응용 프로그램에는 서버 측 프로그래밍, 모바일 애플리케이션 개발 및 사물 인터넷 제어가 포함됩니다. 1. 서버 측 프로그래밍은 Node.js를 통해 실현되며 동시 요청 처리에 적합합니다. 2. 모바일 애플리케이션 개발은 재교육을 통해 수행되며 크로스 플랫폼 배포를 지원합니다. 3. Johnny-Five 라이브러리를 통한 IoT 장치 제어에 사용되며 하드웨어 상호 작용에 적합합니다.

일상적인 기술 도구를 사용하여 기능적 다중 테넌트 SaaS 응용 프로그램 (Edtech 앱)을 구축했으며 동일한 작업을 수행 할 수 있습니다. 먼저, 다중 테넌트 SaaS 응용 프로그램은 무엇입니까? 멀티 테넌트 SAAS 응용 프로그램은 노래에서 여러 고객에게 서비스를 제공 할 수 있습니다.

이 기사에서는 Contrim에 의해 확보 된 백엔드와의 프론트 엔드 통합을 보여 주며 Next.js를 사용하여 기능적인 Edtech SaaS 응용 프로그램을 구축합니다. Frontend는 UI 가시성을 제어하기 위해 사용자 권한을 가져오고 API가 역할 기반을 준수하도록합니다.

JavaScript는 현대 웹 개발의 핵심 언어이며 다양성과 유연성에 널리 사용됩니다. 1) 프론트 엔드 개발 : DOM 운영 및 최신 프레임 워크 (예 : React, Vue.js, Angular)를 통해 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축합니다. 2) 서버 측 개발 : Node.js는 비 차단 I/O 모델을 사용하여 높은 동시성 및 실시간 응용 프로그램을 처리합니다. 3) 모바일 및 데스크탑 애플리케이션 개발 : 크로스 플랫폼 개발은 개발 효율을 향상시키기 위해 반응 및 전자를 통해 실현됩니다.

JavaScript의 최신 트렌드에는 Typescript의 Rise, 현대 프레임 워크 및 라이브러리의 인기 및 WebAssembly의 적용이 포함됩니다. 향후 전망은보다 강력한 유형 시스템, 서버 측 JavaScript 개발, 인공 지능 및 기계 학습의 확장, IoT 및 Edge 컴퓨팅의 잠재력을 포함합니다.

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

Python은 데이터 과학 및 기계 학습에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 간결한 구문 및 풍부한 라이브러리 생태계로 유명하며 데이터 분석 및 웹 개발에 적합합니다. 2. JavaScript는 프론트 엔드 개발의 핵심입니다. Node.js는 서버 측 프로그래밍을 지원하며 풀 스택 개발에 적합합니다.

JavaScript는 이미 최신 브라우저에 내장되어 있기 때문에 설치가 필요하지 않습니다. 시작하려면 텍스트 편집기와 브라우저 만 있으면됩니다. 1) 브라우저 환경에서 태그를 통해 HTML 파일을 포함하여 실행하십시오. 2) Node.js 환경에서 Node.js를 다운로드하고 설치 한 후 명령 줄을 통해 JavaScript 파일을 실행하십시오.


핫 AI 도구

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

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

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

Clothoff.io
AI 옷 제거제

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

인기 기사

뜨거운 도구

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

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

Dreamweaver Mac版
시각적 웹 개발 도구

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전
