В чём разница между props и data в Vue. js

22 июля 2019

Во Vue предусмотрено два способа хранения данных в компоненте: props и data.

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

Так в чём же отличие между props и data?

Data — это приватное хранилище любого компонента, в котором вы можете хранить любые данные.

Props — то, как вы передаёте данные из родительского компонента в дочерний.

В этой статье вы узнаете:

  • Чем являются props, и почему они нужны только для передачи данных вниз;
  • Для чего используется data;
  • Что такое реактивность;
  • Как избежать конфликта имён между props и data;
  • Как одновременно использовать props и data вместе, чтобы получить пользу 💰

Что такое props?

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

Когда мы проектируем приложение, то в конечном итоге создаём структуру, называемую деревом. Это похоже на семейное дерево:

  • родители,
  • дети,
  • предки,
  • потомки.

Данные передаются вниз от корневого компонента, который находится в самом верху. Похоже на то, как передаётся генетика из поколения в поколение, так же и родительские компоненты передают props дальше своим дочерним компонентам.

Во Vue мы добавляем props в компоненты в секции <template>:

<template>
  <my-component cool-prop="hello world"></my-component>
</template>

В этом примере мы передаём prop cool-prop со значением «hello world». Мы сможем получить доступ к значению этого свойства внутри компонента my-component.

Однако, когда мы обращаемся к пропсам внутри компонента, то мы не можем их изменить, потому что не являемся их владельцами (точно также вы не можете изменить гены, которые вам передали родители).

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

Что же делать, если нельзя менять пропсы? На помощь приходит data!

Что такое Data?

Data — это память каждого компонента. Это место, где вы можете хранить ваши данные и другие переменные, которые вам необходимы.

Если мы делаем приложение-счётчик, то мы будем отслеживать и хранить переменную-счётчик count в data:

<template>
  <div>
    {{ count }}
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
  </div>
</template>

<script>
export default {
  name: 'Counter',
  data() {
    return {
      // Инициализируем с нуля
      count: 0,
    }
  },
  methods: {
    increment() {
      this.count += 1;
    },
    decrement() {
      this.count -= 1;
    }
  }
}
</script>

Этот data-параметр является приватным и доступен только внутри компонента. Другие компоненты не имеют к нему доступа.

Заметьте: На самом деле вы можете получать доступ к этому data-параметру из других компонентов, но это очень-очень плохая идея. Не стоит так делать!

Если вам нужно передать данные между компонентами, то используйте props для передачи вниз по дереву (дочерним компонентам), или события для передачи вверх по дереву (родительским компонентам).

Props и Data реактивны

С Vue вам не нужно думать о том, когда компонент обновит сам себя и перерендерится с новыми данными. Всё потому, что Vue реактивный.

Вместо вызова setState в случае каких-то изменений, вы просто меняете это напрямую. Пока вы обновляете реактивные свойства (props, computed props и что-либо в data), Vue знает как следить за изменениями.

Возвращаясь к нашему приложению-счётчику, давайте посмотрим на методы:

methods: {
  increment() {
    this.count += 1;
  },
  decrement() {
    this.count -= 1;
  }
}

Всё что нам нужно сделать, это обновить значение count. Vue автоматически это отследит и сразу же перерендерит наше приложение с новым значением.

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

Предотвращение конфликта имён

Есть ещё одна вещь, которую делает Vue, что делает разработку ещё прекраснее.

Давайте определим некоторые props и data в компоненте:

export default {
  props: ['propA', 'propB'],
  data() {
    return {
      dataA: 'hello',
      dataB: 'world',
    };
  },
};

Если вы захотите получить доступ к ним внутри метода компонента, то нет необходимости писать this.props.propA или this.props.dataA. Vue позволяет использовать упрощённый интерфейс доступа к props и data.

Мы можем получить к ним доступ через this.propA или this.dataA:

methods: {
  coolMethod() {
    // Получаем доступ к props
    console.log(this.propA);

    // Получаем доступ к data
    console.log(this.dataA);
  }
}

По этой причине мы можем столкнуться с проблемами, если будет использовать одинаковые имена для props и data.

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

export default {
  props: ['secret'],
  data() {
    return {
      secret: '1234',
    };
  },
  methods: {
    printSecret() {
      // К чему именно вы обращаетесь?
      console.log(this.secret);
    }
  }
};

Настоящая магия и чудо происходят в тот момент, когда вы используете props и data вместе.

Использование props и data вместе

Мы изучили, чем props и data отличаются. Давайте посмотрим на примере простого приложения, почему их стоит использовать одновременно.

Давайте представим, что мы делаем социальную сеть и работаем над страницей пользователя. Мы дошли до того момента, когда нам нужно вывести контактную информацию пользователя.

Мы будем отображать это с помощью компонента ContactInfo:

