最近,在开发的时候遇到一个问题,让我对vue中nextTick()的用法加深了了解~
下面是在组件中引用的一个拖拽的组件:
<vue-draggable-resizable class="drag-img" :w="coordinate[0].width" :h="coordinate[0].height" :x="coordinate[0].abs" :y="coordinate[0].ord" @dragging="onDragAvator" @resizing="onResizeAvator" @dragstop="onDragstopAvator" @onDragStart="onDragStartAvator" :min-width="50" :min-height="50" :handles="['tl','tr','br','bl']" :lock-aspect-ratio="true" :parent="true"> <img @click="setAvatorDafault" src="@/assets/img/icon_touxiang@2x.png" alt=""> </vue-draggable-resizable>
这个拖拽组件的横坐标和纵坐标、宽高是由data的一个数据控制的:
data() { return { coordinate:[{ 50, height: 50, abs: 10, ord: 10 }] } }
在dragging和resizing的过程中,这个数据和dom的操作应该都是双向绑定的:
onResizeAvator: function (x, y, width, height) { this.coordinate[0].abs = x this.coordinate[0].ord = y }, onDragAvator: function (x, y) { this.coordinate[0].abs = x this.coordinate[0].ord = y },
但是,在编辑回显的时候,需要将异步获取的数据赋给coordinate,再显示到页面。
现在问题来了,当数据coordinate被赋值成异步获取的数据后,页面中的拖拽组件的宽高并没有变化,这是为什么?
methods: { getDefaultData() { let that = this axios.get(this.$store.state.marketinghost + '/fission/task/get/bycode?onlischoolCode=' + this.onlischoolCode + '&taskCode=' + this.$route.query.id) .then(res => { if(res.data.code == "1") { var data = res.data.data if (data.components.length) { that.coordinate = data.components } } }) .catch((err) => { Promise.resolve(err); }) } } mounted() { this.getDefaultData() }
加了一个变量控制拖拽组件后,组件DOM会强制性更新,宽高就变成回显时候的值了
<vue-draggable-resizable
class="drag-img"
v-if="!editLoading"
:w="coordinate[0].width"
:h="coordinate[0].height"
:x="coordinate[0].abs"
:y="coordinate[0].ord"
@dragging="onDragAvator"
@resizing="onResizeAvator"
@dragstop="onDragstopAvator"
@onDragStart="onDragStartAvator"
:min-width="50"
:min-height="50"
:handles="['tl','tr','br','bl']"
:lock-aspect-ratio="true"
:parent="true">
<img @click="setAvatorDafault" src="@/assets/img/icon_touxiang@2x.png" alt="">
</vue-draggable-resizable>
data() {
return {
editLoading: false,
coordinate:[{
50,
height: 50,
abs: 10,
ord: 10
}]
}
}
methods: {
getDefaultData() { let that = this that.editLoading = true axios.get(this.$store.state.marketinghost + '/fission/task/get/bycode?onlischoolCode=' + this.onlischoolCode + '&taskCode=' + this.$route.query.id) .then(res => { if(res.data.code == "1") { var data = res.data.data if (data.components.length) { that.coordinate = data.components that.$nextTick(() => { that.editLoading = false }) } } }) .catch((err) => { Promise.resolve(err); }) }
}
mounted() {
this.getDefaultData()
}
为了更加了解nextTick(),下面是vue官网关于异步更新队列的解释:
https://cn.vuejs.org/v2/guide/reactivity.html#%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0%E9%98%9F%E5%88%97
总之,当你设置 vm.someData = 'new value'
,该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新,为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)
:
Vue.component('example', { template: '<span>{{ message }}</span>', data: function () { return { message: '未更新' } }, methods: { updateMessage: function () { this.message = '已更新' console.log(this.$el.textContent) // => '未更新' this.$nextTick(function () { console.log(this.$el.textContent) // => '已更新' }) } } })
因为nextTick()返回的事一个Promise对象,所以也可以写成async/await的方式:
methods: { updateMessage: async function () { this.message = '已更新' console.log(this.$el.textContent) // => '未更新' await this.$nextTick() console.log(this.$el.textContent) // => '已更新' } }