最後幾天來聊一下關於生命週期的事情,這裡提到的生命週期並不限於 Vue 所屬的區域,也包含了一些原生 JavaScript 那種無關 Bug 而是一種 Feature 的那些 生命週期 的事情。
沒有被 JS 婊過就不算寫過 JS 你說是吧( 並沒有這種說法好嗎 )
我之前犯過幾個低級錯誤,舉個例子給大家看個笑話:
<template>
<section class="hello">
<h1 v-if="window.location.href === '/'">Homepage</h1>
<h1 v-else>Other pages</h1>
</section>
</template>
以上是錯誤的範例,至於看不出來哪裡有錯的人 左轉不送謝謝。就字面上來說每個字都對,寫法也沒有錯誤,不過,就 Vue 而言,你會獲得一個 window
是 undefined
的結果。至於原因嘛,在 Vue 樣版作用域當中,裡面所有的變數是 明確指向該物件 的作用域裡面,也就是說,最終都會指回該物件的 this
本身。
所以,那上面的
window
並不會是你以為的window
物件。
另外一個關於封裝的低級錯誤:
let myOptions = {}
export default {
name: 'component',
data () {
return {
options: myOptions
}
}
}
以上的寫法也沒有任何錯誤、或是會讓人覺得奇怪的地方。但,錯誤會發生在 這個物件被重用( Reuse )的時候 。我在 第 4 天 有提到會被污染的事情,另外在 第 12 天 也提到了一些生命週期的奇妙狀況。
對於 Webpack 最終封裝的結果來說,你只要是放在 export default {}
外面的事情,最好都可以當他是一個 小規模的全域變數 來看待。如果還是不知道我在講什麼,為什麼這樣會有錯的人,請複習一下 Kuro 的文章:
歐,你不要以為剛剛的 let myOptions = {}
改成 const myOptions = {}
就會沒事,如果認為會沒事的人 一樣左轉不送謝謝 ,你可以參考一下 Object.freeze() 的相關文件。
犯蠢的事情很多,不過就一些小地方來說,還是得自己留意一下才行。通常不會是因為你 寫法 上面的錯誤,多數狀況是 那些東西 被 寫在哪裡 或是說被 應用在哪裡 的問題。終究你還是得摸清楚你所使用的框架,是不是有什麼需要留意的地方。
終究你可能會學到一件事情,就是那些讓人煩躁的生命週期,最終都是用來浪費生命的(欸)。我舉一個有趣的例子,是關於生命週期與原生 JS 混合的案例:
<template>
<section>
<img class="previewer" :src="previewSrc" />
<input type="file" @change.prevent="previewFile($event)" />
</section>
</template>
export default {
name: 'component',
data () {
return {
previewSrc: ''
}
},
methods: {
previewFile (evt) {
let fileReader = new window.FileReader()
fileReader.readAsDataURL(evt.target.files[0])
fileReader.onload = event => {
const preview = this.$el.querySelector('.previewer')
preview.addEventListener('load', () => {
// 中間就不贅述了。
}, false)
this.previewSrc = event.target.result
}
}
}
}
上述的例子混合了 Vue 的方法與在原生 JavaScript 操作的 onload
方法。但為何會說這樣的方式會出問題呢?原因在於 前端渲染 這件事,倘若你的操作並不會 重繪 所有的 DOM 結構樹的話,那麼,對於瀏覽器與 JavaScript 來說,那些 DOM 物件與其被綁定的事件,就依舊會在同樣的 位置 上。
這裡的 位置 也包含了記憶體位址。但是,請注意上面例子,同樣都是使用 讀取 的事件,有一個是使用
onload
來指定,而另外一個則使用addEventListener
來指定,這會造成一些意想不到的錯誤。
那麼,當你重複的操作剛剛的檔案上傳動作時,會有以下這樣的結果:
操作 3 次選擇檔的動作,然後你會發現 Preview 的事件被累加了,而 FileReader 的卻維持正常。主要的差異在於,雖然都是屬於匿名函式,但是 addEventListener
所接受的匿名函式,在每次運作的時候,都會是 新的位置 ,而對於 onload
來說,他只是原有物件屬性,被做了一個 賦值 的動作,所以,兩者所參考的記憶體位置是不同的。
所以,不是每個地方都雞婆的用上
addEventListener
就自以為很會寫 JS 好像很潮。
上述的狀況,如果再加上 updated
這個生命週期來看,那麼,他理所當然的會被夾在 FileReader 與 Preview 中間。不過,倘若你的圖片 src
是用 preview.src
來賦予值的話,那麼你的 updated
不會被呼叫是很合理的,對於 Vue 來說,你自己操作 DOM 跟他無關,所以只要是虛擬節點沒有任何改變,自然就不會呼叫 updated
。
其實 JavaScript 寫到最後都會懷疑人生,這是一個階段不用太灰心(欸)。