# Композиция

# mixins

  • Тип: Array<Object>

  • Подробности:

    Опция mixins принимает массив объектов примесей. Эти объекты могут содержать опции экземпляра, как и обычные объекты экземпляра, и они будут объединены в окончательный набор опций с использованием специальной логики объединения. Например, если хук created указан в примеси и есть в компоненте, то будут вызваны обе функции.

    Хуки в примесях вызываются в порядке их определения до вызова собственных хуков компонента.

Информация

Во Vue 2, примеси были основным механизмом для создания многократно используемых фрагментов логики компонентов. Хотя примеси продолжают поддерживаться во Vue 3, теперь Composition API является предпочтительным подходом для повторного использования кода между компонентами.

  • Пример:

    const mixin = {
      created() {
        console.log(1)
      }
    }
    
    createApp({
      created() {
        console.log(2)
      },
      mixins: [mixin]
    })
    
    // => 1
    // => 2
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • См. также: Примеси

# extends

  • Тип: Object

  • Подробности:

    Позволяет одному компоненту расширять другой, наследуя его опции.

    С точки зрения реализации, extends очень похож на mixins. Компонент, указанный с помощью extends, будет рассматриваться так же, как если бы он был первой примесью.

    Однако extends и mixins преследуют разные цели. Опция mixins в основном используется для компоновки функциональных блоков, в то время как extends в основном связана с наследованием.

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

  • Пример:

    const CompA = { ... }
    
    const CompB = {
      extends: CompA,
      ...
    }
    
    1
    2
    3
    4
    5
    6

