# Примеси

# Основы

Примеси (mixins) — инструмент для повторного использования кода в компонентах Vue. В объекте примеси могут содержаться любые опции компонентов. При использовании примеси в компоненте все её опции будут «подмешиваться» к опциям компонента.

Пример:

// объявляем объект примеси
const myMixin = {
  created() {
    this.hello()
  },
  methods: {
    hello() {
      console.log('привет из примеси!')
    }
  }
}

// объявляем приложение, которое использует примесь
const app = Vue.createApp({
  mixins: [myMixin]
})

app.mount('#mixins-basic') // => "привет из примеси!"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Слияние опций

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

Например, каждая примесь может иметь свою собственную функцию data. Каждая из них будет вызвана, а возвращённые объекты объединены. Свойства из data компонента при этом будут иметь приоритет в случае конфликта.

const myMixin = {
  data() {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

const app = Vue.createApp({
  mixins: [myMixin],
  data() {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created() {
    console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Функции хуков жизненного цикла объединяются в массив и все они будут вызваны. Причём хуки примеси будут вызываться перед хуками самого компонента.

const myMixin = {
  created() {
    console.log('вызван хук из примеси')
  }
}

const app = Vue.createApp({
  mixins: [myMixin],
  created() {
    console.log('вызван хук из компонента')
  }
})

// => "вызван хук из примеси"
// => "вызван хук из компонента"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Опции, значения которых будут объектами, такие как methods, components и directives будут объединены. В случае конфликтов приоритет будет у опций компонента:

const myMixin = {
  methods: {
    foo() {
      console.log('foo')
    },
    conflicting() {
      console.log('из примеси')
    }
  }
}

const app = Vue.createApp({
  mixins: [myMixin],
  methods: {
    bar() {
      console.log('bar')
    },
    conflicting() {
      console.log('из самого компонента')
    }
  }
})

const vm = app.mount('#mixins-basic')

vm.foo() // => "foo"
vm.bar() // => "bar"
vm.conflicting() // => "из самого компонента"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# Глобальные примеси

Примесь можно добавить глобально для всего Vue приложения:

const app = Vue.createApp({
  myOption: 'hello!'
})

// внедрение обработчика для пользовательской опции `myOption`
app.mixin({
  created() {
    const myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

app.mount('#mixins-global') // => "hello!"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Используйте данную возможность с ОСТОРОЖНОСТЬЮ! Глобальная примесь оказывает влияние на все экземпляры компонентов, создаваемые в дальнейшем в этом приложении (например, во всех дочерних компонентах):

const app = Vue.createApp({
  myOption: 'привет!'
})

// внедрение обработчика для пользовательской опции `myOption`
app.mixin({
  created() {
    const myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

// добавляем myOption также в дочерний компонент
app.component('test-component', {
  myOption: 'привет из компонента!'
})

app.mount('#mixins-global')

// => "привет!"
// => "привет из компонента!"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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

# Пользовательские стратегии слияния опций

При слиянии пользовательских опций по умолчанию применяется стратегия, в которой одни значения просто заменяются другими. Если нужно применить пользовательскую логику при слиянии, то потребуется переопределить функцию app.config.optionMergeStrategies:

const app = Vue.createApp({})

app.config.optionMergeStrategies.customOption = (toVal, fromVal) => {
  // возвращаем значение после слияния
}
1
2
3
4
5

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





 
 
 
 
 
 








const app = Vue.createApp({
  custom: 'привет!'
})

app.config.optionMergeStrategies.custom = (toVal, fromVal) => {
  console.log(fromVal, toVal)
  // => "пока!", undefined
  // => "привет!", "пока!"
  return fromVal || toVal
}

app.mixin({
  custom: 'пока!',
  created() {
    console.log(this.$options.custom) // => "привет!"
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Как можно увидеть, в консоли выводятся значения toVal и fromVal сначала из примеси, а затем из app. Сейчас при наличии будет возвращаться fromVal, поэтому в результате this.$options.custom будет со значением привет!. Переделаем стратегию слияния на «всегда возвращать значение дочернего экземпляра»:





 








const app = Vue.createApp({
  custom: 'привет!'
})

app.config.optionMergeStrategies.custom = (toVal, fromVal) => toVal || fromVal

app.mixin({
  custom: 'пока!',
  created() {
    console.log(this.$options.custom) // => "пока!"
  }
})
1
2
3
4
5
6
7
8
9
10
11
12

# Недостатки

Во Vue 2 примеси были основным инструментом для абстрагирования кусков логики компонентов в переиспользуемые части. Но у них есть несколько серьёзных проблем:

  • Примеси приводят к конфликтам: поскольку свойства каждой примеси объединяются в одном компоненте, всё равно потребуется знать обо всех примесях во избежание конфликта имён свойств и в целях отладки.

  • Будет казаться, что свойства появляются из ниоткуда: если компонент использует несколько примесей, то не всегда очевидно из какой примеси какие свойства пришли.

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

В целях их решения был представлен новый способ организации кода: Composition API.