Добавление свойств экземпляра

Простой пример

Могут быть данные/утилиты, которые планируете использовать во многих компонентах, но не хотите при этом загрязнять глобальную область видимости. В этих случаях вы можете сделать их доступными для каждого экземпляра Vue, указав их в прототипе:

Vue.prototype.$appName = 'Моё приложение';

Теперь $appName доступно во всех экземплярах Vue, даже до создания. Если мы запустим:

new Vue({
beforeCreate: function () {
console.log(this.$appName);
}
});

Тогда "Моё приложение" будет выведено в консоль!

Важность именования свойств экземпляра

Вам может быть интересно:

“Почему appName начинается $? Это важно? Что это даёт?

Никакой магии здесь нет. $ — это соглашение, которое Vue использует для свойств, доступных во всех экземплярах. Это позволяет избежать конфликтов с любыми объявленными свойствами в data, вычисляемыми свойствами или методами.

“Конфликты? Что вы имеете ввиду?”

Ещё один хороший вопрос! Допустим, если вы установили:

Vue.prototype.appName = 'Моё приложение';

Тогда что вы ожидаете будет выведено в консоль в примере ниже?

new Vue({
data: {
// Ох ах - appName *случайно* совпало с именем
// свойства экземпляра, которое мы определили!
appName: 'Название какого-то другого приложения'
},
beforeCreate: function () {
console.log(this.appName);
},
created: function () {
console.log(this.appName);
}
});

Будет выведено "Моё приложение", а затем "Название какого-то другого приложения", потому что this.appName (в каком-то роде) будет перезаписан свойством data после создания экземпляра. Мы именуем свойства экземпляра с $ чтобы избежать такого. Вы даже можете использовать своё собственное соглашение по именованию, если хотите, например $_appName или ΩappName, для предотвращения конфликтов с плагинами или будущими функциями.

Пример из жизни: замена Vue Resource на Axios

Предположим, вы заменяете ушедший на пенсию Vue Resource. Но вам очень понравился доступ к методам запросов через this.$http и вы хотите получить аналогичное с axios.

Всё, что вам нужно сделать — это добавить axios в ваш проект:

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.2/axios.js"></script>

<div id="app">
<ul>
<li v-for="user in users">{{ user.name }}</li>
</ul>
</div>

И связать axios со свойством Vue.prototype.$http:

Vue.prototype.$http = axios;

Теперь вы можете использовать такие методы как this.$http.get в любом экземпляре Vue:

new Vue({
el: '#app',
data: {
users: []
},
created() {
var vm = this;
this.$http
.get('https://jsonplaceholder.typicode.com/users')
.then(function (response) {
vm.users = response.data;
});
}
});

Контекст методов прототипа

Если вы не знаете, то методы, добавленные в прототип, в JavaScript получают контекст экземпляра. Это означает, что они могут использовать this для доступа к свойствам data, вычисляемым свойствам, методам, или чему-либо ещё, объявленному в экземпляре.

Воспользуемся этой возможностью в методе $reverseText:

Vue.prototype.$reverseText = function (propertyName) {
this[propertyName] = this[propertyName]
.split('')
.reverse()
.join('');
};

new Vue({
data: {
message: 'Привет'
},
created: function () {
console.log(this.message); // => "Привет"
this.$reverseText('message');
console.log(this.message); // => "тевирП"
}
});

Обратите внимание, что привязка к контексту не будет работать, если вы используете стрелочные функции ES6/2015, поскольку они неявно связываются с родительской областью видимости. Это означает, что вариант со стрелочной функцией:

Vue.prototype.$reverseText = propertyName => {
this[propertyName] = this[propertyName]
.split('')
.reverse()
.join('');
};

Закончится ошибкой:

Uncaught TypeError: Cannot read property 'split' of undefined

Когда избегать этого паттерна

Пока вы внимательно следите за контекстом свойств прототипа, использование этого паттерна вполне безопасно и не добавит никаких багов.

Однако, иногда это может привести к путанице с другими разработчиками. Например, они могут увидеть this.$http и подумать “О, я и не знал об этой возможности Vue!”. Затем они переходят на другой проект и не понимают, почему this.$http будет undefined. Или, может быть они хотят найти в Google как сделать что-либо, но ничего не могут найти, потому что не понимают, что на самом деле это всего лишь псевдоним для axios.

Удобство приходит за счёт очевидности. При просмотре компонента невозможно сказать откуда появился $http. Возможность Vue? Плагин? Добавил коллега?

Итак, какие есть альтернативы?

Альтернативы

Если не используем модульную систему

В приложениях без модульной системы (такой как Webpack или Browserify), существует паттерн, который часто используется в любом случае расширения фронтенда на JavaScript: глобальный объект App.

Если то, что вы хотите добавить, не имеет ничего общего с Vue, это может стать хорошим местом для расположения подобной логики. Вот пример:

var App = Object.freeze({
name: 'Моё приложение',
version: '2.1.4',
helpers: {
// Это чистая функциональная версия
// метода $reverseText, который мы видели ранее
reverseText: function (text) {
return text
.split('')
.reverse()
.join('');
}
}
});

Если вас удивило использование Object.freeze, то он предотвращает изменение объекта в будущем. Это по сути делает все свойства объекта константами, защищая вас от появления ошибок изменения состояния.

Теперь источник этих общих свойств можно увидеть явно: есть объект App, объявленный где-то в приложении. Найти его разработчики смогут поиском по проекту.

Другим преимуществом будет то, что App теперь можно использовать где угодно в вашем коде, независимо от того относится ли он к Vue или нет. Это включает привязку значений напрямую к свойствам экземпляра, вместо того, чтобы вводить функцию для доступа к свойствам в this:

new Vue({
data: {
appVersion: App.version
},
methods: {
reverseText: App.helpers.reverseText
}
});

Если используем модульную систему

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

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