在Vue中,this可以「直接讀取」內層的屬性,原因是因為Vue會在背後自動生成componentInstance,這方式讓使用者可以簡潔性的呼叫(少打字)。
話說這個componentInstance 是誰?
這個 componentInstance(元件實例) 是在 Vue 內部,當執行 .mount() 之後,Vue 框架根據你的元件選項(data(), methods, etc.)自動創建出來的一個複雜 JavaScript 物件,是一個元件的藍圖。當Vue準備渲染你的元件時,它會讀取這些藍圖,並在記憶體中建構出一個包含所有狀態和方法的實際運行時對象,這就是 componentInstance。
定義元件data() { return { message: 'hi' } }。
Vue 內部創建的componentInstance,
componentInstance.message = 'hi'。
定義元件methods: { update() { ... } }
Vue 內部創建的componentInstance,
componentInstance.update = function() { ... }
Vue 在創建這個實例時,做了一件很重要的事:它將 data、methods 等選項中的屬性,提升並直接掛載到了 componentInstance 的頂層。這就是 this 為什麼能直接存取 message 的原因。而componentInstance 是由 Vue 框架在執行階段創建出來的。這個實例將你定義在 data 裡面的屬性message直接拉到最頂層。將你定義在 methods 裡面的方法update直接拉到最頂層。
<div id="counter">
</div>
const Counter = Vue.createApp({
data() {
return {
number: 0
}
},
methods: {
addOne() {
this.number++;
}
}
}).mount("#counter")
</script>
當你寫這段程式碼之後,這時候Vue內部大概會做這樣的操作。
建立一個componentInstance物件。
在物件componentInstance裡面,新增一個屬性number=0、新增一個函式addOne(){this.number++},強制把addOne(){}裡的this設定為componentInstance。
而這時候Counter常數就是一個根組件,也等同於componentInstance。
依照上面範例,如果要執行addOne()。輸入Counter.addOne才是正確的。如果輸入Counter.data.addOne,則會出現錯誤。
因為Vue系統跑完後,並不是把data屬性整個刪除,而是將data()函式回傳物件裡面的屬性(例如 number)和 methods 裡面的函式(例如 addOne),都提升(或稱代理)到 Counter 物件(即元件實例)的最外層。
number 屬性:直接成為 Counter.number。
addOne 方法:直接成為 Counter.addOne。
也就是Vue在內部創建了一個扁平化的元件實例結構。
而data不能使用則是因為,data 和 methods 這兩個選項在元件實例化後,並沒有被保持為一個可供外部直接存取的 Counter.data 或 Counter.methods 物件。Vue 只是在內部使用它們來建構 Counter 實例。
componentInstance 是一個概念性名稱,或者說是 Vue 內部原始碼中用來指代這個物件的變數名稱。它只存在於 Vue 框架的內部作用域。
對於外部的使用者程式碼來說,Vue 框架將這個實例物件回傳給你,你用 Counter 這個變數名來儲存和指稱它。
在上個程式碼範例,執行console.log(Counter.number);畫面會出現0。執行console.log(componentInstance.number);會出錯ReferenceError: componentInstance is not defined。