vue2_rus

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

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

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

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