Могут быть данные/помогайки, которые планируете использовать во многих компонентах, но не хотите при этом загрязнять глобальную область видимости. В этих случаях вы можете сделать их доступными для каждого экземпляра 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. Но вам очень понравился доступ к методам запросов через 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
там где они нужны. В таком варианте больше ясности, потому что в каждом файле вы получаете список зависимостей. Вы знаете наверняка откуда что используется.
Хотя этот подход несколько многословнее, но он наиболее удобен для поддержки, особенно при работе с другими разработчиками и/или создании больших приложений.