# provide / inject

  • Тип:

    • provide: Object | () => Object
    • inject: Array<string> | { [key: string]: string | Symbol | Object }
  • Подробности:

    Эта пара опций используется вместе, чтобы позволить компоненту-родителю внедрять зависимости во все компоненты-потомки. Не имеет значения, насколько глубоко в иерархии они находятся, пока располагаются в той же самой родительской цепочке. Если знакомы с React, то это очень похоже на функцию context.

    Опция provide должна быть объектом или функцией, возвращающей объект. Этот объект содержит свойства, которые после внедрения будут доступны в потомках. Можно использовать ES2015 Symbols в качестве ключей объекта, но только в окружениях, которые нативно поддерживают Symbol и Reflect.ownKeys.

    Опция inject должна быть:

    • массивом строк;
    • или объектом, где ключами являются локальные имена привязки, а значением может быть:
      • ключ (строка или Symbol) для поиска в доступных внедрениях;
      • объект, в котором
        • свойство from является ключом (строка или Symbol) для поиска в доступных внедрениях,
        • свойство default используется в качестве запасного значения.

    Примечание: привязки provide и inject НЕ РЕАКТИВНЫ. Так сделано специально. Однако, если передать реактивный объект, то его свойства останутся реактивными.

  • Пример:

    // родительский компонент предоставляет доступ к 'foo'
    const Provider = {
      provide: {
        foo: 'bar'
      }
      // ...
    }
    
    // дочерний компонент внедряет 'foo' для использования
    const Child = {
      inject: ['foo'],
      created() {
        console.log(this.foo) // => "bar"
      }
      // ...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    Работа с функциями provide и объектом inject с использованием ES2015 Symbols:

    const s = Symbol()
    
    const Provider = {
      provide() {
        return {
          [s]: 'foo'
        }
      }
    }
    
    const Child = {
      inject: { s }
      // ...
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    Использование внедряемого значения для входного параметра в качестве значения по умолчанию:

    const Child = {
      inject: ['foo'],
      props: {
        bar: {
          default() {
            return this.foo
          }
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    Использование внедряемого значения в качестве данных:

    const Child = {
      inject: ['foo'],
      data() {
        return {
          bar: this.foo
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    Инъекции можно указывать с собственным значением по умолчанию:

    const Child = {
      inject: {
        foo: { default: 'foo' }
      }
    }
    
    1
    2
    3
    4
    5

    Для внедрения значение из свойства под другим именем можно использовать опцию from для определения свойства-источника:

    const Child = {
      inject: {
        foo: {
          from: 'bar',
          default: 'foo'
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    Как и для значений по умолчанию входных параметров, для не-примитивных значений необходимо использовать фабричную функцию:

    const Child = {
      inject: {
        foo: {
          from: 'bar',
          default: () => [1, 2, 3]
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  • См. также: Provide / Inject

# setup

  • Тип: Function

Функция setup — новая опция компонента. Она будет стартовой точкой для использования Composition API внутри компонентов.

  • Время вызова

    Функция setup вызывается сразу после первичного разрешения входных параметров, когда создаётся экземпляр компонента. В жизненном цикле вызов будет перед хуком beforeCreate.

  • Использование с шаблонами

    Если setup возвращает объект, его свойства будут объединены в контексте отрисовки для шаблона компонента:

    <template>
      <div>{{ count }} {{ object.foo }}</div>
    </template>
    
    <script>
      import { ref, reactive } from 'vue'
    
      export default {
        setup() {
          const count = ref(0)
          const object = reactive({ foo: 'bar' })
    
          // предоставляем доступ в шаблоне
          return {
            count,
            object
          }
        }
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    Обратите внимание, что refs, возвращаемые из setup, автоматически разворачиваются при обращениях в шаблоне. Поэтому нет необходимости указывать .value в шаблонах.

  • Использование с render-функциями / JSX

    Функция setup также может возвращать render-функцию, которая может напрямую использовать реактивное состояние, объявленное в той же области видимости:

    import { h, ref, reactive } from 'vue'
    
    export default {
      setup() {
        const count = ref(0)
        const object = reactive({ foo: 'bar' })
    
        return () => h('div', [count.value, object.foo])
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • Аргументы

    Первым аргументом функция принимает разрешённые входные параметры:

    export default {
      props: {
        name: String
      },
      setup(props) {
        console.log(props.name)
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    Обратите внимание, что объект props является реактивным — т.е. он обновляется при передаче новых значений входных параметров. Это позволяет его отслеживать и реагировать на изменения с помощью watchEffect или watch:

    export default {
      props: {
        name: String
      },
      setup(props) {
        watchEffect(() => {
          console.log(`Имя: ` + props.name)
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    НЕ ИСПОЛЬЗУЙТЕ деструктуризацию для объекта props, потому что это приведёт к потере его реактивности:

    export default {
      props: {
        name: String
      },
      setup({ name }) {
        watchEffect(() => {
          console.log(`Имя: ` + name) // Реактивности уже не будет!
        })
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

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

    Второй аргумент предоставляет объект с контекстом, который позволяет получить доступ к различным объектам и функциям, полезным внутри setup:

    const MyComponent = {
      setup(props, context) {
        context.attrs
        context.slots
        context.emit
        context.expose
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    attrs, slots и emit являются эквивалентами свойств экземпляра $attrs, $slots и $emit соответственно.

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

    const MyComponent = {
      setup(props, { attrs }) {
        // функция, которая может вызываться где-то позднее
        function onClick() {
          console.log(attrs.foo) // гарантированно будет актуальной ссылкой
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    В версии Vue 3.2 добавлена expose — функция, позволяющая объявить определённые свойства через публичный экземпляр компонента. По умолчанию, публичный экземпляр, полученный с помощью $refs, $parent или $root эквивалентен внутреннему экземпляру, используемому шаблоном. При вызове expose будет создан отдельный публичный экземпляр с объявленными свойствами:

    const MyComponent = {
      setup(props, { expose }) {
        const count = ref(0)
        const reset = () => count.value = 0
        const increment = () => count.value++
    
        // Только метод reset будет доступен извне, например через $refs
        expose({
          reset
        })
    
        // Внутри у шаблона будет доступ к count и increment
        return { count, increment }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

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

    • Гораздо чаще компонент использует props, чем какие-либо другие свойства. Очень часто компонент использует только props.

    • Наличие props в качестве отдельного аргумента облегчает вывод их типов, без путаницы с типами других свойств в контексте. Это также позволяет сохранить консистентную сигнатуру между setup, render и простыми функциональными компонентами с поддержкой TSX.

  • См. также: Composition API