Шинэ түүх

Vue.js: Props Like a Pro хуваалцах

by Andrei Sieedugin7m2025/05/03
Read on Terminal Reader

Хэтэрхий урт; Унших

Таны төслийн бүрэлдэхүүн хэсгүүд үүсгэх үед бүх зүйл нь тавтай морилно уу, хялбар байх болно. "MyButton.vue" үүсгэх, зарим хэлбэрээр нэмнэ үү. Дараа нь та дараа нь та нар нь өөр өөр өнгө, хэмжээтэй байхыг хүсч байгаа тул танд тавтай морилно уу "Anulate" болон "Ok" бутлуудыг нэг өнгөтэй байж чадахгүй бөгөөд та хэрэглэгчийн интеракцийг хариулахын тулд тэднийг хэрэгтэй.
featured image - Vue.js: Props Like a Pro хуваалцах
Andrei Sieedugin HackerNoon profile picture

Хэрэв та өөрийн төслийн бүрэлдэхүүн хэсэг үүсгэхийн тулд, энэ нь бүх нь тавтай морилно уу, хялбар эхэлнэ.MyButton.vueӨнгөрсөн хэлбэрийг нэмнэ үү, энд байна.

<template>
  <button class="my-fancy-style">
    <slot></slot>
  </button>
</template>


Дараа нь та зүгээр л танд мэднэ үү, та нар нь долоо хоногийн хэрэгцээтэй байх ёстой, учир нь таны дизайны баг өөр өнгө, хэмжээтэй байхыг хүсч байна, шууд, зөв иконүүдтэй, дугуйтай ...

const props = withDefaults(defineProps<{
  theme?: ComponentTheme;
  small?: boolean;
  icon?: IconSvg; // I’ve described how I cook icons in my previous article
  rightIcon?: IconSvg;
  counter?: number;
}>(), {
  theme: ComponentTheme.BLUE,
  icon: undefined,
  rightIcon: undefined,
  counter: undefined
});


Энэ нь хэзээ ч байтугай тохиромжтой байдаг; Энэ нь ач холбогдолтой юм. Эцэст нь, та "Anulate" болон "Ok" бутлуудыг нэг өнгөтэй байж чадахгүй, та хэрэглэгчийн интеракцийг хариулахын тулд тэдэнд хэрэгтэй.

const props = withDefaults(defineProps<{
  theme?: ComponentTheme;
  small?: boolean;
  icon?: IconSvg;
  rightIcon?: IconSvg;
  counter?: number;
  disabled?: boolean;
  loading?: boolean;
}>(), {
  theme: ComponentTheme.BLUE,
  icon: undefined,
  rightIcon: undefined,
  counter: undefined
});


Энд, та санаа олж авах болно: ямар ч байтугай байх болно, гэх мэтwidth: 100%эсвэл автофокус нэмэх - Бид бүгд Фигма-д харьцуулахад жинхэнэ амьдралд харьцуулахад энэ нь хялбар гэж мэддэг.


Одоо линк бутлуур гэж үзнэ үү: Энэ нь нэг ижил байна, гэхдээ та үүнийг дарна уу, Та гадаргын эсвэл дотоод линк руу явах ёстой. Та тэднийг хавтгайж болно<RouterLink>эсвэл<a>Тавтай морилно уу. Та бас нэмж болноtoНөхцөлhrefтаны эхний компонент нь зайлсхийх, гэхдээ та хурдан хөнгөн мэдэрч болно:

<component
  :is="to ? RouterLink : href ? 'a' : 'button'"
  <!-- ugh! -->


Ямар ч, танд таны бутлуургыг хамардаг "дөрвөн түвшин" компонент хэрэгтэй болно (Энэ нь стандарт хиперлинк ширхэг болон зарим бусад сонирхолтой зүйлсийг ажиллуулж болно, гэхдээ ялангуяа үүнийг хуваалцах болно):

<template>
  <component
    :is="props.to ? RouterLink : 'a'"
    :to="props.to"
    :href="props.href"
    class="my-link-button"
  >
    <MyButton v-bind="$attrs">
      <slot></slot>
    </MyButton>
  </component>
</template>

<script lang="ts" setup>
import MyButton from './MyButton.vue';
import { RouteLocationRaw, RouterLink } from 'vue-router';

const props = defineProps<{
  to?: RouteLocationRaw;
  href?: string;
}>();
</script>

Энэ нь бидний түүх эхэлж байна.

Square One

Зохиогчийн эрх

Энд, ихэвчлэн энэ нь ажиллах болно, би л биш болно. Та одоо ч бас хэвлэх болно<MyLinkButton :counter=“2">But there will be no autocomplete for the derived props, энэ нь хялбар биш юм:


Only "href" and "to"