// ContactInfo
<template>
  <div class="container">
    <div class="row">
      Email: {{ emailAddress }}
      Twitter: {{ twitterHandle }}
      Instagram: {{ instagram }}
    </div>
  </div>
</template>
export default {
  name: 'ContactInfo',
  props: ['emailAddress', 'twitterHandle', 'instagram'],
};

Компонент ContactInfo получается props emailAddress, twitterHandle и instagram, и показывает их на странице.

Наш компонент для страницы, ProfilePage, выглядит так:

// ProfilePage
<template>
  <div class="profile-page">
    <div class="avatar">
      <img src="user.profilePicture" />
      {{ user.name }}
    </div>
  </div>
</template>

<script>
export default {
  name: 'ProfilePage',
  data() {
    return {
      // В реальном приложении эти данные мы бы получили от сервера
      user: {
        name: 'John Smith',
        profilePicture: './profile-pic.jpg',
        emailAddress: 'john@smith.com',
        twitterHandle: 'johnsmith',
        instagram: 'johnsmith345',
      },
    }
  }
};
</script>

Компонент ProfilePage показывает фото и имя пользователя. Он также содержит объект с данными пользователя в data.

Как нам получить данные из родительского компонента (ProfilePage) в дочернем компоненте ContactInfo?

Мы должны передать эти данные (data) используя props.

Для начала мы должны импортировать компонент ContactInfo в наш основной компонент ProfilePage:

// Импортируем компонент
import ContactInfo from './ContactInfo.vue';

export default {
  name: 'ProfilePage',

  // Добавляем его как зависимость
  components: {
    ContactInfo,
  },

  data() {
    return {
      user: {
        name: 'John Smith',
        profilePicture: './profile-pic.jpg',
        emailAddress: 'john@smith.com',
        twitterHandle: 'johnsmith',
        instagram: 'johnsmith345',
      },
    }
  }
};

Во-вторых, мы должны добавить компонент в секцию <template>:

// ProfilePage
<template>
  <div class="profile-page">
    <div class="avatar">
      <img :src="user.profilePicture" />
      {{ user.name }}
    </div>

    <!-- Добавляем компонент и используем props -->
    <contact-info
      :email-address="user.emailAddress"
      :twitter-handle="user.twitterHandle"
      :instagram="user.instagram"
    />

  </div>
</template>




Теперь все данные пользователя, которые нужны компоненту ContactInfo, будут переданы вниз по дереву компонентов от ProfilePage.

Мы не стали хранить объект с данными пользователя в компоненте ContactInfo, потому что есть другие части профиля (компоненты), которым нужен доступ к этому объекту.

Поскольку данные (data) передаются только вниз, это означает, что мы должны помещать данные (data) достаточно высоко в дереве компонентов. Так мы будем иметь возможность передать все данные (data) с помощью свойств (props) в любой дочерний компонент.

Статья является переводом материала «Props Versus Data in Vue: The Subtle Differences You Need to Know», автор Майкл Тиссен.

Комментарии 5
  1. Ринат 28 октября 2019

    Почему в последнем примере при таком коде картинка не грузится:
    А если аттрибут src «забиндить», то картинка отображается:?

  2. Кирилл Арутюнов 28 октября 2019

    Действительно, в последнем примере ошибка. Спасибо, что заметил!

    Фокус в том, что когда ты пишешь :src="myVar", шаблонизатор Vue подставляет туда значение переменной, а если пишешь src="someVar", то Vue никак не обрабатывает значение внутри кавычек. Раньше, как я понимаю, могли писать так: src="{{ someVar }}", но потом это убрали.

    Подробнее про парсинг шаблонов можно почитать в документации.

  3. Юрий 15 января 2023

    Приветствую. Не могли бы пояснить за последний пример? В тексте статьи про совместное использование данных из data и props, а в коде props не используется, в шаблоне выводится только то, что вернула функция data ().

  4. Кирилл Арутюнов 17 января 2023

    Привет! Да, в последнем примере речь идёт о двух разных компонентах. Причём один компонент (ProfilePage) внутри себя рендерит второй компонент (ContactInfo). И из ProfilePage через пропсы прокидываются данные из ProfilePage. data в ContactInfo.props.
    Куски кода не собраны вместе и это может вызывать путаницу — я брал их 1-в-1 из исходной статьи и не адаптировал при переводе.

  5. Данияр 13 марта 2023

    Здравствуйте! Как правильно биндить значения при отрисовке компонента в template? Какими должны быть значени1 и значение2? (:email-address="user.emailAddress" или: значение1="значение2″). Ввело в заблуждение то, что email-adress пишется через дефис при бинде, хотя в компоненте ContactInfo данное значение внутри интерполяции пишется как emailAdress, т. е. без дефиса

Добавить комментарий