В чём разница между 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>
Now all the user data that ContactInfo needs will flow down the component tree and into ContactInfo from the ProfilePage!

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

The reason we keep the data in ProfilePage and not ContactInfo is that other parts of the profile page need access to the user object.

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

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

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

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