一个 Vue 地图组件错误引发的思考

706 阅读1分钟

最近在 Vue 项目里面编写一个地图组件,发现如果把地图实例挂载到 data 中,会报错。

代码如下:

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

错误信息如下:

通过在控制台中打印 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')

会报错,错误信息如下:

二、定义成局部变量,放到外面

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 中,便于管理。