По традиции делимся полезными переводными материалами во frontend-разработке. В этот раз frontend-специалист SimbirSoft Никита сделал выбор в пользу материалов Nwose Lotanna, опубликованных на сайте blog.logrocket.com. С разрешения автора мы перевели статью, в которой он рассказывать о способах использования пропсов для передачи данных дочерним компонентам в Vue 3.

Примечание автора: Данная статья последний раз была обновлена 15 декабря 2022, чтобы отразить обновления, добавленные в Vue 3.

Пропсы — это важная фича в Vue для управления родительскими и дочерними компонентами, однако взаимодействие с ними может быть довольно мудреным. В этой статье мы изучим, как передавать данные из родительского компонента в дочерний с использованием пропсов в Vue 3. Весь код из этой статьи вы можете найти на GitHub. Что ж, начнем!

Содержание:

  • Перед началом

  • Что такое пропсы в Vue?

  • Зачем мне использовать пропсы?

  • Определение данных в родительском компоненте

  • Получение пропсов в Vue

  • Регистрация пропсов в Vue

  • Использование пропсов в Vue

  • Строго типизированные пропсы

Перед началом

Эта статья предназначена для разработчиков любого уровня, включая новичков.

Чтобы отслеживать то, что будет происходить в этой статье, вам понадобится установленный Node.js версии не ниже 16.х. Вы можете проверить установленную версию в терминале, выполнив команду:

node -v

Кроме того, вам понадобится текстовый редактор. Мы настоятельно рекомендуем VS Code. И наконец, вам необходим глобально установленный Vue 3. На момент написания статьи, актуальной является третья версия.

Прежде чем начать, скачайте стартовый проект Vue. Разархивируйте скачанный проект, перейдите в папку проекта, а затем выполните команду ниже для установки всех зависимостей:

npm install

Что такое пропсы в Vue?

Пропс в Vue — это кастомный атрибут, который вы можете зарегистрировать в любом компоненте. Вы определяете данные в родительском компоненте и присваиваете им определенное значение. Затем переходите в дочерний компонент, которому эти данные необходимы, и передаете значение атрибуту props. В результате данные становятся свойством дочернего компонента.

В компоненте, реализованном на Composition API, то есть с использованием <script setup>, синтаксис будет выглядеть следующим образом:

<script setup>
const props = defineProps(['title'])

console.log(props.title)
</script>

Для компонентов, реализованных с помощью Options API:

export default {
  props: ['title'],
  setup(props) {
    // setup() принимает пропсы в качестве первого аргумента
    console.log(props.title)
  }
}

Чтобы получить доступ к этим данным динамически из любого компонента, которому они нужны, вы можете использовать корневой компонент App.vue как родительский, хранить в нем данные, а затем регистрировать пропсы.

Зачем мне использовать пропсы?

Давайте представим, что у вас в есть объект с данными, которые вы хотите по-разному отобразить в двух разных компонентах. К примеру, пусть это будет топ-10 лучших исполнителей. Вполне вероятно, что в первую очередь вы подумаете о создании двух отдельных компонентов, в каждом из которых будет массив объектов с исполнителями и их отображение с помощью блока <template>.

В данном случае это будет отличным решением, однако по мере масштабирования проекта и добавления все новых и новых компонентов, этот метод перестанет быть эффективным. Давайте убедимся в этом на примере стартового проекта, который вам нужно открыть с помощью VS Code. Откройте файл Test.vue и скопируйте в него код:

