Когда использовать vm. или это. в Vue.js

Я немного смущен, когда использовать слово "this" в vue.js. Например, в коде ниже везде я использую "vm" вместо "this" код не работает.

Я также видел несколько примеров, используя "я", но я не гуру javascript, и это действительно запутывает.

var vm = new Vue({
        el: '#app',
        data: {
            tickets: [],
            top: 100,
            search: '',
            showAdd: false,
         },
        mounted: function () {
            this.$nextTick(function () {
                console.log('mounted');
                this.GetTickets(100);
            })
        },
        methods: {
            GetTickets: function (top) {
                axios.get('/api/Tickets', {
                    params: {
                        Top: top
                    }
                })
                    .then(function (response) {
                        vm.tickets = response.data;
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
            },
            ClearTicket: function () {
                var t = {
                    "ticketSubject": '',
                    "contactName": '',
                    "createdAt": moment()
                }
                vm.ticket = t;
                vm.showAdd = !vm.showAdd;
            },
            AddTicket: function () {
                //vm.tickets.unshift(vm.ticket);
                axios.post('/api/Tickets', vm.ticket)
                    .then(function (response) {
                        console.log(response);
                        vm.GetTickets(100);
                    })
                    .catch(function (error) {
                        console.log(error);
                    });
                vm.showAdd = false;

            }
        },

    })

Ответ 1

Как правило, внутри методов, или вычисляемых свойств или обработчиков жизненного цикла в Vue, вы будете использовать this для ссылки на компонент, к которому присоединен метод /computed/handler. this относится к контексту, в котором функция в настоящее время выполняется.

При использовании this метода возникают проблемы, когда в контексте текущей функции объявляется новая функция, как это происходит при написании обратного вызова для обещания (axios.post, axios.get). Рассмотрим этот код:

AddTicket: function () {
  // "this", on this line, refers to the Vue
  // and you can safely use "this" to get any of the
  // data properties of the Vue
    axios.post('/api/Tickets', ...)
      .then(function (response) {
        // "this" HERE, does NOT refer to the Vue!!
        // The reason why explained below              
      })
}

В приведенном выше коде первый комментарий может быть заменен кодом, который использует this для получения свойств данных или вызова методов Vue (this.tickets). Второй комментарий, однако, находится внутри нового контекста функции, и this НЕ будет ссылаться на Vue. Это связано с тем, что в Javascript, когда вы объявляете новую функцию с использованием синтаксиса function() {}, эта функция имеет собственный контекст функции, который отличается от функции, в которой она объявлена.

Есть несколько способов справиться с этим в Javascript. Чаще всего в эти дни либо использовать замыкание, чтобы захватить правильное this, или использовать функцию стрелки. Рассмотрим этот код:

AddTicket: function () {
  // As before, "this" here is the Vue
    axios.post('/api/Tickets', ...)
      .then((response) => {
        // "this" HERE is ALSO the Vue
      })
}

Обратите внимание, что в этом примере обратный вызов определяется с помощью функции стрелки (() => {}). Функции стрелок НЕ создают свой собственный контекст функции и используют контекст, в котором они объявлены. Это также известно как наличие лексической области.

Другой наиболее распространенный обходной путь - использование замыкания.

AddTicket: function () {
  const self = this // Here we save a reference to the "this" we want
    axios.post('/api/Tickets', ...)
      .then(function(response){
        // and HERE, even though the context has changed, and we can't use
        // "this", we can use the reference we declared (self) which *is*
        // pointing to the Vue
        self.tickets = response
      })
}

Наконец, вы можете использовать метод bind для создания функции со специфическим this, хотя в наши дни это не так часто встречается при наличии функций стрелок.

AddTicket: function () {
    axios.post('/api/Tickets', ...)
      .then(function(response){
        this.tickets = response
      }.bind(this)) // NOTE the ".bind(this)" added to the end of the function here
}

Практически ни в коем случае не следует ли вам действительно делать то, что вы делаете в своем вопросе: сохранить ссылку на Vue в переменной vm и использовать эту переменную внутри самого объекта Vue. Это плохая практика.

В любом случае, как использовать правильно this подробно описано в многочисленных сообщениях по всему Интернету и здесь, fooobar.com/questions/1337/..., а также.

Наконец, вот код вопроса, пересмотренный так, что this должно использоваться правильно.

var vm = new Vue({
  el: '#app',
  data: {
    tickets: [],
    top: 100,
    search: '',
    showAdd: false,
    ticket: null
  },
  mounted: function () {
    // there is no need for $nextTick here
    this.GetTickets(100)
  },
  methods: {
    GetTickets: function (top) {
      axios.get('/api/Tickets', { params: { Top: top }})
        .then(response => this.tickets = response.data)
        .catch(error => console.log(error));
    },
    ClearTicket: function () {
      var t = {
        "ticketSubject": '',
        "contactName": '',
        "createdAt": moment()
      }
      this.ticket = t;
      this.showAdd = !this.showAdd;
    },
    AddTicket: function () {
      axios.post('/api/Tickets', this.ticket)
        .then(() => this.GetTickets(100))
        .catch(error => console.log(error));

      this.showAdd = false;
    }
  },
})

Ответ 2

В конце концов, это просто. Пока вы не полностью поймете, как это работает, просто следуйте этим простым правилам:

Используйте this всюду в объекте Vue и используйте его ссылочный идентификатор вне:

var vm = new Vue({
  // Use "this" inside
  el: '#app',
  data: {
    something: true
  },
  created: function () {
    this.something = false // like here
  }
})

// Here, outside, use reference iditentifier,
// as you have no other choice
vm.something = null

Никогда не используйте имя ссылки внутри самого объекта ссылки. Вне объекта Vue у вас нет другого выбора, вы просто должны использовать имя ссылки.

Внутри Vue содержимое this может отличаться. И воля. Это просто еще один объект, автоматически создаваемый внутри каждой функции/объекта. Поэтому вам нужно это второе правило: прежде чем вы погрузитесь в вложенную функцию второго уровня, сохраните this в reference/variable. Почему:

var vm = new Vue({
  el: '#app',
  data: {
    something: true
  },
  created: function () {
    // You are in first level function,
    // you can use "this"
    axios.get('/uri').then(function () {
      // Here you are in nested, second level
      // function. It will have own "this" object
      this.something = false // no error here but...
      // You did not changed "something" value in data object,
      // you just created new property also called "something",
      // but in this nested "this" object.
    })
  }
})

Как упоминалось выше, каждая функция получает свой собственный объект this. Таким образом, с помощью this.something = false вы только что создали новое свойство "что-то" в объекте 'this' во вложенной функции второго уровня вместо изменения 'this' в функции первого уровня. Другими словами, во вложенной функции вы потеряли ссылку на предыдущую this из функции первого уровня, так как ее содержимое было перезаписано вновь созданным контентом в процессе создания вложенных функций. Итак, если вам нужно использовать функцию уровня this для первого уровня в вложенной функции, просто сохраните ее под другим именем, которое не будет перезаписано:

var vm = new Vue({
  el: '#app',
  data: {
    something: true
  },
  created: function () {
    var anyName = this // Save reference to this under another name
    axios.get('/uri').then(function () {
      this.something = false // no error here, but you are still creating new property
      anyName.something = false // NOW you are really changing "something" value in data
    })
  }
})

Как вы можете видеть, вы можете сохранить его под любым именем. Но следуйте удобству и назовите его как self. Не vm, так как это имя может смутить вас снова, будет ли оно сталкиваться с var vm = new Vue(), или нет. Это не будет, но не смущает себя и просто назовите его self.

Не экспериментируйте со стрелочными функциями, не используйте bind. Просто следуйте этим простым правилам. Позже вы станете более опытными, вы можете (и должны) использовать их, но на данный момент наслаждайтесь кодированием, а не отладкой:)