Книга рецептовbeta
- Введение
- Добавление свойств экземпляра
- Валидация форм
- Редактируемая система SVG-иконок
- Создание блога, управляемого CMS
- Модульное тестирование Vue-компонентов
- Создание пользовательской директивы прокрутки
- Отладка в VS Code
- Используем axios для доступа к API
- Устранение утечек памяти
- Хранение данных на стороне клиента
- Публикация Vue-компонентов в npm
- Интегрируем Docker в приложение Vue.js
- Практическое использование слотов с ограниченной областью видимости с GoogleMaps
Эта документация для версий v2.x и ранее. Для v3.x, документация здесь.
Добавление свойств экземпляра
Простой пример
Могут быть данные/утилиты, которые планируете использовать во многих компонентах, но не хотите при этом загрязнять глобальную область видимости. В этих случаях вы можете сделать их доступными для каждого экземпляра 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
там где они нужны. В таком варианте больше ясности, потому что в каждом файле вы получаете список зависимостей. Вы знаете наверняка откуда что используется.
Хотя этот подход несколько многословнее, но он наиболее удобен для поддержки, особенно при работе с другими разработчиками и/или создании больших приложений.