<template>
  <div>
    <h1>Vue Top 20 Artists</h1>
    <ul>
      <li v-for="(artist, x) in artists" :key="x">
      <h3>{{artist.name}}</h3>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Test',
  data (){
    return {
      artists: [
       {name: 'Davido', genre: 'afrobeats', country: 'Nigeria'},
       {name: 'Burna Boy', genre: 'afrobeats', country: 'Nigeria'},
       {name: 'AKA', genre: 'hiphop', country: 'South-Africa'},
       {name: 'Sarkodie', genre: 'hiphop', country: 'Ghana'},
       {name: 'Stormzy', genre: 'hiphop', country: 'United Kingdom'},
       {name: 'Lil Nas', genre: 'Country', country: 'United States'},
       {name: 'Nasty C', genre: 'hiphop', country: 'South-Africa'},
       {name: 'Shatta-walle', genre: 'Reagae', country: 'Ghana'},
       {name: 'Khalid', genre: 'pop', country: 'United States'},
       {name: 'ed-Sheeran', genre: 'pop', country: 'United Kingdom'}
      ]
    }
  }
}
</script>

Создайте новый файл Test2.vue в папке Components и скопируйте в него код ниже:

<template>
  <div>
    <h1>Vue Top Artist Countries</h1>
    <ul>
      <li v-for="(artist, x) in artists" :key="x">
      <h3>{{artist.name}} from {{artist.country}}</h3>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Test2',
  data (){
    return {
      artists: [
       {name: 'Davido', genre: 'afrobeats', country: 'Nigeria'},
       {name: 'Burna Boy', genre: 'afrobeats', country: 'Nigeria'},
       {name: 'AKA', genre: 'hiphop', country: 'South-Africa'},
       {name: 'Sarkodie', genre: 'hiphop', country: 'Ghana'},
       {name: 'Stormzy', genre: 'hiphop', country: 'United Kingdom'},
       {name: 'Lil Nas', genre: 'Country', country: 'United States'},
       {name: 'Nasty C', genre: 'hiphop', country: 'South-Africa'},
       {name: 'Shatta-walle', genre: 'Reagae', country: 'Ghana'},
       {name: 'Khalid', genre: 'pop', country: 'United States'},
       {name: 'ed-Sheeran', genre: 'pop', country: 'United Kingdom'}
      ]
    }
  }
}
</script>
<style scoped>
li{
    height: 40px;
    width: 100%;
    padding: 15px;
    border: 1px solid saddlebrown;
    display: flex;
    justify-content: center;
    align-items: center;
  }  
a {
  color: #42b983;
}
</style>

Чтобы зарегистрировать новый компонент, который вы только что создали, откройте файл App.vue и скопируйте в него код:

<template>
  &lt;div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <Test/>
    <Test2/>
  </div>
</template>
<script>
import Test from './components/Test.vue'
import Test2 from './components/Test2.vue'
export default {
  name: 'app',
  components: {
    Test, Test2
  }
}
</script>

В терминале VS Code запустите ваш проект локально, выполнив команду:

npm run dev

Результат в окне браузера должен быть следующим:

Вы заметите, что если вам понадобится создать еще 5 похожих компонентов, вы будете копировать данные в каждый компонент по отдельности. Однако существует возможность один раз объявить данные в родительском компоненте, а затем передавать их в каждый из дочерних компонентов с помощью пропсов.

Определение данных в родительском компоненте

В тот момент, когда вы выбрали корневой компонент в качестве родительского, вам в первую очередь необходимо определить объект с данными внутри него. Если вы продолжаете следить за тем, что мы делаем, откройте файл App.vue и скопируйте объект с данными в секцию <script>.

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <Test />
    <Test2 />
  </div>
</template>

<script>
import Test from "./components/Test.vue";
import Test2 from "./components/Test2.vue";
export default {
  name: "app",
  components: {
    Test,
    Test2,
  },
  data() {
    return {
      artists: [
        { name: "Davido", genre: "afrobeats", country: "Nigeria" },
        { name: "Burna Boy", genre: "afrobeats", country: "Nigeria" },
        { name: "AKA", genre: "hiphop", country: "South-Africa" },
        { name: "Sarkodie", genre: "hiphop", country: "Ghana" },
        { name: "Stormzy", genre: "hiphop", country: "United Kingdom" },
        { name: "Lil Nas", genre: "Country", country: "United States" },
        { name: "Nasty C", genre: "hiphop", country: "South-Africa" },
        { name: "Shatta-walle", genre: "Reagae", country: "Ghana" },
        { name: "Khalid", genre: "pop", country: "United States" },
        { name: "Ed Sheeran", genre: "pop", country: "United Kingdom" },
      ],
    };
  },
};
</script>

