바닐라 JSX

안녕하세요 여러분! 이 기사에서는 TypeScript 및 Vite 번들러가 있는 프로젝트에서 JSX를 사용하는 방법을 설명합니다. 이 자료는 다음과 같은 경우에 적합합니다.
  • ⚛️ React에 대한 경험이 있지만 JSX를 처리하는 방법에 대해 알지 못함
  • 🕵️‍♂️ 프론트엔드 기초가 궁금하다
  • 🤓 바닐라 TypeScript와 모든 것을 사랑하는 괴짜

  • 왜요? 물론 재미를 위해! 바닐라 JSX의 아이디어는 오버 엔지니어링 없이는 실제 프로젝트에 적합하지 않습니다. 아마도 JSX 지원을 확장하면 새로운 프런트엔드 프레임워크가 생성될 것입니다. 따라서 새 브라우저 탭에서 GitHub repo을 열고 편안하게 사용하십시오. 우리는 앞으로 JSX에 대해 깊이 파고들 것입니다!

    JSX란 무엇인가요?



    JSX는 JS에 대한 구문 확장입니다. ECMAScript 표준이 아니므로 Babel and React과 같은 도구는 JSX가 일반 JavaScript로 변환되는 것을 처리합니다. 고전적인 JSX 예제를 살펴보겠습니다.

    const profile = (
      <div>
        <img src="avatar.png" className="profile" />
        <h3>{[user.firstName, user.lastName].join(" ")}</h3>
      </div>
    );
    

    @babel/plugin-transform-react-jsx 실행 후 브라우저에서 코드를 이해할 수 있게 됩니다.

    const profile = React.createElement(
      "div",
      null,
      React.createElement("img", { src: "avatar.png", className: "profile" }),
      React.createElement("h3", null, [user.firstName, user.lastName].join(" "))
    );
    


    보시다시피 Babel은 JSX를 깔끔한React.createElement 함수로 성공적으로 변환했습니다. 이는 래퍼 태그, 해당 속성(또는 위의 경우 속성 - null ) 및 하위 요소로 구성되며 차례로 동일한 기능으로 생성됩니다.

    React, Vue 및 Solid 프레임워크는 자체적으로 JSX를 처리하지만 다르게 수행합니다. 이는 createElement 함수의 구현이 다르기 때문에 발생합니다. 그건 그렇고, JSX Pragma라고합니다. 그것에 대해 알게 되었을 때, 나는 즉시 나만의 Pragma를 만들기로 결정했습니다.

    JSX 파싱



    Pragma 생성에 뛰어들기 전에 JSX를 구문 분석하는 방법을 배워야 합니다. 오래된 브라우저 지원이 없는 소규모 현대 프로젝트의 경우 Babel이 필요하지 않지만 Vite 또는 TypeScript이면 충분합니다. 우리는 둘 다 사용할 것입니다.



    Vite은 최신 프런트엔드 앱 번들러입니다. Webpack에 비해 native ESM 이상의 소스 코드를 제공합니다. 프로젝트를 부트스트랩하려면 다음 명령을 실행하기만 하면 됩니다.

    npm create vite@latest
    


    Vite 및 TypeScript는 기본적으로 .jsx 또는 .tsx 파일에서 JSX를 구문 분석합니다. 파싱 ​​결과를 React.createElement 함수에 대입합니다. 그러나 사용자 지정 함수를 대체하려면 tsconfig.json 를 변경해야 합니다.

    {
      "compilerOptions": {
        "jsx": "preserve",
        "jsxFactory": "h",
        "jsxFragmentFactory": "Fragment"
      }
    }
    


    TypeScript 없이 앱을 작성하는 경우 vite.config.js 를 변경하십시오.

    import { defineConfig } from 'vite';
    export default defineConfig({
      esbuild: {
        jsxFactory: 'h',
        jsxFragment: 'Fragment'
      }
    });
    


    이러한 설정은 파서가 루트 요소가 하나만 있는 JSX에 대해 h(하이퍼스크립트, 하이퍼텍스트 + 자바스크립트를 의미) 함수를 사용하고 다중 루트 JSX에 대해 Fragment를 사용하도록 지시합니다.

    JSX 프라그마


    h 함수를 처리하도록 파서를 구성한 후 src/pragma.ts에서 구현을 시작할 수 있습니다.

    // Tag can be string or a function if we parse the functional component 
    type Tag = string | ((props: any, children: any[]) => JSX.Element);
    
    // Attributes of the element – object or null
    type Props = Record<string, string> | null;
    
    // Element children – return value from the h()
    type Children = (Node | string)[];
    
    export const h = (tag: Tag, props: Props, ...children: Children) => {
      // If tag is a component, call it
      if (typeof tag === 'function') {
        return tag({ ... props }, children);
      }
      // Create HTML-element with given attributes
      const el = document.createElement(tag);
      if (props) {
        Object.entries(props).forEach(([key, val]) => {
          if (key === 'className') {
            el.classList.add(...(val as string || '').trim().split(' '));
            return;
          }
          el.setAttribute(key, val);
        });
      }
    
      // Append child elements into the parent
      children.forEach((child) => {
        el.append(child);
      });
    
      return el;
    };
    

    createElement 와 마찬가지로 h 함수는 하위 요소에 대한 태그 이름(또는 기능 구성 요소), 속성 및 h 함수의 결과를 허용합니다.

    모든 .jsx 파일은 h 함수를 가져와야 하므로 변환 후 코드 범위에 있습니다. 예를 들어 이것은 간단한 JSX 사용법입니다.

    import { h } from '../pragma';
    import { LikeComponent } from './like';
    export const App = (
      <main className="hello">
        <h1>
          Hello JSX!
        </h1>
        <LikeComponent big />
      </main>
    );
    


    이제 우리가 해야 할 일은 변환된 코드를 HTML에 추가하는 것입니다.

    import { App } from './components/app';
    const app = document.querySelector<HTMLDivElement>('#app')!
    app.append(App);
    


    그게 다야! JSX를 구문 분석하는 TypeScript와 웹 사이트에 표시하기 위한 올바른 DOM을 생성하는 Pragma로 앱을 만들었습니다!

    실용



    처음에 말했듯이 이 아이디어는 실제 프로젝트에서 사용하기 위한 것이 아닙니다. 런타임 라이브러리를 사용하지 않고 문자 그대로 바닐라 JS에서 작동하는 JSX를 구문 분석하는 것이 얼마나 쉬운지 보여줍니다.

    JSX Pragma의 개념은 확장하기 어렵습니다. 기능 구성 요소에 논리를 추가하려면 변수 및 이벤트 리스너로 사례를 처리해야 하므로 the concept of reactivity 을 다시 구현해야 합니다.

    결론



    JSX Pragma와 같은 비표준 개념은 프레임워크 없이 바닐라 JS만 있으면 쉽게 처리할 수 있음이 밝혀졌습니다!

    손에 넣을 수 있는 모든 기술을 실험하고 가능한 한 깊이 들어가도록 권장합니다. 행운을 빕니다!

    좋은 웹페이지 즐겨찾기