당신의 머릿속에서 날씬하게 컴파일하기 ({# 만약})

본문에서 우리는 우리의 첫 번째 논리 블록,if 블록을 소개할 것이다.
우리의 관점이 일치하는지 확인하기 위해서, 우선ifblock이 어떻게 일을 하는지 설명해 봅시다.
{#if} 블록
컨텐트를 조건부로 표시하려면 {#if} 블록을 사용하여 포장할 수 있습니다.
{#if condition}
  <div>Conditionally rendered content</div>
{/if}
만약 condition가 진실이라면, 너는 볼 수 있을 것이다 <div>Conditionally rendered content</div>. 그렇지 않으면 너는 아무것도 볼 수 없을 것이다.
JavaScript와 마찬가지로 Svelte에서 elseelse if를 사용하여 여러 조건을 테스트할 수도 있습니다.
{#if condition_a}
  <div>Rendered due to condition_a</div>
{:else if condition_b}
  <div>Rendered due to condition_b</div>
{:else}
  <div>Otherwise</div>
{/if}
Svelte의 상호작용 강좌를 방문하여 {#if} logic block에 대한 더 많은 정보를 얻을 수 있습니다.

바닐라 JS
그렇다면 우리는 어떻게 아무런 틀도 없는 상황에서 {#if} 논리를 실현할 수 있을까?
앞에서 말한 바와 같이, 우리는 프레임 도움말 없이 요소를 만드는 방법을 보았다.

구현 if 블록
구현{#if} 논리 블록은 다음과 같습니다.
function createElementsIfConditionA() {
  // code to create `<div>Rendered due to condition_a</div>`
}
function createElementsIfConditionB() {
  // code to create `<div>Rendered due to condition_b</div>`
}
function createElementsElse() {
  // code to create `<div>Otherwise</div>`
}

function createIfBlock() {
  if (condition_a) {
    createElementsIfConditionA();
  } else if (condition_b) {
    createElementsIfConditionB();
  } else {
    createElementsElse();
  }
}
condition_acondition_b는 동적일 수 있다. 이것은 조건에 변화가 발생하면 우리는 다시 호출해야 한다는 것을 의미한다createIfBlock.
그 전에 만들어진 요소를 삭제해야 합니다.이것은 이전에 어떤 조건을 만족시켰는지, 그리고 이전에 어떤 요소를 만들었는지에 달려 있다.
그러면 이러한 정보를 변수에 저장합니다.
function destroyElementsIfConditionA() {
  // code to destroy `<div>Rendered due to condition_a</div>`
}
function destroyElementsIfConditionB() {
  // code to destroy `<div>Rendered due to condition_b</div>`
}
function destroyElementsElse() {
  // code to destroy `<div>Otherwise</div>`
}

let previousDestroy;
function getPreviousDestroy() {
  if (condition_a) {
    previousDestroy = destroyElementsIfConditionA;
  } else if (condition_b) {
    previousDestroy = destroyElementsIfConditionB;
  } else {
    previousDestroy = destroyElementsElse;
  }
}

function createIfBlock() {
  // ...
  getPreviousDestroy();
}
따라서 조건이 변경되면 이전에 생성한 요소를 제거하고 새 요소를 생성합니다.
function updateIfBlock() {
  // if `condition_a` or `condition_b` changed
  if (conditionChanged) {
    previousDestroy();
    createIfBlock();
  }
}
단, 조건이 바뀌지 않았더라면if 블록의 내용에 변화가 생겼습니다. 예를 들어 value_a, value_b 또는 value_else 다음 코드에 변화가 생겼습니다.
{#if condition_a}
  <div>{ value_a }</div>
{:else if condition_b}
  <div>{ value_b }</div>
{:else}
  <div>{ value_else }</div>
{/if}
그런 다음 요소를 업데이트하는 방법을 알아야 합니다.
function updateElementsIfConditionA() {
  // code to update `<div>{ value_a }</div>`
}
function updateElementsIfConditionB() {
  // code to update `<div>{ value_b }</div>`
}
function updateElementsElse() {
  // code to update `<div>{ value_else }</div>`
}

function updateIfBlock() {
  // if `condition_a` or `condition_b` changed
  if (conditionChanged) {
    previousDestroy();
    createIfBlock();
  } else {
    if (condition_a) {
      updateElementsIfConditionA();
    } else if (condition_b) {
      updateElementsIfConditionB();
    } else {
      updateElementsElse();
    }
  }
}
마지막으로 요소를 제거하려면 전체 블록{#if}을 제거할 때 사용할 수 있습니다previousDestroy. 요소를 작성하는 기준에 기반하기 때문입니다.
function destroyIfBlock() {
  previousDestroy();
}
여기createIfBlock,updateIfBlockdestroyIfBlock가 있습니다.if (condition) 논리가 createIfBlock, getPreviousDestroyupdateIfBlock 사이에 분산되어 있기 때문에 매우 서투르게 보인다.
그래서 이걸 재구성해 봅시다.코드를 바꿔서 더 깨끗하게 합시다.✨

코드 재구성
각 논리적 분기마다 요소를 생성, 업데이트 및 제거할 수 있습니다.첫 번째 조건의 구분에 대해 우리는 다음과 같은 것을 가지고 있다.
  • createElementsIfConditionA
  • updateElementsIfConditionA
  • destroyElementsIfConditionA
  • 보아하니 우리는 이곳에서 좀 고용할 수 있을 것 같다.
    각 조건부 분기의 작업을 그룹화할 수 있으며 각 작업마다 인터페이스가 동일합니다{ create(){}, update(){}, destroy(){} }.
    const operationConditionA = {
      create: createElementsIfConditionA,
      update: updateElementsIfConditionA,
      destroy: destroyElementsIfConditionA,
    };
    const operationConditionB = {
      create: createElementsIfConditionB,
      update: updateElementsIfConditionB,
      destroy: destroyElementsIfConditionB,
    };
    const operationConditionElse = {
      create: createElementsElse,
      update: updateElementsElse,
      destroy: destroyElementsElse,
    };
    
    이제 동일한 인터페이스가 있기 때문에 다음과 같은 조건으로 작업을 선택할 수 있습니다.
    function getOperation() {
      if (condition_a) {
        return operationConditionA;
      } else if (condition_b) {
        return operationConditionB;
      } else {
        return operationConditionElse;
      }
    }
    
    여기서 우리는 우리의 createIfBlock, updateIfBlockdestroyIfBlock를 다시 쓸 수 있다.
    let currentOperation = getOperation();
    
    function createIfBlock() {
      currentOperation.create();
    }
    
    function updateIfBlock() {
      const previousOperation = currentOperation;
      currentOperation = getOperation();
      // if (conditionChanged)
      if (currentOperation !== previousOperation) {
        previousOperation.destroy();
        currentOperation.create();
      } else {
        currentOperation.update();
      }
    }
    
    function destroyIfBlock() {
      currentOperation.destroy();
    }
    
    조건이 변경되었는지 확인하기 위해서 우리는 이 조작을 계산하고 이전의 조작과 비교하여 그것이 변경되었는지 확인할 수 있다.

    컴파일된 JS
    Svelte가 출력 자바스크립트로 {#if} 를 어떻게 컴파일했는지 보여 줍니다.
    <script>
        let loggedIn = false;
    
        function toggle() {
            loggedIn = !loggedIn;
        }
    </script>
    
    {#if loggedIn}
        <button on:click={toggle}>
            Log out
        </button>
    {:else}
        <button on:click={toggle}>
            Log in
        </button>
    {/if}
    
    Svelte REPL
    출력 코드:
    /* App.svelte generated by Svelte v3.25.1 */
    // ...
    function create_else_block(ctx) {
      // ...
      return {
        c() { /* ... */ },
        m(target, anchor) { /* ... */ },
        p: noop,
        d(detaching) { /* ... */ },
      };
    }
    
    // (9:0) {#if loggedIn}
    function create_if_block(ctx) {
      // ...
      return {
        c() { /* ... */ },
        m(target, anchor) { /* ... */ },
        p: noop,
        d(detaching) { /* ... */ },
      };
    }
    
    function create_fragment(ctx) {
      // ...
      function select_block_type(ctx, dirty) {
        if (/*loggedIn*/ ctx[0]) return create_if_block;
        return create_else_block;
      }
    
      let current_block_type = select_block_type(ctx, -1);
      let if_block = current_block_type(ctx);
    
      return {
        c() {
          if_block.c();
          if_block_anchor = empty();
        },
        m(target, anchor) {
          if_block.m(target, anchor);
          insert(target, if_block_anchor, anchor);
        },
        p(ctx, [dirty]) {
          if (current_block_type === (current_block_type = select_block_type(ctx, dirty)) && if_block) {
            if_block.p(ctx, dirty);
          } else {
            if_block.d(1);
            if_block = current_block_type(ctx);
    
            if (if_block) {
              if_block.c();
              if_block.m(if_block_anchor.parentNode, if_block_anchor);
            }
          }
        },
        i: noop,
        o: noop,
        d(detaching) {
          if_block.d(detaching);
          if (detaching) detach(if_block_anchor);
        },
      };
    }
    
    몇 가지 관찰 결과:
    관찰1: 만약에 Svelte의 컴파일 출력과 우리가 전에 발표한 JS 코드를 비교한다면 비슷한 점을 볼 수 있습니다.
  • 모든 논리적 지점에 대해 우리는 하나가 있는데 이 예는 create_else_blockcreate_if_block이다.앞에서 설명한 바와 같이, 이 함수들은 모든 논리 지점에 DOM 세션을 구축하는 방법에 대한 설명 매뉴얼을 되돌려줍니다.
  • 이것은 우리가 앞에서 토론한 조작과 유사하다. 예를 들어 operationConditionA, operationConditionBoperationConditionElse이다.
  • 어떤 create_fragment 함수를 사용하는지 확인하기 위해 우리는 select_block_type 함수를 가지고 있다.
  • 이것은 우리가 앞에서 토론한 getOperation과 유사하다.
  • 그리고 현재 조건부 분기의 세션을 초기화합니다.
  • let current_block_type = select_block_type(ctx, -1);
    let if_block = current_block_type(ctx);
    
  • 이제 다음을 수행할 수 있습니다.
  • 생성 if_block.c()
  • 브래킷if_block.m(target, anchor)
  • 업데이트 if_block.p(ctx, dirty)
  • 폐기if_block.d(detaching)
  • {#if} 블록의 원소.
  • p(u p 날짜) 방법에서 current_block_type가 변경되었는지 확인하고 변경되지 않으면 필요에 따라 호출if_block.p(ctx, dirty)하여 업데이트합니다.
  • 만약 변화가 있다면, 우리는 if_block.d(1) 이전의 요소를 없애고 current_block_type를 바탕으로 새로운 세션을 만들고 if_block.c()if_block.m(...)를 통해 요소를 만들고 불러옵니다.
    이것은 우리가 말한 previousOperation.destroy()currentOperation.create() 또는 currentOperation.update()와 유사하다.
    관찰2:if_block_anchor 이후 삽입if_block
    if_block_anchor = empty()
    
    empty() 빈 텍스트 노드를 만듭니다.
    // https://github.com/sveltejs/svelte/blob/v3.25.1/src/runtime/internal/dom.ts#L56-L58
    export function empty() {
      return text('');
    }
    
    그리고 u p date 방법if_block_anchor을 설치할 때if_block를 사용합니다.
    if_block.m(if_block_anchor.parentNode, if_block_anchor)
    
    그러면 이 추가 빈 텍스트 노드는 무엇을 합니까?

    추가 텍스트 노드
    블록{#if}을 업데이트하고 세그먼트 블록 유형을 변경해야 한다는 것을 알았을 때 이전에 만든 요소를 제거하고 새로 만든 요소를 삽입해야 합니다.
    우리가 새로운 요소를 삽입할 때, 우리는 어디에 그것들을 삽입하는지 알아야 한다. insertBefore API를 사용하면 요소를 삽입할 노드를 지정할 수 있습니다.그래서 지금 질문을 회피하고 있습니다. 어느 노드입니까?
    정답은 구성 요소에 기록된 위치에 따라 달라집니다{#if}.가능한 경우는 4가지입니다.
    1. {#if} 블록 뒤에 원소가 있다
    {#if condition}
      <div />
    {/if}
    <span />
    
    Svelte REPL
    보실 거예요.
  • Svelte에서 추가 텍스트 노드를 만들지 않음
  • Svelte 사용<span /> 노드
  • if_block.m(span.parentNode, span)
    

    When the {#if} condition changes, {#if} block will replace and insert new elements before the <span /> element.


    2. {#if} 블록은 마지막 하위 블록이고 {#if} 블록에는 상위 블록이 있음
    <div>
      {#if condition}
        <div />
      {/if}
    </div>
    
    Svelte REPL
    보실 거예요.
  • Svelte에서 추가 텍스트 노드를 만들지 않음
  • 의 반대로 Svelte는 {#if} 블록을 부 노드<div />에 삽입하고 null 이전에 삽입한다.nullinsertBefore에 전달하면 부가 원소를 마지막 하위 원소로 한다)
  • if_block.m(div, null);
    

    When the {#if} condition changes, {#if} block will replace and insert new elements as the last children of the parent <div /> element.


    3. {#if} 블록은 마지막 하위 항목이고 {#if} 블록은 상위 항목이 없습니다.
    {#if condition}
      <div />
    {/if}
    
    Svelte REPL
    보실 거예요.
  • Svelte에서 추가 anchor 요소 만들기
  • 블록 다음에 anchor 요소를 삽입한다.
  • 다음에 u p date 함수에 {#if} 요소 앞에 {#if} 블록을 삽입합니다.
  • if_block.m(if_block_anchor.parentNode, if_block_anchor);
    

    When the {#if} condition changes, {#if} block will replace and insert new elements before the anchor element.


    그런데 왜요?
    섬세한 조립품은 어디서나 사용할 수 있기 때문이다.
    다음 장면을 살펴보겠습니다.
    <!-- A.svelte -->
    {#if condition}
      <div id="a" />
    {/if}
    
    <!-- B.svelte -->
    <div id="b" />
    
    <!-- App.svelte -->
    <script>
      import A from './A.svelte';
      import B from './B.svelte';
    </script>
    
    <div id="parent">
      <A />
      <B />
    </div>
    
    anchor에서 A.svelte 블록은 마지막 하위 블록이고 그 뒤에는 동급 원소가 없다.
    우선 우리{#if}원소가 없다고 가정하자.anchorcondition에서 false로 바뀌었을 때 Svelte는 새 요소true를 부모 요소에 삽입해야 합니다.<div id="a"> 블록 뒤에 다음 원소도 없고 {#if} 원소도 없기 때문에 우리는 anchor 블록 앞에 삽입해야 한다.여기서 null는 부모 요소<div id="a" />의 마지막 하위 요소로 삽입됩니다.야, 우리가 벌레 한 마리를 발견했어!내부 원소<div id="parent"><A /> 뒤에 나타납니다!
    <div id="parent">
      <div id="b"></div>
      <div id="a"></div> <!-- newly inserted element -->
    </div>
    
    우리는 <B /> 원소를 첨가함으로써 이런 상황이 발생하는 것을 방지할 수 있다.anchorcondition인 경우 DOM은 다음과 같습니다.
    <div id="parent">
      <#text /> <!-- an empty text node, not visible to the user -->
      <div id="b"></div>
    </div>
    
    false회전condition할 때 우리는 true원소 앞에 <div id="a" />:
    <div id="parent">
      <div id="a"></div> <!-- newly inserted element -->
      <#text /> <!-- an empty text node, not visible to the user -->
      <div id="b"></div>
    </div>
    
    네, 저희는 anchor<A />의 질서를 유지하고 있습니다.🎉 !<B /> 블록의 anchor 원소는 an anchor to a ship와 유사하다. "이곳은 {#if} 블록이 마땅히 {#if}해야 할 곳!"
    4.insertBefore() 블록 뒤에 다른 논리 블록 연결
    마지막 장면.{#if} 블록 뒤에 다른 논리 블록 연결:
    {#if condition}
      <div id="a" />
    {/if}
    {#if condition2}
      <div id="b" />
    {/if}
    
    두 번째{#if}의 막힘 조건은 {#if} 또는true일 수 있다.이것은 false 거기에 있을 수도 있고 없을 수도 있다는 것을 의미한다.
    따라서 변경<div id="b" />할 때 어디에 삽입해야 하는지<div id="a" />를 알아야 한다. 우리는 첫 번째condition 블록 다음, 두 번째anchor 블록 앞에 {#if} 요소를 삽입해야 한다.

    폐막사
    우리는 Svelte가 {#if} 블록을 어떻게 컴파일하는지, 그리고 {#if} 블록이 필요로 하는 anchor 요소의 방식과 원인을 소개했다.
    날씬함에 대해 더 알고 싶다면.
    다음 부분을 준비한 후에 다음 글은 {#if} 논리 블록에 관한 것이다.

    좋은 웹페이지 즐겨찾기