모든 것에 구성요소가 필요한 것은 아닙니다

DDD
풀어 주다: 2024-11-26 03:32:12
원래의
204명이 탐색했습니다.

Not Everything Needs a Component

2000년대 초 Divitis라는 신조어는 div 요소가 많은 웹페이지 코드를 작성하는 관행을 일컫는다. 의미 있는 HTML 요소의 위치. 이는 점진적 향상 기술의 틀 내에서 HTML의 의미에 대한 인식을 높이려는 노력의 일환이었습니다.

20년이 지나서 저는 웹 개발자에게 영향을 미치는 새로운 증후군을 목격했습니다. 저는 구성 요소염이라고 부릅니다. 제가 정의한 내용은 다음과 같습니다.

구성요소염: 더 간단하고 재사용 가능한 솔루션 대신 UI의 모든 측면에 대한 구성요소를 만드는 관행.

구성요소

그럼 우선 컴포넌트란 무엇일까요? 제 생각에는 React가 빌딩 블록을 지칭하기 위해 이 용어를 대중화한 것 같습니다.

React를 사용하면 마크업, CSS 및 JavaScript를 사용자 정의 "구성 요소", 앱의 재사용 가능한 UI 요소
로 결합할 수 있습니다. — React 문서 - 첫 번째 구성 요소

재사용 가능한 UI 요소라는 개념은 당시에는 새로운 것이 아니었지만(CSS에는 이미 OOCSS, SMACSS, BEM과 같은 기술이 있었습니다), 주요 차이점은 마크업 위치, 스타일 및 위치에 대한 독창적인 접근 방식입니다. 상호 작용. React 구성 요소(및 모든 후속 UI 라이브러리)를 사용하면 구성 요소 경계 내에서 단일 파일의 모든 항목을 같은 위치에 배치할 수 있습니다.

따라서 Facebook의 최신 CSS 라이브러리인 Stylex를 사용하여 다음과 같이 작성할 수 있습니다.

import * as stylex from "@stylexjs/stylex";
import { useState } from "react";

// styles
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: "#000",
    },
});

export function Toggle() {
    // interactions
    const [toggle, setToggle] = useState(false);
    const onClick = () => setToggle((t) => !t);

    // markup
    return (
        <button {...stylex.props(styles.base)} type="button" onClick={onClick}>
            {toggle}
        </button>
    );
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

객체 표기법으로 CSS를 작성하는 것을 좋아할 수도 있고 그렇지 않을 수도 있지만 이러한 수준의 공동 배치는 종종 구성 요소 기반 프로젝트를 보다 유지 관리하기 쉽게 만드는 좋은 방법입니다. 명시적으로 바인딩됩니다.

Svelte와 같은 라이브러리에서는 공동 위치가 훨씬 더 명확하고 코드도 더 간결합니다.

<script>
    let toggle = $state(false)
    const onclick = () => toggle = !toggle
</script>

<button type='button' {onclick}>
    {toggle}
</button>

<style>
button {
    font-size: 16px;
    line-height: 1.5;
    color: #000;
}
</style> 
로그인 후 복사
로그인 후 복사

시간이 지남에 따라 이 패턴은 모든 것이 구성 요소에 캡슐화될 정도로 많은 관심을 끌었습니다. 아마도 다음과 같은 페이지 구성 요소를 접했을 것입니다.

export function Page() {
    return (
        <Layout>
            <Header nav={<Nav />} />
            <Body>
                <Stack spacing={2}>
                    <Item>Item 1</Item>
                    <Item>Item 2</Item>
                    <Item>Item 3</Item>
                </Stack>
            </Body>
            <Footer />
        </Layout>
    );
}
로그인 후 복사
로그인 후 복사

하나의 공동 위치

위 코드는 깔끔하고 일관적으로 보입니다. 페이지를 설명하기 위해 구성요소 인터페이스를 사용합니다.

그럼 스택 구현이 가능한지 살펴보겠습니다. 이 구성 요소는 일반적으로 모든 직계 하위 요소가 수직으로 쌓이고 균일한 간격으로 배치되도록 하는 래퍼입니다.

import * as stylex from "@stylexjs/stylex";
import type { PropsWithChildren } from "react";

const styles = stylex.create({
    root: {
        display: "flex",
        flexDirection: "column",
    },
    spacing: (value) => ({
        rowGap: value * 16,
    }),
});

export function Stack({
    spacing = 0,
    children,
}: PropsWithChildren<{ spacing?: number }>) {
    return (
        <div {...stylex.props(styles.root, styles.spacing(spacing))}>
            {children}
        </div>
    );
}
로그인 후 복사
로그인 후 복사

우리는 구성요소의 스타일과 루트 요소만 정의합니다.

이 경우 우리가 함께 배치하는 유일한 것은 스타일 블록이라고 말할 수도 있습니다. HTML은 CSS 클래스 참조를 보유하는 데만 사용되며 상호작용이나 비즈니스는 없기 때문입니다. 논리.

유연성의 (피할 수 있는) 비용

이제 루트 요소를 섹션으로 렌더링하고 일부 속성을 추가하려면 어떻게 해야 할까요? 우리는 다형성 구성 요소의 영역으로 들어가야 합니다. React와 TypeScript에서는 다음과 같이 될 수 있습니다.

import * as stylex from "@stylexjs/stylex";
import { useState } from "react";

// styles
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: "#000",
    },
});

