如何修復Vue中的 “this is undefined” 問題

前端小智發表於2020-03-17

作者:Michael Thiessen 譯者:前端小智 來源:techalyst

點贊再看,養成習慣

本文 GitHub github.com/qq449245884… 上已經收錄,更多往期高贊文章的分類,也整理了很多我的文件,和教程資料。歡迎Star和完善,大家面試可以參照考點複習,希望我們一起有點東西。


當我們使用 Vue 在愉快的開發專案的時候,突然報了一個錯誤:

this is undefined
複製程式碼

別擔心,不只有你一個人,我也經常遇到這個問題很多次,接下我們一起來看看如何解決這個問題。

一個可能的原因是混淆了常規函式和箭頭函式的用法,如果你遇到這個問題,我猜你用的是箭頭函式。如果用常規函式替換箭頭函式,它可能會為你修復這個問題。

我們再深入一點,試著理解為什麼會這樣。

畢竟,知識就是力量,如果知道造成問題的原因,那麼我們將來可以避免很多挫敗感和時間浪費。

還有一些其它原因可能也會出現此類錯誤。

  • 使用 fetchaxios 獲取資料
  • 使用像 lodashunderscore 這類的庫

理解兩種主要的函式型別

在 JS 中,我們有兩種不同的函式。它們以幾乎相同的方式運作,除了它們處理變數的方式不同。

這給新舊Javascript開發人員帶來了很多困惑,但是當我們弄懂這個問題時,就很好會有這個困惑。

常規函式

常規函式可以用幾種不同的方式定義。

第一種方法在 Vue 元件中較不常見,因為寫出來要更長一些:

methods: {
  regularFunction: function() {
    // Do some stuff
  }
}
複製程式碼

第二種方法是簡寫方式,我們也經常使用:

methods: {
  shorthandFunction() {
    // Do some stuff
  }
}
複製程式碼

在像這樣的常規函式中,this將引用函式的“所有者”。因為我們是在Vue元件上定義它的,所以this指的是Vue元件。

在大多數情況下,我們應該在 Vue 中使用常規函式,特別是在建立時

  • methods
  • computed props
  • watched props

雖然常規函式通常是我們所需要的,但是箭頭函式也非常方便。

箭頭函式

箭頭函式可以更短,更快的編寫,因此最近獲得了廣泛的歡迎。但是,它們在物件上定義方法時並沒有太大的不同,就像我們在編寫Vue元件時所做的那樣。

這是他們在Vue元件上的樣子:

methods: {
  arrowFunction: () => {
    // Do some stuff
  }
}
複製程式碼

在處理 this 問題時,真正的差異開始發揮作用。

箭頭函式採用詞法作用域,意味著箭頭函式從它的上下文中獲取this

如果試圖從Vue元件上的箭頭函式內部訪問 this,將得到一個錯誤,因為 this 不存在

data() {
  return {
    text: 'This is a message',
  };
},
methods: {
  arrowFunction: () => {
    console.log(this.text);  // ERROR! this is undefined
  }
}
複製程式碼

簡而言之,儘量避免在Vue元件上使用箭頭函式。這將會省去許多頭痛和困惑的問題。

有時使用箭頭函式是很好的,但這隻在不引用this的情況下才有效。

computed: {
  location: () => window.location,
}
複製程式碼

現在我們知道兩種主要的函式型別,如何正確使用它們?

匿名函式

當我們只需要建立一個函式而不需要從其他任何地方呼叫它時,匿名函式非常有用。

下面是使用匿名函式的一些場景

  • 使用 axiosfetch 訪存資料
  • filtermapreduce等函式方法
  • 在 Vue 方法中的任何地方

來個例子看一下:

// Fetching data
fetch('/getSomeData').then((data) => {
  this.data = data;
});

// Functional methods
const array = [1, 2, 3, 4, 5];
const filtered = array.filter(number => number > 3);
const mapped = array.map(number => number * 2);
const reduced = array.reduce((prev, next) => prev + next);
複製程式碼

從示例中可以看到,大多數情況下,當我們建立匿名函式時,使用箭頭函式。我們通常使用箭頭函式有幾個原因

  • 更短、更簡潔的語法
  • 改善可讀性
  • this 取自父類

在Vue方法中,箭頭函式也可以作為匿名函式使用。

等等,我們不是剛發現當我們試圖訪問 this 時,箭頭函式不起作用嗎?

這就是區別所在。

當我們在常規函式或簡寫函式中使用箭頭函式時,常規函式將this設定為我們的Vue元件,而箭頭函式則不一樣。

來個例子:

