자체 Vue를 생성합니다.js 처음부터 - 섹션 3(VDOM 구축)

21925 단어 vuejavascript
자체 Vue를 생성합니다.js 처음부터 - 섹션 3(VDOM 구축)
만약 네가 이 글을 좋아한다면, 아마도 너도 내가 트위터에 올린 내용을 좋아할 것이다.궁금하면 내 거 봐.🚀
이것은 자체 Vue 시리즈의 세 번째 섹션입니다.js는 처음부터 반응식 프레임워크(예를 들어 Vue)를 만드는 기초 지식을 가르쳐 드리겠습니다.js.이 박문을 읽으려면 이 시리즈의 firstsecond 부분을 읽으시기 바랍니다.
이 글은 처음에는 길었을 수도 있지만, 그렇게 전문적으로 보이지 않았을 수도 있다.그것은 코드의 모든 절차를 묘사하는데, 이것이 바로 왜 코드가 상당히 복잡해 보이는지이다.하지만 저를 용서해 주십시오. 이 모든 것은 결국 완벽한 의미가 있을 것입니다.😊

노선도🚘

  • Introduction
  • Virtual DOM basics
  • 가상 DOM 및 렌더링 구현(본문)


  • 가상 DOM 구축


    뼈대


    second part of this series에서 우리는 가상 DOM의 작동 원리에 대한 기초 지식을 배웠다.VDOM 뼈대를 마지막 점에서 복사합니다.우리는 이 코드를 사용하여 추적을 진행한다.VDOM 엔진의 최종 버전도 찾을 수 있습니다.나는 또 하나 this gist 를 만들었는데, 너는 거기에서 그것을 놀 수 있다.

    코드 펜 가상 노드 생성


    따라서 가상 노드를 만들려면 태그, 속성, 하위 노드가 필요합니다.우리의 함수는 다음과 같이 보인다.
    function h(tag, props, children){ ... }
    
    (Vue에서 가상 노드를 만드는 데 사용되는 함수 이름은 h 이므로 여기서 이렇게 부릅니다.
    이 함수에서는 다음 구조의 JavaScript 객체가 필요합니다.
    {
        tag: 'div',
        props: {
            class: 'container'
        },
        children: ...
    }
    
    이를 위해서는 태그, 속성 및 하위 노드 매개변수를 객체에 포장하고 반환해야 합니다.
    function h(tag, props, children) {
        return {
            tag,
            props,
            children,
        }
    }
    
    가상 노드의 생성이 완료되었습니다.

    DOM에 가상 노드 마운트


    가상 노드를 DOM에 마운트하는 것은 주어진 용기에 추가한다는 뜻입니다.이 노드는 원시 용기 (예: #app -div) 나 다른 가상 노드 (예: <span> 에 설치할 수 있습니다.
    이것은 모든 노드의 하위 노드를 훑어보고 해당하는 용기에 불러와야 하기 때문에 귀속 함수가 될 것이다.
    우리의 <div> 함수는 다음과 같다.
    function mount(vnode, container) { ... }
    

    1) DOM 요소를 생성해야 합니다.


    const el = (vnode.el = document.createElement(vnode.tag))
    

    2) 등록 정보(mount)를 DOM 요소의 등록 정보로 설정해야 합니다.


    우리는 다음과 같이 교체를 통해 이 점을 실현한다.
    for (const key in vnode.props) {
        el.setAttribute(key, vnode.props[key])
    }
    

    3) 우리는 요소에 하위 요소를 설치해야 한다


    기억해라, 두 가지 유형의 아이가 있다.
  • 단순 텍스트
  • 가상 노드 어레이
  • 우리는 두 가지 측면을 처리한다.
    // Children is a string/text
    if (typeof vnode.children === 'string') {
        el.textContent = vnode.children
    }
    
    // Chilren are virtual nodes
    else {
        vnode.children.forEach(child => {
            mount(child, el) // Recursively mount the children
        })
    }
    
    코드의 두 번째 부분에서 보듯이 이 하위 레벨들은 같은 props 함수로 설치됩니다.텍스트 노드만 남을 때까지 반복적으로 계속됩니다.그리고 점차 멈추다.

    이 마운트 함수의 마지막 부분에서 생성된 DOM 요소를 해당 컨테이너에 추가해야 합니다.


    container.appendChild(el)
    

    DOM에서 가상 노드 제거

    mount 함수에서 실제 DOM의 상위 노드에서 지정된 가상 노드를 제거합니다.이 함수는 가상 노드만 매개 변수로 합니다.
    function unmount(vnode) {
        vnode.el.parentNode.removeChild(vnode.el)
    }
    

    가상 노드 패치


    이것은 두 개의 가상 노드를 취하여 그것들을 비교하고 그것들 사이의 차이를 찾아내는 것을 의미한다.
    지금까지 우리가 가상 DOM을 위해 작성할 가장 광범위한 함수였지만 기다려 주십시오.

    1) 우리가 사용할 DOM 요소 할당


    const el = (n2.el = n1.el)
    

    2) 노드에 다른 태그가 있는지 확인


    만약 노드가 서로 다른 표기를 가지고 있다면, 우리는 내용이 완전히 다르다고 가정할 수 있으며, 우리는 노드를 완전히 교체하기만 하면 된다.우리는 새 노드를 설치하고 낡은 노드를 마운트 해제함으로써 이 점을 실현한다.
    if (n1.tag !== n2.tag) {
        // Replace node
        mount(n2, el.parentNode)
        unmount(n1)
    } else {
        // Nodes have different tags
    }
    
    만약 노드에 같은 라벨이 있다면;그러나 그것은 두 가지 다른 일을 의미할 수 있다.
  • 문자열 서브노드가 있는 새 노드
  • 새 노드에 하위 그룹이 있음
  • 3) 노드에 문자열 하위 레벨이 있는 경우


    이 예에서, 우리는 원소 unmount 를 계속해서'children'으로 바꾸기만 하면 된다.
    ...
        // Nodes have different tags
        if (typeof n2.children === 'string') {
            el.textContent = n2.children
        }
    ...
    

    4) 노드에 하위 그룹이 있는 경우


    이런 상황에서 우리는 반드시 아이 사이의 차이를 검사해야 한다.세 가지 경우가 있습니다.
  • 아이의 길이가 같다
  • 이전 노드의 하위 노드가 새 노드보다 많다.이 경우 DOM
  • 에서 EXCEND 하위 레벨을 제거해야 합니다.
  • 새 노드는 이전 노드보다 더 많은 하위 노드를 가지고 있다.이 경우 DOM에 추가 하위 객체를 추가해야 합니다.
  • 따라서 우선, 우리는 하위 노드의 공공 길이를 확정하거나 다시 말하면 각 노드의 최소자 노드 수:
    const c1 = n1.children
    const c2 = n2.children
    const commonLength = Math.min(c1.length, c2.length)
    

    5) 패치 일반 어린이


    textContent의 각 상황에 대해 우리는 4) 노드가 공통점을 가진 하위 노드를 필요로 한다.
    for (let i = 0; i < commonLength; i++) {
        patch(c1[i], c2[i])
    }
    
    길이가 같은 상황에서 이것은 이미 그것이다.할 거 없어요.

    6) DOM에서 불필요한 하위 객체 제거


    새 노드의 하위 노드가 이전 노드보다 적으면 DOM에서 이 하위 노드를 제거해야 합니다.이 함수 patch 를 작성했기 때문에 추가 하위 항목을 훑어보고 마운트 해제해야 합니다.
    if (c1.length > c2.length) {
        c1.slice(c2.length).forEach(child => {
            unmount(child)
        })
    }
    

    7) DOM에 추가 하위 객체 추가


    새 노드가 이전 노드보다 하위 노드가 많으면 DOM에 추가해야 합니다.우리도 이미 이 때문에 unmount 함수를 썼다.이제 다른 하위 항목에 걸쳐 로드해야 합니다.
    else if (c2.length > c1.length) {
        c2.slice(c1.length).forEach(child => {
            mount(child, el)
        })
    }
    
    그렇습니다.우리는 노드 간의 차이점을 발견하고 그에 상응하여 DOM을 수정했다.그러나 이 해결 방안이 실현되지 않은 것은 속성 보완이다.이것은 블로그 게시물을 더욱 길게 할 뿐만 아니라 요점을 놓칠 수도 있다.

    실제 DOM에서 가상 트리 렌더링


    이제 가상 DOM 엔진이 준비되었습니다.그것을 보여주기 위해서 우리는 일부 노드를 만들고 렌더링을 할 수 있다.다음과 같은 HTML 구조가 필요하다고 가정합니다.
    <div class="container">
        <h1>Hello World 🌍</h1>
        <p>Thanks for reading the marc.dev blog 😊</p>
    </div>
    

    1) mount를 사용하여 가상 노드 생성


    const node1 = h('div', { class: 'container' }, [
        h('div', null, 'X'),
        h('span', null, 'hello'),
        h('span', null, 'world'),
    ])
    

    2) 노드를 DOM에 마운트


    새로 만든 DOM을 마운트하려고 합니다.어디파일 위쪽h -div:
    mount(node1, document.getElementById('app'))
    
    결과는 다음과 같다.

    3) 두 번째 가상 노드 생성


    이제 두 번째 노드를 만들고 변경할 수 있습니다.몇 개의 노드를 추가했습니다. 결과는 다음과 같습니다.
    <div class="container">
        <h1>Hello Dev 💻</h1>
        <p><span>Thanks for reading the </span><a href="https://marc.dev">marc.dev</a><span> blog</span></p>
        <img src="https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif" style="width: 350px; border-radius: 0.5rem;" />
    </div>
    
    다음은 이 노드를 만드는 코드입니다.
    const node2 = h('div', { class: 'container' }, [
        h('h1', null, 'Hello Dev 💻'),
        h('p', null, [
            h('span', null, 'Thanks for reading the '),
            h('a', { href: 'https://marc.dev' }, 'marc.dev'),
            h('span', null, ' blog'),
        ]),
        h(
            'img',
            {
                src: 'https://media.giphy.com/media/26gsjCZpPolPr3sBy/giphy.gif',
                style: 'width: 350px; border-radius: 0.5rem;',
            },
            [],
        ),
    ])
    
    보시다시피, 우리는 일부 노드를 추가하고, 또 하나의 노드를 변경했습니다.

    4) 두 번째 노드 렌더링


    우리는 두 번째 노드로 첫 번째 노드를 교체하고 싶어서 사용하지 않는다#app.우리가 해야 할 일은 양자 간의 차이를 찾아내고 변경한 후에 과장하는 것이다.그래서 우리mount
    setTimeout(() => {
        patch(node1, node2)
    }, 3000)
    
    여기에 시간 초과를 추가했기 때문에 코드 DOM이 변경된 것을 볼 수 있습니다.없으면 렌더링된 새 VDOM만 표시됩니다.

    총결산


    그렇습니다!우리는 매우 기본적인 DOM 엔진 버전을 가지고 있는데, 이것은 우리를 허용한다.
  • 가상 노드 생성
  • DOM에 가상 노드 마운트
  • DOM에서 가상 노드 제거
  • 두 가상 노드 간의 차이를 찾아 DOM
  • 을 업데이트합니다.
    너는 에서 우리가 이 문장에서 한 코드를 찾을 수 있다.만약 네가 단지 그것을 가지고 놀고 싶을 뿐이라면, 나는 또 하나를 만들었기 때문에, 너는 이렇게 할 수 있다.
    만약 이것에 대해 또 어떤 문제가 있으면 언제든지 저에게 연락을 주십시오.
    원본 표지 사진은 Github Gist I prepared for you Unplash에서 촬영되었고 Codepen 편집되었다.

    좋은 웹페이지 즐겨찾기