Бид шууд Props хуваалцах боломжтой боловч IDE нь тэдний талаар ямар нэг зүйл мэддэггүй бөгөөд энэ нь хамаагүй юм.


Хязгаарлагдмал, ягаан туяаны шийдэл нь тэднийг ягаан туяар хуваалцах:

<template>
  <component
    :is="props.to ? RouterLink : 'a'"
    :to="props.to"
    :href="props.href"
    class="my-link-button"
  >
    <MyButton
      :theme="props.theme"
      :small="props.small"
      :icon="props.icon"
      :right-icon="props.rightIcon"
      :counter="props.counter"
      :disabled="props.disabled"
      :loading="props.loading"
    >
      <slot></slot>
    </MyButton>
  </component>
</template>

<script lang="ts" setup>
// imports...

const props = withDefaults(
  defineProps<{
    theme?: ComponentTheme;
    small?: boolean;
    icon?: IconSvg;
    rightIcon?: IconSvg;
    counter?: number;
    disabled?: boolean;
    loading?: boolean;

    to?: RouteLocationRaw;
    href?: string;
  }>(),
  {
    theme: ComponentTheme.BLUE,
    icon: undefined,
    rightIcon: undefined,
    counter: undefined,
  }
);
</script>


Энэ нь ажиллаж болно. IDE нь тохиромжтой автокомплете байх болно. Бид энэ нь дэмжихын тулд маш их болтугай байх болно.


Ямар ч, "Бид өөрсдийн дутагдалгүй" принцип энд хэрэглэдэг биш юм, энэ нь бид бүр шинэчлэлт синхрончлох хэрэгтэй гэсэн үг юм. Өнгөрсөн өдөр, та өөр нэг дэмжлэг нэмэх хэрэгтэй, та үндсэн нэг нь хавтгай дэх бүх компонент олох хэрэгтэй болно. Үнэндээ, Button болон LinkButton магадгүй хялбар байдаг, гэхдээ TextInput болон энэ дээр суурилсан бүрэлдэхүүн хэсгүүд: PasswordInput, EmailInput, NumberInput, DateInput, HellKnowsWhatElseInput.


Эцэст нь, энэ нь дуртай. Тэгээд бид илүү олон шивээстэй байх, энэ нь дуртай байх болно.

Clean It Up

Зөвлөгөө

Аноним хэлбэрээр өөрсдийн ашиглах нь маш хялбар юм, Тиймээс үүнийг нэрлэдэг гэж үзнэ үү.

// MyButton.props.ts

export interface MyButtonProps {
  theme?: ComponentTheme;
  small?: boolean;
  icon?: IconSvg;
  rightIcon?: IconSvg;
  counter?: number;
  disabled?: boolean;
  loading?: boolean;
}


Бид интерфейс нь экспортын чадахгүй байна.vueАрхивууд нь зарим интернетийнscript setupMagic, Тиймээс бид өөр өөр нэг үүсгэх хэрэгтэй.tsфайл. Хөнгөн хуудсыг үзнэ үү, бид энд байна уу:

const props = withDefaults(defineProps<MyButtonProps>(), {
  theme: ComponentTheme.BLUE,
  icon: undefined,
  rightIcon: undefined,
  counter: undefined,
});


илүү цэвэр, энэ нь юу вэ? Тэгээд энд эртний нэг юм:

interface MyLinkButtonProps {
  to?: RouteLocationRaw;
  href?: string;
}

const props = defineProps<MyButtonProps & MyLinkButtonProps>();


Гэсэн хэдий ч, энд нэг асуудал байдаг: Одоо, үндсэн хуваалцах ньMyLinkButton's props, тэдний хамт хуваалцах бишv-bind=”$attrs”Үүнээс гадна, бид үүнийг өөрсдийн хийх ёстой.

<!-- MyLinkButton.vue -->

<component
  :is="props.to ? RouterLink : 'a'"
  :to="props.to"
  :href="props.href"
  class="my-link-button"
>
  <MyButton v-bind="props"> <!-- there we go -->
    <slot></slot>
  </MyButton>
</component>


Бүх сайн байна, гэхдээ бид хүсэж байгаа хэдий ч илүү бага байна:


W3C disapproves


Хэрэв та харж болно, одоо бидний доорх товч нь басhrefатрибут. Энэ нь трагик биш, зүгээр л жижиг хатуу, нэмэлт байт, харин хялбар биш юм. Бид энэ нь очиж, бас.

<template>
  <component
    :is="props.to ? RouterLink : 'a'"
    :to="props.to"
    :href="props.href"
    class="my-link-button"
  >
    <MyButton v-bind="propsToPass">
      <slot></slot>
    </MyButton>
  </component>
</template>