Получение пропсов в Vue

После определения данных, перейдите в каждый из дочерних компонентов и удалите из них объекты data() вместе с их содержимым. Чтобы прокинуть пропсы в компонент, вы должны объявить пропсы, которые хотите получить, внутри каждого из компонентов. В каждом из Test-компонентов на месте объектов с данными, которые мы только что удалили, создайте новое свойство так, как показано ниже:

<script>
export default {
  name: 'Test',
  props: ['artists']
}   
</script>

Регистрация пропсов в Vue

Чтобы дать движку Vue понять, что у вас есть пропсы, которые вы динамически хотите передать в дочерние компоненты, вы должны сигнализировать об этом внутри родительского компонента. Сделать это можно в секции <template>, как показано ниже:

<template>
  <div id="app">
    <Test v-bind:artists="artists"/>
    <Test2 v-bind:artists="artists" />
  </div>
</template>
<script>
export default {
  name: 'Test',
  props: {
    artists: {
      type: Array
    }
  }
}
</script>

В коде выше мы используем директиву v-bind, чтобы связать массив artists в родительском компоненте с пропсом artists, который находится в каждом из дочерних компонентов.

Если вы сделаете это без использования директивы, то никакого смысла в этом не будет. Будьте внимательны: ни компилятор Vue, ни ESLint не подсветят это как ошибку:

    <Test artists="artists"/>
    <Test2 artists="artists"/>

Поэтому так важно быть внимательным и помнить об использовании директивы v-bind для всех динамических связок.

Использование пропсов в Vue

После того как вы настроили пропсы в своем проекте Vue, вы можете использовать их внутри дочерних компонентов так, будто данные были объявлены внутри каждого из них. Это значит, что доступ к данным будет осуществляться с помощью this.artists.

Строго типизированные пропсы

При строгой типизации пропсов вы можете быть уверены, что дочерний компонент получит именно тот тип данных, который вам нужен. В стартовом проекте для созданных нами пропсов вы можете добавить условие, что в качестве пропсов могут быть переданы только массивы:

<script>
export default {
  name: 'Test',
  props: {
    artists: {
      type: Array
    }
  }
}
</script>

Как следствие, если вы попытаетесь передать пропсом неверный тип, например String, то в консоли появится сообщение об ошибке, которая укажет на несоответствие типов:

Заключение

В этой статье мы открыли для себя пропсы в Vue 3, выяснили, как они могут помочь соблюдать принцип DRY (не повторяйся) путем переиспользования объектов с данными. Мы также выяснили, как регистрировать и передавать пропсы внутри проекта Vue. Более подробную информацию о пропсах можно найти в официальной документации Vue. Удачи!

Спасибо за внимание! Надеемся, что этот перевод был полезен для вас. 

Авторские материалы для разработчиков мы также публикуем в наших соцсетях – ВК и Telegram.

