Vue 컴포넌트에서 로직 재사용하기

업데이트: Link

Vue 컴포넌트에서 로직 재사용하기

Reusing Logic in Vue Components Written by Alex Jover Morales

어느 날 Vue 애플리케이션을 만들기 시작합니다. 애플리케이션의 다른 부분을 구성하기 위해 컴포넌트를 만들기 시작합니다. 멋지네요. Vue와 웹 컴포넌트 아키텍처의 훌륭한 개발 경험을 느낄 수 있을 것입니다. 프로젝트를 진행하면서 아마도 페이지와 컴포넌트별로 응용 프로그램 컴포넌트를 구조화하기 시작합니다.

그러나 프로젝트가 계속 성장함에 따라 여러 컴포넌트에서 반복된다는 사실을 알기 시작합니다. Don’t Repeat Yourself(Dry) 및 Keep It Simple, Stupid(KISS)에 대해 들어왔습니다. 사용과 유지관리가 쉬운 소프트웨어를 작성할 수 있는 두 가지 원칙입니다.

이러한 원칙을 따르는 데 도움이 되는 몇 가지 패턴, 라이브러리 및 기술을 이미 알고 있을 수도 있습니다. Vuex는 컴포넌트에서 상태 논리를 가져오는 데 도움이 되며 Vue 라우터는 라우팅 논리에 대해 동일한 작업을 수행하지만 컴포넌트는 어떻습니까?

컴포넌트에 속한 일부 UI 기능을 재사용해야 하는 경우가 종종 있습니다. 예를 들어, Popover와 Tooltip은 특정 이벤트가 트리거될 때 열고 닫는 기능을 공유할 수 있을 뿐만 아니라 요소에 고정되고 배치됩니다.

이 기사에서는 스크롤 이벤트를 수신하고 스크롤 위치에 따라 색상을 변경하는 Colorful.vue 컴포넌트의 예를 사용할 것입니다. 창 높이보다 높으면 파란색, 그렇지 않으면 빨간색이 됩니다. 이는 :style을 사용하여 color 로컬 상태 변수를 바인딩하여 달성됩니다.

<!-- Colorful.vue -->
<template>
  <div :style="{ background: color }">
  </div>
</template>

<script>
export default {
  data: () => ({
    color: "red"
  }),
  methods: {
    handleScrollChange(status) {
      this.color = status === "in" ? "red" : "blue";
    },
    handleScroll() {
      if (window.scrollY > window.innerHeight) {
        this.handleScrollChange("out");
      } else {
        this.handleScrollChange("in");
      }
    }
  },
  mounted() {
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.handleScroll);
  }
};
</script>

컴포넌트에 잘못된 것이 없을 수도 있습니다. mounted 후크에 scroll 이벤트를 등록하고 destroyed에 등록을 취소합니다. 스크롤 위치 확인을 수행하는 handleScroll 메소드를 호출하고 status 인수에 따라 색상을 변경하기 위해 handleScrollChange 메소드를 호출합니다.

현재 스크롤 기능은 Colorful 구성요소 안에 있습니다. 그러나 mounteddestroyed 후크와 handleScroll 메서드를 제거하여 다른 컴포넌트에서 재사용할 수 있습니다.

이를 수행하는 다양한 방법을 살펴보겠습니다.

Component 상속

먼저 스크롤 관련 동작을 자체 컴포넌트 Scroll.js로 이동하여 시작하겠습니다.

export default {
  methods: {
    handleScroll() {
      if (window.scrollY > window.innerHeight) {
        this.handleScrollChange("out");
      } else {
        this.handleScrollChange("in");
      }
    }
  },
  mounted() {
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.handleScroll);
  }
};

보시다시피 이 Scroll 컴포넌트는 handleScrollChange가 존재하기를 기대합니다. 이 컴포넌트는 자식 컴포넌트에서 구현합니다. 즉, 이 컴포넌트는 확장되어야 하며 자체적으로 작동할 수 없습니다.

JavaScript만 포함하기 때문에 .js 파일에 작성할 수 있지만 필요한 경우 .vue 파일도 될 수 있습니다. .vue 파일의 JavaScript 부분만 상속된다는 점에 유의하십시오.

그런 다음 Colorful 컴포넌트에서 이동한 컴포넌트 동작을 모두 제거하고 extens 컴포넌트 옵션을 사용하여 Scroll 파일을 가져옵니다.

<!-- Colorful.vue -->
<template>
  <div :style="{ background: color }"></div>
</template>

<script>
import Scroll from "./scroll";
export default {
  extends: Scroll,
  data: () => ({
    color: "red"
  }),
  methods: {
    handleScrollChange(status) {
      this.color = status === "in" ? "red" : "blue";
    }
  }
};
</script>

컴포넌트 확장은 클래스 상속이 아니라는 점에 유의하십시오. 이 경우 Vue는 부모 및 자식 컴포넌트 옵션을 모두 병합하여 새로운 혼합 개체를 만듭니다.

예를 들어, 이전 예제에서는 다음 API를 사용하는 컴포넌트를 갖게 됩니다.