data() {
  return {
    match: 'This is a message',
  };
},
computed: {
  filteredMessages(messages) {
    console.log(this); // Vue
    
    const filteredMessages = messages.filter(
      // 引用我們的Vue元件
      (message) => message.includes(this.match)
    );
    
    return filteredMessages;
  }
}
複製程式碼

filter方法可以訪問this.match,因為箭頭函式使用的方法與filteredMessages方法使用的上下文相同。 由於此方法是常規函式(而不是箭頭函式),因此將其自身的上下文設定為Vue例項。

讓我們進一步討論如何使用axiosfetch來獲取資料。

在獲取資料時使用正確的函式

如果正在使用fetchaxios獲取非同步資料,最好使用 PromisePromise喜歡匿名箭頭函式,它們也使處理this問題變得容易得多。

如果你正在獲取一些資料並想在你的元件上設定它,這是你應該做的正確的方式:

export default {
  data() {
    return {
      dataFromServer: undefined,
    };
  },
  methods: {
    fetchData() {
      fetch('/dataEndpoint')
        .then(data => {
          this.dataFromServer = data;
        })
        .catch(err => console.error(err));
    }
  }
};
複製程式碼

請注意,我們如何在 Vue 元件上使用常規函式作為方法,然後在 Promise 內部使用匿名箭頭函式

.then(data => {
  this.dataFromServer = data;
})
複製程式碼

fetchData()作用域內,我們將this設定為Vue元件,因為它是一個常規函式。由於箭頭函式使用外部作用域作為它們自己的作用域,因此箭頭函式也將this設定為我們的Vue元件。

這允許我們通過this訪問 Vue 元件並更新dataFromServer

但是,如果需要將函式傳遞幫助庫,比如lodashunderscore,該怎麼辦呢

與 Lodash 或 Underscore 一起使用

假設我們的Vue元件上有一個要使用LodashUnderscore方法。如何防止this is undefine的錯誤。

如果你用過 React ,你可能見過類似的東西。

這是我們用Vue做的。

created() {
  this.methodToDebounce = _.debounce(this.methodToDebounce, 500);
},
methods: {
  methodToDebounce() {
    // Do some things here
  }
}
複製程式碼

就是這樣!

我們要做的就是獲取函式,將其包裝在debounce函式中,然後返回一個內建了debounce的新函式。現在,當我們在Vue元件上呼叫this.methodToDebounce()時,我們將呼叫debounced版本。

什麼是詞法作用域

如前所述,常規函式和箭頭函式之間存在差異的主要原因與詞法作用域有關。來分析一下它的含義。

首先,作用域是程式中存在變數的任何區域。在Javascript中,window 變數具有全域性作用域,它在任何地方都可用。儘管大多數變數被限制在定義它們的函式、它們所屬的類或模組中。

其次,單詞“詞法”僅僅意味著作用域由你如何編寫程式碼決定。某些程式語言只在程式執行時才確定作用域內的內容。這可能會讓人很困惑,所以大多數語言都只使用詞法作用域。

箭頭函式使用詞法作用域,而常規函式和簡寫函式不使用。

這裡最棘手的部分是詞法作用域如何在函式中影響 this。對於箭頭函式,this與外部作用域的this繫結在一起。常規函式的this繫結方式有些奇怪,這就是引入箭頭函式的原因,也是為什麼大多數人儘可能多地使用箭頭函式的原因。

作用域如何在函式中工作

下面是一些示例,它們演示了作用域如何在這兩種函式型別之間以不同的方式工作

// 此變數在 window 作用域內
window.value = 'Bound to the window';

const object = {
  // 此變數在 object 作用域內
  value: 'Bound to the object',
  arrowFunction: () => {
    console.log(this.value); // 'Bound to the window'
  },
  regularFunction() {
    console.log(this.value);  // 'Bound to the object'
  }
};
複製程式碼

將作用域繫結到函式上

我們可以使用 bind 方法來改變 this 的繫結

const boundFunction = unboundFunction.bind(this);
複製程式碼

這使我們在編寫Vue元件時具有更大的靈活性,更輕鬆地重用方法。當然,可讀性相對差點,應該儘量避免太頻繁地使用它。


程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文: www.techalyst.com/posts/vuejs…


交流

文章每週持續更新,可以微信搜尋「 大遷世界 」第一時間閱讀和催更(比部落格早一到兩篇喲),本文 GitHub github.com/qq449245884… 已經收錄,整理了很多我的文件,歡迎Star和完善,大家面試可以參照考點複習,另外關注公眾號,後臺回覆福利,即可看到福利,你懂的。

如何修復Vue中的 “this is undefined” 問題

相關文章