Перейти к содержанию

Пропсы

Эта страница предполагает, что вы уже прочитали Основы компонентов.

Объявление пропсов

Компоненты Vue требуют явного объявления пропсов, чтобы Vue знал, какие внешние параметры, переданные компоненту, должны рассматриваться как передаваемые атрибуты.

В однофайловых компонентах, использующих <script setup>, пропсы могут быть объявлены с помощью макроса defineProps():

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

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

В компонентах, не относящихся к <script setup>, пропсы объявляются с помощью опции props:

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

Обратите внимание, что аргумент, передаваемый в defineProps(), совпадает со значением, указанным в опции props: один и тот же Options API используется совместно в двух стилях объявления.

Пропсы объявляются с помощью свойства props:

js
export default {
  props: ['foo'],
  created() {
    // props разворачиваются через `this`
    console.log(this.foo)
  }
}

Помимо объявления параметров с помощью массива строк, мы также можем использовать синтаксис объекта:

js
export default {
  props: {
    title: String,
    likes: Number
  }
}
js
// в <script setup>
defineProps({
  title: String,
  likes: Number
})
js
// не в <script setup>
export default {
  props: {
    title: String,
    likes: Number
  }
}

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

Это не только задокументирует ваш компонент, но и предупредит других разработчиков, использующих ваш компонент, в консоли браузера, если они передадут неверный тип. Более подробно о валидации параметров мы поговорим далее на этой странице.

Если вы используете TypeScript с <script setup>, можно также объявлять параметры с помощью чистых аннотаций типов:

vue
<script setup lang="ts">
defineProps<{
  title?: string
  likes?: number
}>()
</script>

Подробнее: Типизация пропсов компонента

Деструктуризация реактивных пропсов

Система реактивности Vue отслеживает использование состояний на основе доступа к свойствам. Например, когда вы обращаетесь к props.foo в вычисляемом геттере или наблюдателе, параметр foo отслеживается как зависимость.

Итак, приведём следующий код:

js
const { foo } = defineProps(['foo'])

watchEffect(() => {
  // выполняется только один раз до 3.5
  // перезапускается при изменении параметра "foo" в версии 3.5+
  console.log(foo)
})

В версии 3.4 и ниже foo является фактической константой и никогда не изменится. В версии 3.5 и выше компилятор Vue автоматически добавляет префикс props., когда код в одном блоке <script setup> обращается к переменным, деструктурированным из defineProps. Поэтому приведённый выше код становится эквивалентным следующему:

js
const props = defineProps(['foo'])

watchEffect(() => {
  // `foo` преобразуется компилятором в `props.foo`
  console.log(props.foo)
})

Кроме того, вы можете использовать собственный синтаксис JavaScript для объявления значений по умолчанию для параметров. Это особенно полезно при использовании объявления параметров на основе типов:

ts
const { foo = 'hello' } = defineProps<{ foo?: string }>()

Если вы предпочитаете визуально отличать деструктурированные параметры от обычных переменных в вашей IDE, расширение Vue VSCode предоставляет настройку, позволяющую включить подсказки для деструктурированных параметров.

Передача деструктурированных пропсов в функции

Когда мы передаем деструктурированный параметр в функцию, например:

js
const { foo } = defineProps(['foo'])

watch(foo /* ... */)

Это не будет работать так, как ожидалось, потому что это эквивалентно watch(props.foo, ...) — мы передаем значение watch, а не реактивный источник данных. На самом деле, компилятор Vue будет отлавливать такие случаи и выдавать предупреждение.

Подобно тому, как мы можем наблюдать за обычным параметром с помощью watch(() => props.foo, ...), мы также можем наблюдать за деструктурированным параметром, обернув его в геттер:

js
watch(() => foo /* ... */)

Кроме того, это рекомендуемый подход, когда нам нужно передать деструктурированный параметр во внешнюю функцию, сохранив при этом реактивность:

js
useComposable(() => foo)

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

Подробности передачи пропсов

Регистр имён пропсов

