ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Svelte API
    Svelte_private 2021. 1. 23. 17:15

    @ Reference

    https://svelte.dev/docs

    https://heropy.blog/2019/09/29/svelte/

     

    새로운 프론트엔드 프레임워크라길래 Svelte에 대해 몇 가지 학습을 해보았다.

    학습을 많이 한 건 아니지만 게임을 찍먹한다는 느낌으로 배워봤다

    Svelte는 기존 React와 사용법의 차이가 있지만 자세히 보니 vue와 매우 닮아있다.

    개인적으로 Vue를 자세히는 모르지만 v- 접두사도 있고 computed 같은 속성?들도 있어서 썩 와닿지도 않았고

    직관적이지 않았다

     

    그렇게 많이 써본 것은 아니지만 React에 비해 뭔가 많이 아쉽다고 느꼈다.

     

    1. 확장자가 .svelte라는 것인데, VS Code에서 지원하는 확장이 있는진 모르겠지만 아이콘이 일반 Plain Text처럼 되어있다.

     

    2. 다소 헷갈리는 속성 네이밍

    => 익숙하지 않아서 그런지 모르겠지만 bind:~~, use:~~, class:~~, on:~~ 이러한 디렉티브들이 너무 헷갈린다.

    거기다 문법 강조(Syntax Highlighting) 확장이 있긴 하지만 콜론(:) 앞의 키워드들은 보라색이고 뒤이어 나오는 이벤트 등은 녹색이고 컴포넌트도 녹색이여서 보면은 어지럽다

     

    3. cli와 빌드도구

    CRA같은 cli가 없고 템플릿을 찾아서 degit을 설치하고 저장소에서 복사해와야 해서 불편하다

    기존 템플릿을 복사하면 rollup 번들러를 제공해주는데 새롭게 snowpack이라는 빌드 도구가 나와서 rollup은 그닥 좋아보이진 않는다. snowpack은 매우 빠른 속도로 개발서버를 열어주고 빌드시간도 매우 짧다. snowpack은 굉장히 좋아보인다. 리액트에 적용하면 개발서버 켜는데 10초남짓하던게 매우 빨리 줄어들 것 같단 생각이 든다

     

    4. 한 파일에서 여러 컴포넌트 작성 불가

    리액트에선 하나의 js 파일 안에 JSX Element를 반환하는 함수를 여러 개 만들어 관리할 수 있다.

    => 컴포넌트를 여러 개 쓸 수 있다.

    그러나 svelte에선 하나의 파일에는 하나의 컴포넌트만 작성할 수 있다.

     

    5. 기존 ES6와는 다른 props 정의

    ES6에서 export를 하게 되면 변수, 함수 등을 내보낼 수 있다.

    그러나 svelte에서는 export로 props를 가져와서 ES6 기능을 하려면 js파일을 따로 작성해야 한다...

    <script>
      import Article from './Header.svelte'
      
      export let stroy = 'This is our story'
    </script>
    
    <Article story={story} />

     

    6. 확장 프로그램과 성능

    확장 프로그램을 설치해봤으나 어느 부분이 리렌더링이 되었는지를 알려주는 기능이 없어서 최적화는 어떻게 해야 하는지?라는 의문이 생기긴 한다.

    최적화를 어떻게 할 것인지는 찾아봐야겠지만 공식 문서의 API, Blog, FAQ에서 성능과 관련된 단어로 검색해봐도 자료는 나오지 않는다.

     

    7. 그 외의 장점

    반면, 단점만 언급했지만 장점도 있다. 확장 프로그램에서 렌더링하는데 얼마나 걸렸는지, 무엇이 시간을 오래잡아먹었는지 그래프로 빠르게 보여준다. 다른 확장 프로그램은 Redux devtools처럼 state 상태도 보여준다고 소개를 하지만 따로 설치를 진행하진 않았다.

     

    또한 스토어와 여러 디렉티브, 다중 이벤트 핸들러 등 내장 기능이 다양하다.

    React에서도 스토어 기능이 가능은 하지만 코드량이 Svelte에 비해 비약적으로 늘어난다.

     

    결론: 위를 포함한 몇몇 이유들로 인해 JS 친화적인 React를 계속 쓸 것 같다.

    Svelte는 버전 안정과 라이브러리 생태계가 더 좋아지면 쓸만하지 않을까 싶다.

    아직은 눈 여겨보는 정도?

     

    서론이 길었지만 공부한 것들을 정리해본다면 다음과 같다.

    1] 표현식

    기존 React와 동일하게 중괄호를 통해 변수의 값을 입력할 수 있다.

    <script>
      let x = 123
      let y = 456
      const person = { name: `Chifuyu`, shirtColor: `red` }
      const { name, shirtColor } = person
    </script>
    
    <h3>{x}</h3>
    <h3>{x + y}</h3>
    <div>
      name: {name}, shirt-color: {shirtColor}
    </div>

     

     

    2] each

    each를 비롯한 몇 가지 블록 구문은 규칙이 있다.

    {#키워드 (표현식)} <!-- << 시작하는 지점, 키워드 앞에 # -->
    {:키워드 (표현식)} <!-- 분기처리할 지점, 키워드 앞에 : -->
    {/키워드} <!-- << 끝나는 지점, 키워드 앞에 / -->

     

    React에서의 map함수, RN에서의 FlatList와 내부동작은 어떤지 모르겠으나

    컴포넌트를 반복해서 출력한다는 기능은 비슷하다. 

    <script>
      const colors = [
        { id: 1, name: 'red', hex: '#FF0000' },
        { id: 2, name: 'green', hex: '#00FF00' },
        { id: 3, name: 'blue', hex: '#0000FF' },
        { id: 4, name: 'white', hex: '#FFFFFF' },
        { id: 5, name: 'black', hex: '#000000' },
      ];
    </script>
    <!-- #each 반복할 데이터 as 꺼내온 데이터를 구조분해, (key) -->
    <!-- as 생략가능하고 as를 사용하면 데이터를 as 뒤에 나온 변수로 사용하겠다는 의미 -->
    {#each colors as { name, hex } (colors.id)}
      <div style='background-color: {hex};'>{name}</div>
    {/each}

     

    3] if

    컴포넌트 내부에서 조건 분기처리를 한다.

    통상 JavaScript에서의 if와 같다고 보면 되겠다.

    조건식이 참이면 #if 바로 아래부터 실행을 하고

    조건식이 거짓이면 else 블록 내부를 실행하거나 else가 없다면 실행하지 않는다.

    <script>
      let isTrue = true
    </script>
    
    <!-- #if 조건식 -->
    {#if isTrue}
      <span>True!</span>
    <!-- else if도 가능 -->
    {:else}
      <span>False!</span>
    {/if}

     

    4] key

    key 블록은 key에 연결된 조건식이 변경되면, key 블록 내부에 작성된 것들을 모두 파괴하고 재생성한다

    => 컴포넌트를 초기화

     

    <!-- hex가 바뀌면 기존의 div를 파괴한 뒤 재생성하고
    if코드와 key 코드는 hex 뒤에 as를 붙일 수 없음 -->
    
    {#key hex}
      <div style='background-color: {hex};'></div>
    {/key}

     

    5] await 

    비동기 컴포넌트를 렌더링하는 구문이다.

    <!-- anyPromise라는 프로미스가 pending이면 value이전까지의 내용을 실행하고 -->
    <!-- fulfilled 상태가 되면 then 블록 내부를 실행하며 -->
    <!-- rejected 상태가 되면 catch 블록 내부를 실행한다. -->
    
    {#await anyPromise}
    	<p>loading...</p>
    {:then value}
    	<p>resolve! {value}</p>
    {:catch error}
    	<p>reject! {error.message}</p>
    {/await}

     

    6] @html

    React에서 태그에 html을 삽입할 때와 같은 기능을 한다

    {@html 변수} 와 같은 형태로 사용하며 {@html}로 변수를 감싸면 효력이 발생하지 않으니 주의

    <div class='post'>
      {@html post.content}
    </div>

     

    7] bind:this

    해당 요소에 바인딩을 한다.

    바인딩을 하고 focus를 했음에도 svelte는 데이터가 변경됐어도 화면을 자동으로 갱신해주지 않기 때문에

    밑에서 살펴볼 tick 함수를 써야 한다.

    <script>
      let inputElement;
      async function focusing() {
        await tick();
        inputElement?.focus();
      }
      focusing();
    </script>
    
    <input bind:this={inputElement} />

     

    8] lifecycle

    Svelte는 컴포넌트를 연결할 때는 beforeUpdate -> onMount -> afterUpdate 순으로 라이프사이클을 가지며

    컴포넌트 연결이 해제되기 직전에는 onDestroy 함수가 실행되고

    컴포넌트의 데이터가 갱신되면 beforeUpdate -> afterUpdate의 라이프사이클이 실행된다

    그리고 데이터가 변경되었어도 화면을 갱신하기까지 기다려주려면 tick이라는 비동기함수를 활용하면 된다

     

    1) onMount

    2) beforeUpdate

    3) afterUpdate

    4) onDestroy

    5) tick

    <script>
      import { onMount, onDestroy, beforeUpdate, afterUpdate, tick } from 'svelte';
      
      let inputElement
      
      beforeUpdate(() => {
        console.log('데이터 갱신 전')
      })
      
      onMount(() => {
        console.log('컴포넌트 연결')
        return console.log('컴포넌트 해제') // return하면 onDestroy와 같은 역할을 함
      })
      
      afterUpdate(() => {
        console.log('데이터 갱신 후')
      })
      
      onDestroy(() => {
        console.log('컴포넌트 연결 해제')
      })
      
      // tick은 비동기 함수이므로 async await 키워드를 붙여줘야 한다
      async function wating() {
        await tick()
        inputElement?.focus()
      }
    </script>
    
    <input bind:this={inputElement} />

     

    9] Context API

    React와 마찬가지로 Context API를 지원한다.

    다만 형제요소, 부모요소로는 데이터가 전달되지 않고 그 자식 컴포넌트와 후손 컴포넌트로만 전달된다.

     

    1) getContext: context 데이터를 읽어옴

    2) setContext: context 데이터를 변경

    3) hasContext: context를 가지고 있는지 확인

      <!-- App.svelte -->
      <script>
        import { setContext } from 'svelte';
        import Test from './Test.svelte';
        setContext('num', 5);
      </script>
      
      <Test />
    <!-- Test.svelte -->
    <script>
      import { getContext, hasContext } from 'svelte';
    
      const number = getContext('num'); // 5
      console.log(hasContext('num')); // true
    </script>
    
    <p>{number} {hasContext('num') ? 1 : 0}</p>
    

     

    10] store

    1) writable: 쓰기가능한 스토어 객체를 생성

    2) readable: 읽기만 가능한 스토어 객체를 생성

    3) derived: 계산된 스토어 객체

    4) get: 스토어 객체의 값을 읽어옴

     

    Redux의 스토어와 비슷한 개념이다

    어느 한 곳에 데이터를 저장하고 여러 컴포넌트에서 데이터를 읽거나 쓰거나 계산할 수 있다.

    자동 구독 형태는 값을 자동으로 갱신해준다.

    수동 구독 형태는 subscribe 메서드를 필수적으로 구현해야 하며 set, update 메서드는 옵션이다.

    <!-- App.svelte -->
    <script>
      import { writable } from 'svelte/store';
      const data = writable(3);
    </script>
    
    <Test {data} />
    <!-- Test.svelte -->
    <script>
      import { get } from 'svelte/store';
    
      export let data;
      
      function increasing() {
        $data += 1;
      }
      console.log(get($data))
      // $data가 아닌 data를 출력하면 set, update, subscribe로 구성된 스토어 객체가 나온다.
      // 값을 조회하려면 자동 구독 형태인 접두사 $를 붙여서 사용하면 된다.
    </script>
    
    <h1 style="font-size: 24px;">{$data}</h1>
    <button on:click={increasing}>increase</button>
    

    주의할 점은 svelte 컴포넌트가 아닌 파일에서는 자동구독을 할 수 없어 subscribe 메서드를 써야 한다

     

    11] Event

    요소에 이벤트 핸들러를 여러 개 등록할 수 있고 다중으로도 등록할 수 있다.

    <script>
      import { tick } from 'svelte'
      
      let nums = 3
      let clicked = false
      let Element
      
      function increase() {
        nums += 1
      }
      
      async function convert() {
        clicked = !clicked
        await tick()
        clicked 
          ? Element.style.backgroundColor = 'red'
          : Element.style.backgroundColor = 'skyblue'
      }
      
      const onPress = (e) => {e.key === 'Enter' && increase()}
      
    </script>
    
    <div class='container'
      on:click={increase} on:click={convert}
      on:keydown={onPress} bind:this={Element}>
        <span>{nums}</span>
        <p>Window</p>
    </div>
    
    <style>
      .container {
        width: 100px;
        height: 100px;
        background-color: skyblue;
        display: flex;
        justify-content: space-around;
        align-items: center;
      }
      :global(.container p) {
        color: white;
        margin: 0;
        padding: 0;
        line-height: 1;
      }
    </style>

     

    12] Class

    Class API는 jQuery에서 active라는 이름을 가진 클래스를 추가하고 없애고 하던 그런 방식이다.

    개발자 도구를 켜고 div 요소를 클릭하고 버튼을 계속 누르면 div 요소에 textColor라는 클래스가 생성되고 삭제된다.

    <script>
      let textColor = false;
      function reversing() {
        textColor = !textColor;
        // Svelte는 !textColor만 작성하면 반응성을 가지지 않기 때문에
        // 할당문을 통해서 반응성을 가지도록 작성을 해줘야 함!
      }
    </script>
    
    <div class:textColor>{textColor ? 'true' : 'false'}</div>
    <button on:click={reversing}>reverse</button>
    
    <style>
      div {
        background-color: 'yellow';
      }
      div.textColor {
        background-color: 'red';
      }
    </style>
    

     

    13] use:action

    컴포넌트가 생성되고 사라질 때 함수가 바인딩되고 실행되는 디렉티브이다

     

    <script>
      let param = 10;
      
      function action(node, param1 = 30) {
        // 컴포넌트가 mount 됐을 때 실행됨
        // 첫 번째 매개변수는 자동으로 바인딩된 요소를 가리킨다.
        node.style.color = 'skyblue';
        // node는 연결된 요소 => 여기서는 h1태그가 되겠다.
        return {
          update: (params1) => {
            // 매개변수가 변경될 때마다 실행되는 함수
            console.log(params1);
          },
          destroy() {
            // 컴포넌트가 destroy 될 때 실행됨
            console.log('destroy');
          },
        };
      }
      
      function increase() {
        param += 1;
      }
    </script>
    
    <!-- 여기서의 param은 action함수 파라미터의 두 번째부터 들어간다 -->
    <h1 use:action={param}>{param}</h1>
    <button on:click={increase} type="button">use me!</button>

     

     

    14] props 전달

    자식 컴포넌트에게 props를 전달하는 방법은 다음과 같다.

     

    1) 단방향 바인딩

    <!-- App.svelte -->
    <script>
      const isNumber = isNaN(5) // false
    </script>
    
    <!-- 위와 아래는 같다. 속성의 이름과 데이터의 이름이 같으면 속성 생략 가능 -->
    <Test isNumber={isNumber} />
    <Test {isNumber} />

    2) 양방향 바인딩

    <!-- App.svelte -->
    <script>
      const isNumber = isNaN(5) // false
    </script>
    
    <!-- 위와 아래는 같다. -->
    <!-- 디렉티브가 붙고 속성의 이름과 데이터의 이름이 같으면 데이터 생략 가능 -->
    <Test bind:isNumber={isNumber} />
    <Test bind:isNumber />

     

    15] 스타일

    스타일은 html 작성하듯이 작성하면 된다.

    <div class='container'>
      <p>Window</p>
    </div>
    
    
    <style>
      .container {
        width: 100px;
        height: 100px;
        background-color: skyblue;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      
      /* 모든 컴포넌트에서 전역화할 스타일은 :global()함수를 사용 */
      :global(.container p) {
        color: white;
      }
      @keyframes -global-animations {
        /* ... */
      }
    </style>

     

    16] 반응성 레이블 구문

    <script>
      // Svelte는 레이블 구문에 특별한 의미를 부여하고 반응성을 자동으로 계측
      // Svelte 내에서 레이블을 사용할 때 let을 사용하지 않도록 주의!
      let num = 0
      $: num, console.log(num)
      $: for (let i = 0; i < 2; i+=1) {
        num
        console.log(i)
      }
      $: if (num > 1) {
        console.log('big')
      }
      function increase() {
        num += 1
        count += 1
      }
      // Generator 함수에는 레이블 구문이 적용되지 않으니 주의!
    </script>
    
    <button on:click={increase}>increase</button>

     

     

    @@@@@

     

    'Svelte_private' 카테고리의 다른 글

    Svelte 소개  (0) 2021.01.02

    댓글

Designed by Tistory.