export function Toggle() {
    // interactions
    const [toggle, setToggle] = useState(false);
    const onClick = () => setToggle((t) => !t);

    // markup
    return (
        <button {...stylex.props(styles.base)} type="button" onClick={onClick}>
            {toggle}
        </button>
    );
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

제 생각에는 이 내용이 언뜻 보기에는 읽기 쉽지 않습니다. 그리고 기억하세요: 우리는 3개의 CSS 선언으로 요소를 렌더링하고 있습니다.

기본으로 돌아가기

얼마 전 Angular에서 애완동물 프로젝트를 진행하고 있었습니다. 구성 요소를 생각하는 데 익숙해진 저는 구성 요소에 접근하여 스택을 만들었습니다. Angular에서는 다형성 구성요소를 생성하는 것이 훨씬 더 복잡하다는 것이 밝혀졌습니다.

구현 설계에 의문이 들기 시작했고, 깨달음을 얻었습니다. 솔루션이 바로 내 앞에 있었는데 왜 복잡한 구현에 시간과 코드 줄을 소비해야 할까요?

<script>
    let toggle = $state(false)
    const onclick = () => toggle = !toggle
</script>

<button type='button' {onclick}>
    {toggle}
</button>

<style>
button {
    font-size: 16px;
    line-height: 1.5;
    color: #000;
}
</style> 
로그인 후 복사
로그인 후 복사

실제로 이것이 Stack의 기본 네이티브 구현입니다. 레이아웃에 CSS를 로드하면 코드에서 바로 사용할 수 있습니다.

export function Page() {
    return (
        <Layout>
            <Header nav={<Nav />} />
            <Body>
                <Stack spacing={2}>
                    <Item>Item 1</Item>
                    <Item>Item 2</Item>
                    <Item>Item 3</Item>
                </Stack>
            </Body>
            <Footer />
        </Layout>
    );
}
로그인 후 복사
로그인 후 복사

JavaScript 프레임워크에 유형 안전성 추가

CSS 전용 솔루션은 입력이나 IDE 자동 완성 기능을 제공하지 않습니다.

또한 간격 변형을 사용하지 않는 경우 간격 소품 대신 클래스와 스타일 속성을 모두 작성하는 것이 너무 장황하게 느껴질 수 있습니다. React를 사용한다고 가정하면 JSX를 활용하여 유틸리티 함수를 만들 수 있습니다.

import * as stylex from "@stylexjs/stylex";
import type { PropsWithChildren } from "react";

const styles = stylex.create({
    root: {
        display: "flex",
        flexDirection: "column",
    },
    spacing: (value) => ({
        rowGap: value * 16,
    }),
});

export function Stack({
    spacing = 0,
    children,
}: PropsWithChildren<{ spacing?: number }>) {
    return (
        <div {...stylex.props(styles.root, styles.spacing(spacing))}>
            {children}
        </div>
    );
}
로그인 후 복사
로그인 후 복사

React TypeScript는 알 수 없는 CSS 속성을 허용하지 않습니다. 간결성을 위해 유형 어설션을 사용했지만 더 강력한 솔루션을 선택해야 합니다.

변형을 사용하는 경우 유틸리티 기능을 수정하여 PandaCSS 패턴과 유사한 개발자 경험을 제공할 수 있습니다.

import * as stylex from "@stylexjs/stylex";

type PolymorphicComponentProps<T extends React.ElementType> = {
    as?: T;
    children?: React.ReactNode;
    spacing?: number;
} & React.ComponentPropsWithoutRef<T>;

const styles = stylex.create({
    root: {
        display: "flex",
        flexDirection: "column",
    },
    spacing: (value) => ({
        rowGap: value * 16,
    }),
});

export function Stack<T extends React.ElementType = "div">({
    as,
    spacing = 1,
    children,
    ...props
}: PolymorphicComponentProps<T>) {
    const Component = as || "div";
    return (
        <Component
            {...props}
            {...stylex.props(styles.root, styles.spacing(spacing))}
        >
            {children}
        </Component>
    );
}
로그인 후 복사

코드 중복 및 하드코딩된 값 방지

여러분 중 일부는 마지막 예에서 CSS와 유틸리티 파일 모두에 예상되는 공백 값을 하드코딩했다는 사실을 눈치채셨을 것입니다. 값이 제거되거나 추가되면 두 파일을 동기화 상태로 유지해야 하기 때문에 문제가 될 수 있습니다.

라이브러리를 구축하는 경우 자동화된 시각적 회귀 테스트를 통해 이러한 문제를 포착할 수 있습니다. 어쨌든 여전히 귀찮다면 CSS 모듈을 찾아서 typed-css-modules를 사용하거나 지원되지 않는 값에 대해 런타임 오류를 발생시키는 것이 해결책일 수 있습니다.

<div>





<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
    display: flex;
    flex-direction: column;
    row-gap: calc(var(--s) * 16px);
}
로그인 후 복사
export function Page() {
    return (
        <Layout>
            <Header nav={<Nav />} />
            <Body>
                <div className="stack">



<p>Let's see the main advantages of this approach:</p>

<ul>
<li>reusability</li>
<li>reduced complexity</li>
<li>smaller JavaScript bundle and less overhead</li>
<li><strong>interoperability</strong></li>
</ul>

<p>The last point is easy to overlook: Not every project uses React, and if you’re including the stack layout pattern in a Design System or a redistributable UI library, developers could use it in projects using different UI frameworks or a server-side language like PHP or Ruby.</p>

<h2>
  
  
  Nice features and improvements
</h2>

<p>From this base, you can iterate to add more features and improve the developer experience. While some of the following examples target React specifically, they can be easily adapted to other frameworks.</p>

<h3>
  
  
  Control spacing
</h3>

<p>If you're developing a component library you definitely want to define a set of pre-defined spacing variants to make space more consistent. This approach also eliminates the need to explicitly write the style attribute:<br>
</p>

<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
  display: flex;
  flex-direction: column;
  row-gap: calc(var(--s) * 16px);

  &.s\:1 { --s: 1 }
  &.s\:2 { --s: 2 }
  &.s\:4 { --s: 4 }
  &.s\:6 { --s: 6 }
}

/** Usage:
<div>



<p>For a bolder approach to spacing, see Complementary Space by Donnie D'Amato.</p>

<h3>
  
  
  Add better scoping
</h3>

<p>Scoping, in this case, refers to techniques to prevent conflicts with other styles using the same selector. I’d argue that scoping issues affects a pretty small number of projects, but if you are really concerned about it, you could:</p>

<ol>
<li>Use something as simple as CSS Modules, which is well supported in all major bundlers and frontend frameworks.</li>
<li>Use cascade layers resets to prevent external stylesheets from modifying your styles (this is an interesting technique).</li>
<li>Define a specific namespace like .my-app-... for your classes.</li>
</ol>

<p>Here is the result with CSS Modules:<br>
</p>

<pre class="brush:php;toolbar:false">.stack {
  --s: 0;
  display: flex;
  flex-direction: column;
  row-gap: calc(var(--s) * 16px);

  &.s1 { --s: 1 }
  &.s2 { --s: 2 }
  &.s4 { --s: 4 }
  &.s6 { --s: 6 }
}

/** Usage
import * from './styles/stack.module.css'

<div className={`${styles.stack} ${styles.s2}`}>
    // ...
</div>  
*/
로그인 후 복사

대안

여전히 다형성 구성 요소가 더 낫다고 생각하거나 일반 HTML을 실제로 처리할 수 없거나 CSS를 별도의 파일에 작성하고 싶지 않다면(이유는 모르겠지만) 다음 제안은 다음과 같습니다. PandaCSS를 살펴보고 사용자 정의 패턴을 만들거나 바닐라 추출과 같은 다른 옵션을 살펴보세요. 제 생각에는 이러한 도구는 과도하게 설계된 CSS 메타언어이지만 다형성 구성 요소보다 여전히 좋습니다.

고려해볼 만한 또 다른 대안은 언어와 프레임워크 간 상호 운용이 가능하다는 장점을 갖춘 Tailwind CSS입니다.

Tailwind에서 정의한 기본 간격 배율을 사용하여 다음과 같은 스택 플러그인을 만들 수 있습니다.

import * as stylex from "@stylexjs/stylex";
import { useState } from "react";

// styles
const styles = stylex.create({
    base: {
        fontSize: 16,
        lineHeight: 1.5,
        color: "#000",
    },
});

export function Toggle() {
    // interactions
    const [toggle, setToggle] = useState(false);
    const onClick = () => setToggle((t) => !t);

    // markup
    return (
        <button {...stylex.props(styles.base)} type="button" onClick={onClick}>
            {toggle}
        </button>
    );
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

참고로 Tailwind가 실제 구성 요소를 생성하지 않더라도 복잡한 CSS 규칙 세트를 설명하기 위해 matchComponents의 구성 요소 정신 모델을 사용한다는 점이 흥미롭습니다. 개념이 얼마나 널리 퍼져 있는지 보여주는 또 다른 예일까요?

테이크아웃

구성요소염의 사례는 기술적인 측면을 넘어 우리의 정신적 모델과 습관을 조사하고 질문하는 것이 얼마나 중요한지 보여줍니다. 소프트웨어 개발의 많은 패턴과 마찬가지로 구성 요소는 실제 문제에 대한 솔루션으로 등장했지만 이 패턴을 기본값으로 사용하기 시작하자 소리 없는 복잡성의 원인이 되었습니다. 성분염은 제한된 식단으로 인한 영양 결핍과 유사합니다. 문제는 특정 음식에만 있는 것이 아니라 다른 모든 음식을 놓치는 데 있습니다.

위 내용은 모든 것에 구성요소가 필요한 것은 아닙니다의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