Комментарии (14)


  1. mgis
    00.00.0000 00:00
    +3

    Че то как то скучно, неужели не было больше примеров, как минимум хотя бы описания когда можно обойтись без использования props, а использовать provide/inject. А так сухая выжика из доки с парочкой банальных примеров.


  1. mironoffsky
    00.00.0000 00:00

    А в чем смысл статьи? Пропсы - это базовый термин для Vue, который излагается чуть ли не в первых страницах доки или любого курса, нет смысла его разбирать, тем более отдельно от остальных тем. Имхо, статья должна быть или всеобъемлющей (по теме) или иметь новизну, чтобы понравиться читателю, а это ни рыба, ни мясо.


  1. gmtd
    00.00.0000 00:00
    +1

    Статья не для новичков, а для предновичков

    Увольте своего Никиту

    И где кстати пропсы именно для Vue 3?
    Тут только Vue 2 (Ptions API) синтаксис.
    Где валидация, типизация и прочее?


  1. nin-jin
    00.00.0000 00:00
    +3

    выяснили, как они могут помочь соблюдать принцип DRY (не повторяйся)

    При строгой типизации пропсов вы можете быть уверены, что дочерний компонент получит именно тот тип данных, который вам нужен.


    1. zede
      00.00.0000 00:00

      То что подсвечено действительно камень в сторону автора оригинала статьи. Однако разберемся с указанными проблемами:

      Бесспорно по Vue нет FQN из MAM-а. Однако можно попытаться решить проблему 3мя путями:

      1. Script Setup

      <template>
        <Test />
      </template>
      <script setup>
      import Test from './components/Test.vue'
      </script>
      1. Global component (определяем компонент глобально и используем его везде) или миксином с нужными компонентами

      2. Auto-import (имеются плагины, которые по определенным правилам из указанных папок по указанным правилам генерирует имена компонентов и при совпадении подставлять автоматический импорт)

      Насчет массива. В случае использования JS, можно добавить валидатор, который явно в рантайме проверит значения. Однако при переключении на TS появляется большой набор вариантов решить эту проблему. Те там можно будет явно указать какой тип и с какими параметрами используется в пропсе (расписывание отдельных вариантов нахожу тривиальным).


      1. Djaler
        00.00.0000 00:00

        И все 3 решения так или иначе плохи.
        А на самом деле - и проблемы то нет)


      1. SimbirSoft_frontend Автор
        00.00.0000 00:00

        Спасибо вам и всем другим комментаторам за активную обратную связь! При переводе мы преследовали цель — помочь тем, кто находится на начальном этапе изучения нового для себя фреймворка, подсказать, что есть определенные способы передачи данных между компонентами. Обязательно обратим внимание на ваши уточнения. В будущем, возможно, выкатим статью про указанные кейсы)


    1. SimbirSoft_frontend Автор
      00.00.0000 00:00

      В статье приведены примеры импорта компонентов, которые будут понятны новичкам. Один из комментаторов ниже обратил внимание на отсутствие таких вещей, как Global component, и в будущем мы обязательно о них расскажем.

      Увы, в оригинале статьи автор не делает упор на использовании Vue в тандеме с TypeScript, поэтому более точная типизация массива опущена. Однако спасибо за дельное замечание, если у наших читателей будет интерес к теме Vue + TypeScript, мы обязательно напишем об этом отдельно


  1. Metotron0
    00.00.0000 00:00
    +1

    Я правильно понимаю, что кто-то переписал себе в блог пересказ официальной документации с английского на английский, а тут просто перевели на русский?

    Но, правда, я узнал, что функция setup с импортом всяких ref и reactive — это ещё не compositoin API, а compositon — это конкретно <script setup>, хотя я ещё проверю эту информацию, а то сомневаюсь.


    1. venanen
      00.00.0000 00:00

      И то и то composition. <script setup> это просто синтаксический сахар для function setup(){}.


      1. Metotron0
        00.00.0000 00:00

        Я тоже так думал, но в статье написали иначе.


    1. SimbirSoft_frontend Автор
      00.00.0000 00:00

      Действительно, вы правы, Composition API — это использование функции setup внутри тега <script> с применением описанных вами технологий, которые стали доступны в третьей версии Vue. Однако функцию setup можно использовать и описанным в статье образом с помощью <script setup>


      1. Metotron0
        00.00.0000 00:00

        Тогда фразу "Для компонентов, реализованных с помощью Options API" нужно прокомментировать, что у автора она есть (наверное есть, я не смотрел оригинал), но это ошибка.


  1. bavrr
    00.00.0000 00:00

    Пересказ документации, при этом смогли потерять часть про default, required, validator