재사용 가능한 UI 구성 요소를 만들기위한 팁과 요령

Farzard Nazifi의 여행자 사진

이 기사에서는 Ember.js를 사용하여 핵심 프론트 엔드 라이브러리를 구축 할 때 사용하는 몇 가지 팁과 요령을 공유하고자합니다. 전에는 연락이 없었기 때문에 훌륭한 학습 기회였습니다. 나는 당신이 그것을 즐기시기 바랍니다! 기사의 아이디어를 설명하는 데 사용되는 코드에는 요점을 파악하기에 충분한 정보 만 포함되어 있습니다. 또한 Ember.js 용어를 사용하지만 개념은 프레임 워크에 구애받지 않습니다.

목표

간단히 말해 라이브러리를 구축하기위한 요구 사항은 다음과 같습니다.

  1. 생산성이 높아야합니다.
  2. 유지 보수가 가능해야합니다.
  3. 일관성이 있어야합니다.

접근 방식

비즈니스 로직 최소화

프로젝트에서 가장 자주 발생하는 문제 중 하나는 너무 많은 논리를 포함하는 구성 요소입니다. 따라서 이론적으로 범위를 벗어난 작업을 수행합니다.

기능을 구현하기 전에 구성 요소가 담당하는 일부 의무를 설명하는 것이 좋습니다.

버튼 구성 요소를 구축한다고 가정합니다.

나는 할 수 있기를 원합니다 :

  • 기본 또는 일반 버튼 유형을 알려줍니다.
  • 버튼 안에 표시되는 내용을 알리십시오 (아이콘 및 텍스트)
  • 버튼 비활성화 또는 활성화
  • 클릭시 조치 수행

이 작은 외곽선을 가짐으로써이 구성 요소를 만드는 과정과 관련된 다른 부분을 분리하십시오. 물건을 놓을 수있는 곳을 확인하십시오.

1 — 유형과 내용은 구성 요소별로 다르므로 구성 요소 파일에 배치 할 수 있습니다.

유형이 어느 정도 필요하므로 값이 제공되지 않은 경우 확인을 추가하겠습니다.

const type = get (this, 'type');
const 유형 = {
  기본 : 'btn-- 기본',
  일반 : 'btn-- 일반',
}
반환 (유형)? types [type] : types.regular;

위험 버튼이나 그와 비슷한 것이 필요할 경우 많은 노력을 기울이지 않고 확장 할 수 있기 때문에 속성을 객체에 매핑하는 것이 좋습니다.

2 — 비활성화 된 상태는 입력과 같은 다른 구성 요소에서 찾을 수 있습니다. 반복을 피하기 위해이 동작을 모듈 또는 공유 구조로 옮길 수 있습니다.이를 믹스 인이라고합니다.

3 — 클릭 동작은 다른 구성 요소에서 찾을 수 있습니다. 따라서 다른 파일로 이동할 수 있으며 그 안에는 로직이 없어야합니다. 개발자가 제공 한 콜백을 호출하면됩니다.

이러한 방식으로 확장을 지원하는 기본 아키텍처를 개괄하면서 구성 요소가 해결해야 할 사례를 파악할 수 있습니다.

별도의 재사용 가능한 UI 상태

특정 UI 상호 작용은 다음과 같은 여러 구성 요소에서 공통입니다.

  • 활성화 / 비활성화 – 예. 버튼, 입력
  • 확장 / 축소 – 예. 축소, 드롭 다운 목록
  • 표시 / 숨기기 — 거의 모든 것

이러한 속성은 종종 시각적 상태를 제어하는 ​​데 사용됩니다.

여러 구성 요소에 걸쳐 일관된 명명법을 유지하십시오. 시각적 상태와 관련된 모든 작업을 믹스 인으로 이동할 수 있습니다.