{
  data: () => ({
    color: "red"
  }),
  methods: {
    handleScrollChange(status),
    handleScroll,
  },
  mounted,
  destroyed  
}

mounteddestroyed 후크의 경우 부모와 자식 모두 유지되며 부모에서 자식으로 상속 순서대로 호출됩니다.

Mixins

컴포넌트 상속과 유사하게 컴포넌트 논리를 공유하기 위해 믹스인을 사용할 수 있습니다. 그러나 이전 예제에서는 하나의 컴포넌트에서만 상속할 수 있지만 믹스인을 사용하면 여러 컴포넌트의 기능을 결합할 수 있습니다.

사실 이전 예제의 Scroll.js 파일에서 아무것도 변경할 필요가 없습니다. Colorful 컴포넌트에서 extends 대신 mixins 옵션을 사용하는 것으로 충분합니다. mixins는 배열을 예상한다는 점을 기억하세요.

<!-- Colorful.vue -->
<script>
import Scroll from "./scroll";

export default {
  mixins: [Scroll],
  data: () => ({
    color: "red"
  }),
  methods: {
    handleScrollChange(status) {
      this.color = status === "in" ? "red" : "blue";
    }
  }
};
</script>

컴포넌트 상속과의 주요 차이점은 순서가 다르다는 것입니다. 컴포넌트 상속에서는 자식의 후크가 부모보다 먼저 실행되고 믹스인에서는 컴포넌트가 사용하기 전에 후크가 실행됩니다.

또한 믹스인은 템플릿이나 스타일 태그를 가질 수 없으며 그냥 평범한 JavaScript입니다.

재사용 가능한 Component 작성

상속과 믹스인은 구현하기 쉬운 것처럼 보이지만 어떻게든 암시적입니다. 앞의 예에서 Scroll을 사용할 때 handleScrollChange 메소드를 구현해야 한다는 것을 알아야 합니다.

이 경우는 그렇게 나쁘지는 않지만 여러 믹스인을 확장하거나 사용할 때 상황이 혼란스러워지기 시작하고 많은 부분에서 기능을 추적하기 시작할 수 있습니다.

기능을 재사용하는 또 다른 방법은 props를 수신하고 이벤트를 내보내는 재사용 가능한 컴포넌트를 만드는 것입니다. 이렇게 하면 마법 병합도 없고 컨텍스트를 공유하지도 않는 보다 명시적인 솔루션이 됩니다. 또한 이 솔루션은 다른 컴포넌트 기반 기술에도 동일한 접근 방식을 적용할 수 있으므로 보다 “보편적” 입니다.

먼저 Scroll.vue 컴포넌트로 만들어야 합니다.

<!-- Scroll.vue -->
<template>
  <div></div>
</template>

<script>
export default {
  methods: {
    handleScroll() {
      if (window.scrollY > window.innerHeight) {
        this.$emit("scrollChange", "out");
      } else {
        this.$emit("scrollChange", "in");
      }
    }
  },
  mounted() {
    window.addEventListener("scroll", this.handleScroll);
  },
  destroyed() {
    window.removeEventListener("scroll", this.handleScroll);
  }
};
</script>

이 경우, 매직 메소드 handleScrollChange를 호출하는 대신 상위 컴포넌트가 작업을 수행하는 데 사용할 수 있는 scrollChange 이벤트를 방출합니다.

그런 다음 Colorful.vue에서 컴포넌트로 가져와서 scrollChange 이벤트를 처리해야 합니다.

<!-- Colorful.vue -->
<template>
  <scroll
    @scrollChange="handleScrollChange"
    :style="{ background: color }">
  </scroll>
</template>

<script>
import Scroll from "./scroll";

export default {
  components: {
    Scroll
  },
  data: () => ({
    color: "red"
  }),
  methods: {
    handleScrollChange(status) {
      this.color = status === "in" ? "red" : "blue";
    }
  }
};
</script>

div 태그를 scroll로 교체했지만 Colorful 컴포넌트 로직은 그대로 유지됩니다.

마무리

스크롤 기능을 제거하고 재사용하는 세 가지 방법을 보았습니다. 귀하의 경우에 따라 하나 또는 다른 것을 사용할 것입니다.

컴포넌트 상속 및 믹스인은 컴포넌트 논리의 일부를 마술처럼 분리하고 함께 병합하는 방법을 제공합니다. 이는 너무 혼동되지 않는 한 어떤 경우에 적합할 수 있습니다. 특히 Mixin은 여러 개를 결합할 수 있으므로 더 강력합니다.

컴포넌트 구성을 사용하는 것이 더 명확하고 명확한 솔루션입니다. 다른 컴포넌트 기반 프레임워크에서도 동일한 기술을 사용할 수 있습니다. 그러나 어떤 경우에는 mixin만큼 편리하지 않을 수 있습니다.

Codesandbox에서 데모를 볼 수 있습니다. 여기서 index.js 파일의 예제를 변경하여 세 가지를 시도할 수 있습니다.

댓글남기기