<script lang="ts" setup>
// imports and definitions…

const props = defineProps<MyButtonProps & MyLinkButtonProps>();

const propsToPass = computed(() =>
  Object.fromEntries(
    Object.entries(props).filter(([key, _]) => !["to", "href"].includes(key))
  )
);
</script>


Одоо, бид зүгээр л дамжуулан байх ёстой зүйлсийг дамжуулдаг, гэхдээ эдгээр бүх string literals гайхамшигтай харж биш, тэд юу вэ? Тэгээд энэ нь найдвартай TypeScript түүх юм, хүмүүст. Би үүнийг хэлэх болно.

Interfaces vs Abstract Interfaces

Интерфэйс vs Абстракт интерфэйс

Хэрэв та хэзээ ч тохиромжтой Object-ориентирован хэлтэй ажиллаж байгаа бол, та найдвартай зүйлсийг мэднэ үүСэтгэгдэл, энэ нь бидний структурын талаарх метадандыг олж авах боломжийг олгодог. Үнэндээ, TypeScript-д интерфэйс эфимертэй байдаг; тэд Runtime-д байдаггүй бөгөөд бид хялбар хэлбэрийг хайж чадахгүйMyButtonProps.


Энэ нь бидний хоёр сонголт байдаг гэсэн үг юм. Эхний, бид зүйлсийг хэзээ ч байтугай хадгалаад болно: Бид ямар ч хувилбар нэмж байгаа үедMyLinkButton, бид бас энэ нь тусгаарлах хэрэгтэйpropsToPass(Хэдийгээр бид энэ тухай унших, энэ нь том зүйл биш юм).


Хоёр дахь арга нь интерфэйс бооцоо ашиглах юм. Энэ нь алдартай байж болох юм, гэхдээ би ямар нэг зүйл шифрлэдэг: Энэ нь гайхамшигтай биш болно; Би санал болгож байна.ЭнэҮнэгүй

// MyButton.props.ts

export const defaultMyButtonProps: MyButtonProps = {
  theme: ComponentTheme.BLUE,
  small: false,
  icon: undefined,
  rightIcon: undefined,
  counter: undefined,
  disabled: false,
  loading: false,
};


Үүнээс гадна, энэ нь объектыг үүсгэхийн тулд объектыг үүсгэхийн тулд алдартай биш юм, гэхдээ бид энэ нь суралцагчийн зориулалтаар ашиглаж болно.MyButton.vueЭнэ нь илүү цэвэршүүлэх болно:

const props = withDefaults(defineProps<MyButtonProps>(), defaultMyButtonProps);


Одоо бид зүгээр л шинэчлэх хэрэгтэйpropsToPassӨнгөрсөнMyLinkButton.vue:

const propsToPass = computed(() =>
  Object.fromEntries(
    Object.entries(props).filter(([key, _]) =>
      Object.hasOwn(defaultMyButtonProps, key)
    )
  )
);


Эдгээр үйл ажиллагаа хийхын тулд бид бүгд тодорхойлолт хийх хэрэгтэйundefinedНөхцөлnullБайгалийнdefaultMyButtonProps; Дараа нь, Object нь "HaveOwn" биш юм.


Хэрэв та үндсэн бүрэлдэхүүн хэсэгт зайлсхийхыг нэмж байгаа бол энэ нь суурилсан үнэтэй зүйлстэй нэмж хэрэгтэй. Тэгэхээр, да, энэ нь хоёр газар юм, энэ нь өмнөх хувилбар дахь шийдэлээс илүү сайн биш юм. Хэрэв та ямар нэг нь цэвэршүүлдэг гэж үзнэ үү.

I’m Done

Би хийх

Энэ нь гайхалтай зүйл биш юм, гэхдээ энэ нь TypeScript-ийн хязгаарлалыг хамардаг хамгийн сайн зүйл юм.


Энэ нь SFC файлын дотор prop төрөл нь илүү сайн байна гэж бодож байна, гэхдээ би тэднийг тусгай файлуудыг хуваалцах нь ихэнх хэзээ ч хэзээ ч хэзээ ч бодож байна гэж хэлж чадахгүй байна. Гэхдээ энэ нь definitively prop reusing better, Тиймээс би энэ нь ажил гэж нэрлэдэг хэзээ ч ялалт нь жижиг ялалт гэж үзнэ үү.


Та энэ өгүүллийг GitHub дээр олж болно.

GitHub нь
L O A D I N G
. . . comments & more!

About Author

Andrei Sieedugin HackerNoon profile picture
Andrei Sieedugin@smileek
Senior frontend developer with product management experience

TAG ҮҮ

ЭНЭ ӨГҮҮЛЛИЙГ ТОЛГОЙЛУУЛСАН...

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks
OSZAR »