Мы объявляем длинные имена пропсов, используя «верблюжий» регистр (camelCase), потому что это позволяет избежать необходимости использовать кавычки при использовании их в качестве ключей свойств, а также позволяет нам ссылаться на них непосредственно в выражениях шаблона, поскольку они являются действительными идентификаторами JavaScript:

js
defineProps({
  greetingMessage: String
})
js
export default {
  props: {
    greetingMessage: String
  }
}
template
<span>{{ greetingMessage }}</span>

Технически, вы также можете использовать «верблюжий» регистр (camelCase) при передаче параметров дочернему компоненту (за исключением DOM шаблонов в DOM). Тем не менее, для согласования с атрибутами HTML принято использовать «шашлычный» регистр (kebab-case) во всех случаях:

template
<MyComponent greeting-message="привет" />

Мы используем PascalCase для тегов компонентов, когда это возможно, потому что это улучшает читаемость шаблона, отличая компоненты Vue от собственных элементов. Однако практической пользы от использования «верблюжьего» регистра при передаче параметров не так много, поэтому мы предпочитаем следовать соглашениям каждого языка.

Сравнение статических и динамических пропсов

До сих пор вы видели, что параметры передаются как статические значения, как в примере:

template
<BlogPost title="Мое путешествие с Vue" />

Вы также видели, как параметры назначаются динамически с помощью v-bind или его сокращения :, как, например, в:

template
<!-- Динамическое присвоение значения переменной -->
<BlogPost :title="post.title" />

<!-- Динамически присваивайте значение сложному выражению -->
<BlogPost :title="post.title + ' от ' + post.author.name" />

Передача различных типов значений

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

Число

template
<!-- Несмотря на то, что `42` статичен, нам нужен v-bind,            -->
<!--  чтобы сообщить Vue, что это выражение JavaScript, а не строка. -->
<BlogPost :likes="42" />

<!-- Динамическое присвоение значения переменной -->
<BlogPost :likes="post.likes" />

Булево значение

template
<!-- Включение параметра без значения будет означать `true`. -->
<BlogPost is-published />

<!-- Несмотря на то, что `false` является статичным, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка.    -->
<BlogPost :is-published="false" />

<!-- Динамическое присвоение значения переменной -->
<BlogPost :is-published="post.isPublished" />

Массив

template
<!-- Несмотря на то, что массив статичен, нам нужен v-bind,         -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка. -->
<BlogPost :comment-ids="[234, 266, 273]" />

<!-- Динамическое присвоение значения переменной -->
<BlogPost :comment-ids="post.commentIds" />

Объект

template
<!-- Несмотря на то, что объект статичен, нам нужен v-bind,            -->
<!-- чтобы сообщить Vue об этом это выражение JavaScript, а не строка. -->
<BlogPost
  :author="{
    name: 'Вероника',
    company: 'Veridian Dynamics'
  }"
 />

<!-- Динамическое присвоение значения переменной -->
<BlogPost :author="post.author" />

Связывание нескольких свойств с помощью объекта

Если вы хотите передать все свойства объекта в качестве параметров, вы можете использовать v-bind без аргумента (v-bind вместо :prop-name). Например, зададим объект post:

js
export default {
  data() {
    return {
      post: {
        id: 1,
        title: 'Мое путешествие с Vue'
      }
    }
  }
}
js
const post = {
  id: 1,
  title: 'Мое путешествие с Vue'
}

Следующий шаблон:

template
<BlogPost v-bind="post" />

Это будет эквивалентно:

template
<BlogPost :id="post.id" :title="post.title" />

Односторонний поток данных

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

Кроме того, при каждом обновлении родительского компонента все параметры в дочернем компоненте будут обновляться с учётом последнего значения. Это означает, что вы не должны пытаться изменить параметрт внутри дочернего компонента. Если вы это сделаете, Vue предупредит вас об этом в консоли:

js
const props = defineProps(['foo'])