/ * UIStateMixin * /
disable () {
  set (this,‘disabled’, true);
  이것을 돌려줍니다;
},
enable () {
  set (this, 'disabled', false ');
  이것을 돌려줍니다;
},

각 메소드는 특정 변수 토글에만 책임이 있으며 다음과 같이 체인에 대한 현재 컨텍스트를 리턴합니다.

단추
  .disable ()
  .showLoadingIndicator ();

이 접근 방식은 확장 될 수 있습니다. 내부 컨텍스트를 사용하는 대신 다른 컨텍스트를 허용하고 외부 변수를 제어 할 수 있습니다. 예를 들면 다음과 같습니다.

_getCurrentDisabledAttr () {
  반환 (isPresent (get (this, 'disabled')))
    ? 'disabled'/ * 외부 매개 변수 * /
    : 'isDisabled'; / * 내부 변수 * /
},
사용 (문맥) {
  set (context || this, this._getCurrentDisabledAttr (), false);
  이것을 돌려줍니다;
}

기본 기능 추상화

모든 구성 요소에는 특정 루틴이 포함되어 있습니다. 이러한 루틴은 구성 요소의 목적에 관계없이 수행해야합니다. 예를 들어, 콜백을 트리거하기 전에 확인하십시오.

이러한 기본 방법은 다음과 같이 자체 믹스 인으로 이동할 수도 있습니다.

/ * BaseComponentMixin * /
_isCallbackValid (callbackName) {
  const 콜백 = get (this, callbackName);
  
  return !! (isPresent (콜백) && typeof 콜백 === 'function');
},
_handleCallback (콜백, 매개 변수) {
  if (! this._isCallbackValid (callback)) {
    새로운 에러를 던진다 (/ * message * /);
  }
  this.sendAction (콜백, 매개 변수);
},

그런 다음 구성 요소에 포함됩니다.

/ * 구성 요소 * /
onClick (params) {
  this._handleCallback ( 'onClick', params);
}

이렇게하면 기본 아키텍처가 일관되게 유지됩니다. 또한 확장 및 타사 소프트웨어와의 통합도 가능합니다. 하지만 철학적으로 추상화하지 마십시오.

구성 요소

기능을 최대한 많이 다시 작성하지 마십시오. 전문화가 가능합니다. 구성 및 그룹화를 통해 수행 할 수 있습니다. 새로운 구성 요소를 만들기 위해 더 작은 구성 요소를 함께 조정할 수 있습니다.

예를 들면 다음과 같습니다.

기본 구성 요소 : 버튼, 드롭 다운, 입력.
드롭 다운 버튼 => 버튼 + 드롭 다운
자동 완성 => 입력 + 드롭 다운
선택 => 입력 (읽기 전용) + 드롭 다운

이러한 방식으로 각 구성 요소마다 고유 한 의무가 있습니다. 래퍼 구성 요소는 특정 논리를 처리하는 반면 각각은 자체 상태와 매개 변수를 처리합니다.

최고의 관심사 분리.

관심사 분리

보다 복잡한 구성 요소를 구성 할 때 우려 사항이 분리 될 수 있습니다. 구성 요소의 여러 부분으로 문제를 분할 할 수 있습니다

선택 컴포넌트를 구축한다고 가정 해 보겠습니다.

{{form-select binding = productId items = items}}
아이템 = [
  {설명 : '제품 # 1', 값 : 1},
  {설명 : '제품 # 2', 값 : 2}
]

내부적으로 간단한 입력 구성 요소와 드롭 다운이 있습니다.

{{form-input binding = _description}}
{{ui-dropdown items = items onSelect = (액션 'selectItem')}}

우리의 주요 임무는 설명을 사용자에게 제시하는 것이지만, 우리의 응용 프로그램에는 의미가 없습니다.

옵션을 선택할 때 개체를 분할하여 내부 변수를 통해 입력에 설명을 보내면서 값을 컨트롤러로 올리면서 바인딩 된 변수를 업데이트합니다.

이 개념은 숫자, 자동 완성 또는 선택 필드와 같이 바인딩 된 값을 변환해야하는 구성 요소에 적용 할 수 있습니다. 날짜 선택 도구도이 동작을 구현할 수 있습니다. 마스크 된 값을 사용자에게 제시하면서 바인딩 된 변수를 업데이트하기 전에 날짜를 마스크 해제 할 수 있습니다.

변환이 복잡 해짐에 따라 위험이 높아집니다. 과도한 논리 나 이벤트 지원이 필요하므로이 방법을 구현하기 전에이를 숙지하십시오.

프리셋과 새로운 컴포넌트

때로는 개발을 촉진하기 위해 구성 요소와 서비스를 최적화해야합니다. 이들은 사전 설정 또는 새 구성 요소 형태로 제공됩니다.

사전 설정은 매개 변수입니다. 통보를 받으면 구성 요소에 사전 정의 된 값을 설정하여 선언을 단순화합니다. 그러나 새 구성 요소는 일반적으로 더 전문화 된 기본 구성 요소 버전입니다.

어려운 부분은 사전 설정을 구현하거나 새 구성 요소를 생성 할시기를 아는 것입니다. 이 결정을 할 때 다음 지침을 사용합니다.

사전 설정을 만드는시기

1 — 반복적 인 사용 패턴

특정 구성 요소가 동일한 매개 변수를 사용하여 여러 위치에서 재사용되는 경우가 있습니다. 이 경우, 특히 기본 구성 요소에 매개 변수 수가 너무 많은 경우 새 구성 요소보다 사전 설정을 선호합니다.

/ * 정기 구현 * /
{{양식 자동 완성
    binding = productId
    url = "products"/ * 가져올 URL * /
    labelAttr = "description"/ * 레이블로 사용되는 속성 * /
    valueAttr = "id"/ * 값으로 사용되는 속성 * /
    apiAttr = "product"/ * 요청시 전송되는 Param * /
}}
/ * 사전 설정 * /
{{양식 자동 완성
    preset = "product"
    binding = productId
}}

사전 설정의 값은 매개 변수에 알리지 않은 경우에만 설정되어 유연성을 유지합니다.

/ * 사전 설정 모듈의 순진한 구현 * /
const 프리셋 = {
  제품 : {
    url :‘제품’,
    labelAttr :‘설명’,
    valueAttr :‘id’,
    apiAttr :‘제품’,
  },
}
const attrs = presets [get (this,‘사전 설정’)];
Object.keys (attrs) .forEach ((prop) => {
  if (! get (this, prop)) {
    set (this, prop, attrs [prop]);
  }
});

이 접근 방식은 구성 요소를 사용자 정의하는 데 필요한 지식을 줄입니다. 동시에 한 곳에서 기본값을 업데이트 할 수있어 유지 관리가 용이합니다.

2 — 기본 구성 요소가 너무 복잡합니다

보다 구체적인 구성 요소를 만드는 데 사용하는 기본 구성 요소가 너무 많은 매개 변수를 허용하는 경우 따라서 작성하면 일부 문제점이 발생합니다. 예를 들면 다음과 같습니다.

  • 새 구성 요소에서 기본 구성 요소로 매개 변수를 전부 (전부는 아님) 주입해야합니다. 점점 더 많은 구성 요소가 구성 요소에서 파생 될 때 기본 구성 요소의 모든 업데이트에는 많은 양의 변경 사항이 반영됩니다. 따라서 버그 발생률이 높아집니다.
  • 더 많은 컴포넌트가 생성 될수록 다른 뉘앙스를 문서화하고 기억하기가 더 어려워집니다. 이것은 특히 새로운 개발자에게 해당됩니다.

새 구성 요소를 작성하는시기

1 — 기능 확장

더 간단한 구성 요소에서 기능을 확장 할 때 새 구성 요소를 작성할 수 있습니다. 구성 요소 별 논리가 다른 구성 요소로 누출되는 것을 방지합니다. 추가 동작을 구현할 때 특히 유용합니다.

/ * 선언 * /
{{ui-button-dropdown items = items}}
/* 후드 */
{{# ui-button onClick = (action 'toggleDropdown')}}
  {{label}}  
{{/ ui-button}}
{{#if isExpanded}}
  {{ui-dropdown items = items}}
{{/만약}}

위의 예는 버튼 구성 요소를 사용합니다. 드롭 다운 구성 요소 및 표시 상태를 포함하면서 고정 아이콘을 지원하도록 레이아웃을 확장합니다.

2 — 매개 변수 꾸미기

새 구성 요소를 작성해야하는 또 다른 이유가 있습니다. 이것은 파라미터 가용성을 제어하거나 기본값을 장식해야 할 때입니다.

/ * 선언 * /
{{form-datepicker onFocus = (액션 'doSomething')}}
/* 후드 */
{{form-input onFocus = (액션 '_onFocus')}}
_onFocus () {
  $ (this.element)
    .find ( 'input')
    .고르다(); / * 초점에서 필드 값 선택 * /
  this._handleCallback ( 'onFocus'); / * param 콜백 트리거 * /
}

이 예에서는 필드에 초점을 맞출 때 호출되는 기능을 구성 요소에 제공했습니다.

내부적으로 콜백을 기본 구성 요소에 바로 전달하는 대신 내부 함수를 전달합니다. 특정 작업 (필드 값 선택)을 수행 한 다음 제공된 콜백을 호출합니다.

기본 입력 구성 요소가 승인 한 모든 매개 변수를 경로 재지 정하지는 않습니다. 이는 특정 기능의 범위를 제어하는 ​​데 도움이됩니다. 또한 불필요한 유효성 검사를 피합니다.

필자의 경우 onBlur 이벤트가 다른 이벤트 인 onChange로 대체되었습니다. 사용자가 필드를 채우거나 달력에서 날짜를 선택하면 트리거됩니다.

결론

구성 요소를 만들 때는 일상 생활에서 해당 구성 요소를 사용하는 사람뿐만 아니라 자신의 측면도 고려하십시오. 이렇게하면 모두가 이깁니다.

최상의 결과는 그룹 내 모든 사람과 자신과 그룹을 위해 최선을 다하는 John Nash의 결과입니다.

또한 부끄러워하지 말고 피드백을 요청하십시오. 항상 작업 할 수있는 것을 찾을 수 있습니다.

소프트웨어 엔지니어링 기술을 더욱 향상시키기 위해 Eric Elliott의 "Composing Software"시리즈를 따르는 것이 좋습니다. 그것은 굉장!

글을 읽었 으면 좋겠다. 이 개념을 취하여 자신의 아이디어로 바꾸고 우리와 공유하십시오!

또한 트위터 @gcolombo_에서 저에게 연락하십시오! 당신의 의견을 듣고 함께 일하고 싶습니다.

감사!