一個 Vue 地圖元件錯誤引發的思考

lifefriend_007發表於2019-04-02

最近在 Vue 專案裡面編寫一個地圖元件,發現如果把地圖例項掛載到 data 中,會報錯。

程式碼如下:

export default {
    data () {
        return {
            newMap: null
        }
    },
  	mounted () {
        this.$nextTick(e=>{
      		this.newMap = new maptalks.Map(...)
    	})
    }
}
複製程式碼

錯誤資訊如下:

一個 Vue 地圖元件錯誤引發的思考

通過在控制檯中列印 data 中的資料,發現 Vue 給所有的屬性都加上 setter/getter ( chrome 控制檯會直接列印原始的 object,而重寫了 setter/getter 的 object 將會用 "(...)" 代替 )。這個時候恍然大悟,原來 Vue data 中會重寫變數的 getter/setter,上面的錯誤就出現在 Vue 重寫地圖例項物件的 getter/setter 時。Vue 重寫 object 的 setter/getter 是為了跟蹤物件,實現響應式。地圖例項這樣的複雜物件沒有必要跟蹤,以下是幾種可以避免 Vue 的 walk 過程的解決方案。

一、Object.freeze

地圖例項仍然掛到 data 上但是使用 Object.freeze 來告訴 Vue 不要監聽

...
let map = new maptalks.Map(...)
this.newMap = Object.freeze(map)
複製程式碼

此方法可以避免 Vue 的 walk 錯誤,但是,我們儲存地圖例項到 data 中是為了方便後續呼叫,如果使用了 Object.freeze ,後面對地圖例項操作如:

this.newMap.setCursor('crosshair')
複製程式碼

會報錯,錯誤資訊如下:

一個 Vue 地圖元件錯誤引發的思考

二、定義成區域性變數,放到外面

let newMap = null
export default {
    data () {
        return {
        }
    },
  	mounted () {
        this.$nextTick(e=>{
      		newMap = new maptalks.Map(...)
    	})
    }
}
複製程式碼

此方法可以避免 Vue 的 walk 錯誤,但如果元件有多個例項的話, 會共享同一個 newMap,和之前掛在 data 上的邏輯並不完全等價。

三、變數名以 _ 開頭

export default {
    data () {
        return {
            /* eslint-disable*/
            _newMap: null
            /* eslint-enable */
        }
    },
  	mounted () {
        this.$nextTick(e=>{
      		this._newMap = new maptalks.Map(...)
    	})
    }
}
複製程式碼

此方法可以避免 Vue 的 walk 錯誤,同時不影響後面對地圖例項進行操作,此方案為本次選用的方案。

四、不預先定義變數,直接掛載到 this 上

export default {
    data () {
        return {
        }
    },
  	mounted () {
        this.$nextTick(e=>{
      		this.newMap = new maptalks.Map(...)
    	})
    }
}
複製程式碼

此方法也可以避免 Vue 的 walk 錯誤,同時也不影響後面對地圖例項進行操作,但本人喜歡在 Vue 中變數都定義到 data 中,便於管理。

相關文章