// ❌ предупреждение, параметры доступны только для чтения!
props.foo = 'bar'
js
export default {
  props: ['foo'],
  created() {
    // ❌ предупреждение, параметры доступны только для чтения!
    this.foo = 'bar'
  }
}

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

  1. Свойство prop используется для передачи начального значения; дочерний компонент хочет использовать его в качестве локального свойства данных после этого. В этом случае лучше всего определить локальное свойство данных, которое использует параметр в качестве начального значения:

    js
    const props = defineProps(['initialCounter'])
    
    // счётчик использует только props.initialCounter в качестве начального значения;
    // он отключен от будущих обновлений параметров.
    const counter = ref(props.initialCounter)
    js
    export default {
      props: ['initialCounter'],
      data() {
        return {
          // счётчик использует только this.initialCounter в качестве начального значения;
          // он отключен от будущих обновлений параметров.
          counter: this.initialCounter
        }
      }
    }
  2. Параметр передается в виде необработанного значения, которое необходимо преобразовать. В этом случае лучше всего определить вычисляемое свойство, используя значение параметра:

    js
    const props = defineProps(['size'])
    
    // вычисляемое свойство, которое автоматически обновляется при изменении параметра
    const normalizedSize = computed(() => props.size.trim().toLowerCase())
    js
    export default {
      props: ['size'],
      computed: {
        // вычисляемое свойство, которое автоматически обновляется при изменении параметра
        normalizedSize() {
          return this.size.trim().toLowerCase()
        }
      }
    }

Мутирующие параметры объектов/массивов

Когда объекты и массивы передаются в качестве параметров, дочерний компонент не может изменить привязку параметра, но он будет иметь возможность изменять вложенные свойства объекта или массива. Это связано с тем, что в JavaScript объекты и массивы передаются по ссылке, и для Vue неоправданно дорого предотвращать такие мутации.

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

Валидация пропсов

Компоненты могут указывать требования к своим параметрам, например, типы, которые вы уже видели. Если требование не выполнено, Vue предупредит вас об этом в JavaScript-консоли браузера. Это особенно полезно при разработке компонента, который будет использоваться другими пользователями.

Чтобы задать валидацию параметров, вы можете предоставить объект с требованиями валидации макросу defineProps()свойству props вместо массива строк. Например:

js
defineProps({
  // Базовая проверка типа
  //  (значения `null` и `undefined` допускают любой тип)
  propA: Number,
  // Несколько возможных типов
  propB: [String, Number],
  // Требуется строка
  propC: {
    type: String,
    required: true
  },
  // Обязательная, но допускающая значение NULL строка
  propD: {
    type: [String, null],
    required: true
  },
  // Число со значением по умолчанию
  propE: {
    type: Number,
    default: 100
  },
  // Объект со значением по умолчанию
  propF: {
    type: Object,
    // Объект или массив по умолчанию должен быть возвращен из
    // фабричной функции. Функция получает необработанный
    // параметр, полученный компонентом в качестве аргумента.
    default(rawProps) {
      return { message: 'привет' }
    }
  },
  // Пользовательская функция валидатора
  propG: {
    validator(value) {
      // Значение должно соответствовать одной из этих строк
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // Функция со значением по умолчанию
  propH: {
    type: Function,
    // В отличие от объекта или массива по умолчанию, это не фабричная функция
    // function - это функция, служащая значением по умолчанию.
    default() {
      return 'Default function'
    }
  }
})

Примечание

Код внутри аргумента defineProps() не может получить доступ к другим переменным, объявленным в <script setup>, поскольку при компиляции всё выражение перемещается во внешнюю область видимости функции.

js
export default {
  props: {
    // Базовая проверка типа
    //  (значения `null` и `undefined` допускают любой тип)
    propA: Number,
    // Несколько возможных типов
    propB: [String, Number],
    // Требуется строка
    propC: {
      type: String,
      required: true
    },
    // Число со значением по умолчанию
    propD: {
      type: Number,
      default: 100
    },
    // Объект со значением по умолчанию
    propE: {
      type: Object,
      // Объект или массив по умолчанию должен быть возвращен из
      // фабричной функции. Функция получает необработанный
      // параметр, полученный компонентом в качестве аргумента.
      default(rawProps) {
        return { message: 'привет' }
      }
    },
    // Пользовательская функция валидатора
    propF: {
      validator(value) {
        // Значение должно соответствовать одной из этих строк
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // Функция со значением по умолчанию
    propG: {
      type: Function,
      // В отличие от объекта или массива по умолчанию, это не фабричная функция
      // - это функция, служащая значением по умолчанию.
      default() {
        return 'Функция по умолчанию'
      }
    }
  }
}

Дополнительные детали:

  • Все параметры по умолчанию необязательны, если только не указано required: true.

  • Отсутствующий необязательный параметр, отличный от Boolean, будет иметь значение undefined.

  • Отсутствующий параметр Boolean будет приведен к значению false. Вы можете изменить это, установив для него значение default: undefined, чтобы он вёл себя как небулевский параметр.

  • Если указано значение default, оно будет использоваться, если разрешённое значение prop будет undefined — это относится и к случаям, когда prop отсутствует, и к случаям, когда передано явное значение undefined.

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

При использовании объявлений параметров на основе типов , Vue постарается сделать всё возможное, чтобы скомпилировать аннотации типов в эквивалентные объявления свойств во время выполнения. Например, defineProps<{ msg: string }> будет скомпилирован в { msg: { type: String, required: true }}.

Примечание

Обратите внимание, что параметры проверяются до создания экземпляра компонента, поэтому свойства экземпляра (например, data, computed и т. д.) не будут доступны внутри функций default или validator.

Проверки типов во время выполнения

В качестве type может выступать один из следующих собственных конструкторов:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • Error

Кроме того, type может быть пользовательским классом или функцией конструктора, и утверждение будет выполнено с помощью проверки instanceof. Например, если взять следующий класс:

js
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

Можно использовать его как тип параметра:

js
defineProps({
  author: Person
})
js
export default {
  props: {
    author: Person
  }
}

Vue будет использовать instanceof Person для проверки того, действительно ли значение свойства author является экземпляром класса Person.

Тип null

Если тип является обязательным, но допускает значение NULL, вы можете использовать синтаксис массива, включающий null:

js
defineProps({
  id: {
    type: [String, null],
    required: true
  }
})
js
export default {
  props: {
    id: {
      type: [String, null],
      required: true
    }
  }
}

Обратите внимание, что если type — это просто null без использования синтаксиса массива, то будет разрешён любой тип.

Приведение к булевому типу

Параметры с типом Boolean имеют специальные правила приведения, чтобы имитировать поведение собственных булевых атрибутов. Дан <MyComponent> со следующим объявлением:

js
defineProps({
  disabled: Boolean
})
js
export default {
  props: {
    disabled: Boolean
  }
}

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

template
<!-- эквивалент прохождения :disabled="true" -->
<MyComponent disabled />

<!-- эквивалент прохождения :disabled="false" -->
<MyComponent />

Когда параметр объявлен с возможностью использования нескольких типов, правила приведения для Boolean также будут применяться. Однако есть вариант, когда разрешены и String, и Boolean — правило приведения булевых значений применяется только в том случае, если булевое значение появляется перед String:

js
// disabled будет приведен к значению true
defineProps({
  disabled: [Boolean, Number]
})

// disabled будет приведен к значению true
defineProps({
  disabled: [Boolean, String]
})

// disabled будет приведен к значению true
defineProps({
  disabled: [Number, Boolean]
})

// disabled будет обработан как пустая строка (disabled="")
defineProps({
  disabled: [String, Boolean]
})
js
// disabled будет приведен к значению true
export default {
  props: {
    disabled: [Boolean, Number]
  }
}

// disabled будет приведен к значению true
export default {
  props: {
    disabled: [Boolean, String]
  }
}

// disabled будет приведен к значению true
export default {
  props: {
    disabled: [Number, Boolean]
  }
}

// disabled будет обработан как пустая строка (disabled="")
export default {
  props: {
    disabled: [String, Boolean]
  }
}
Пропсы