2023年6月21日发(作者:)

【前端框架】ElementUIDialog组件中执⾏DOM操作异常问题的分析与处理⽂章⽬录1 问题描述使⽤ Element UI dialog 组件,在 dialog 对话框中渲染图表,需要获取图表挂载点的 dom 元素。由于 Element UI 的 dialog_body 是以lazy 模式进⾏渲染,导致 dialog 打开时,图表加载失败!⽰例页⾯

Button

Try Element

element ui dialog 组件提供了 open 事件,在 Dialog 打开时执⾏回调。官⽹对 open 的解释如下Dialog 的内容是懒渲染的,即在第⼀次被打开之前,传⼊的默认 slot 不会被渲染到 DOM 上。因此,如果需要执⾏ DOM 操作,或通过 ref 获取相应组件,请在 open 事件回调中进⾏。可是实际使⽤时,发现并⾮如此。在上例 TAG 1 所处的时间点,实际页⾯如下:

可见,这个时候的页⾯并不完整,是⽆法通过选择器获取 dom 元素的,那么对 “#hello” dom 元素的所有操作都是⽆效的。此时检测到 dialog 组件的状态如下:e // truevm.$children[1].visible // truevm.$children[1].rendered // truevm.$children[1].opened // false2 问题分析el-dialog 组件会监听 visible 的状态,当状态为 true 时,⽴刻触发 open 事件,但这个时候 el-dialog__body 的内容还没有渲染。因为Vue 组件通过 $emit 触发的事件并不是异步执⾏的,⽽是同步执⾏。代码如下: watch: { visible: function(e) { var t = this; e ? ( = !1, this.$emit("open"), this.$ntListener("scroll", Popper), this.$nextTick(function() { t.$Top = 0 }), ToBody && Child(this.$el)) : (this.$EventListener("scroll", Popper), || this.$emit("close")) } }正如官⽅⽂档所说,Dialog 的内容,也就是 el-dialog__body 中的内容是懒加载的!那么它懒加载的机制是什么呢?查看 element dialog 组件的代码,会发现:
Vue 中 v-if 指令的特征是,根据表达式的值在 DOM 中⽣成或移除⼀个元素,如果 rendered 为 false,那么该 dom 元素会被移除,当rendered 为 true 时,对应元素的克隆会被重新插⼊到 DOM 中。 ⽽ v-show 指令则是根据表达式的值来显⽰或隐藏 HTML 元素,并不会移除 DOM 元素。v-if 是惰性的,如果初始渲染时条件为假,则什么也不做,在条件第⼀次变为真时才开始局部编译(编译结果会缓存起来)。这就是 element dialog-body 懒加载的原理。3 解决⽅案3.1 将元素放在 footer slot 中dialog 组件中的 footer slot 是实时渲染的,放在其中的 dom 元素可以直接获取:
Button

Try Element

3.2 在 open 事件中延迟执⾏具体的代码由于浏览器是单线程执⾏前端代码,因此可以短暂的交出控制权,让其进⾏渲染页⾯,然后再执⾏具体的任务。 var vm = new Vue({ el: '#app', data: function() { return { visible: false } }, methods: { handleOpen(){ setTimeout(, 100, "open: "+$("#hello p").html()) // TAG 1 } } })3.3 在 update 回调函数执⾏相关的逻辑当 v-if 为 true 时,会重新渲染页⾯,渲染结束后会触发 update 回调函数,这个时候可以⽤来执⾏⼀些代码。 var vm = new Vue({ el: '#app', data: function() { return {

visible: false, see: true, nosee: false } }, updated: function(){ var dialog = this.$children[1] if(ed && e) { ("update: " + $("#hello p").html()) } } })需要注意的时,要对 dialog 的状态进⾏验证,确保代码的执⾏时机准确⽆误。3.4 主动更改 rendered 的值为 true在 open dialog 之前,先将 rendered 的值改为 true var vm = new Vue({ el: '#app', data: function() { return { visible: false } }, methods: { handleOpen(){ ("open: "+$("#hello p").html()); // TAG 1 } } }) vm.$children[1].rendered = true可能还有更简洁、⾼效的⽅式,知道的朋友可以告知⼀下,相互学习。

2023年6月21日发(作者:)

【前端框架】ElementUIDialog组件中执⾏DOM操作异常问题的分析与处理⽂章⽬录1 问题描述使⽤ Element UI dialog 组件,在 dialog 对话框中渲染图表,需要获取图表挂载点的 dom 元素。由于 Element UI 的 dialog_body 是以lazy 模式进⾏渲染,导致 dialog 打开时,图表加载失败!⽰例页⾯

Button

Try Element

element ui dialog 组件提供了 open 事件,在 Dialog 打开时执⾏回调。官⽹对 open 的解释如下Dialog 的内容是懒渲染的,即在第⼀次被打开之前,传⼊的默认 slot 不会被渲染到 DOM 上。因此,如果需要执⾏ DOM 操作,或通过 ref 获取相应组件,请在 open 事件回调中进⾏。可是实际使⽤时,发现并⾮如此。在上例 TAG 1 所处的时间点,实际页⾯如下:

可见,这个时候的页⾯并不完整,是⽆法通过选择器获取 dom 元素的,那么对 “#hello” dom 元素的所有操作都是⽆效的。此时检测到 dialog 组件的状态如下:e // truevm.$children[1].visible // truevm.$children[1].rendered // truevm.$children[1].opened // false2 问题分析el-dialog 组件会监听 visible 的状态,当状态为 true 时,⽴刻触发 open 事件,但这个时候 el-dialog__body 的内容还没有渲染。因为Vue 组件通过 $emit 触发的事件并不是异步执⾏的,⽽是同步执⾏。代码如下: watch: { visible: function(e) { var t = this; e ? ( = !1, this.$emit("open"), this.$ntListener("scroll", Popper), this.$nextTick(function() { t.$Top = 0 }), ToBody && Child(this.$el)) : (this.$EventListener("scroll", Popper), || this.$emit("close")) } }正如官⽅⽂档所说,Dialog 的内容,也就是 el-dialog__body 中的内容是懒加载的!那么它懒加载的机制是什么呢?查看 element dialog 组件的代码,会发现:
Vue 中 v-if 指令的特征是,根据表达式的值在 DOM 中⽣成或移除⼀个元素,如果 rendered 为 false,那么该 dom 元素会被移除,当rendered 为 true 时,对应元素的克隆会被重新插⼊到 DOM 中。 ⽽ v-show 指令则是根据表达式的值来显⽰或隐藏 HTML 元素,并不会移除 DOM 元素。v-if 是惰性的,如果初始渲染时条件为假,则什么也不做,在条件第⼀次变为真时才开始局部编译(编译结果会缓存起来)。这就是 element dialog-body 懒加载的原理。3 解决⽅案3.1 将元素放在 footer slot 中dialog 组件中的 footer slot 是实时渲染的,放在其中的 dom 元素可以直接获取:
Button

Try Element

3.2 在 open 事件中延迟执⾏具体的代码由于浏览器是单线程执⾏前端代码,因此可以短暂的交出控制权,让其进⾏渲染页⾯,然后再执⾏具体的任务。 var vm = new Vue({ el: '#app', data: function() { return { visible: false } }, methods: { handleOpen(){ setTimeout(, 100, "open: "+$("#hello p").html()) // TAG 1 } } })3.3 在 update 回调函数执⾏相关的逻辑当 v-if 为 true 时,会重新渲染页⾯,渲染结束后会触发 update 回调函数,这个时候可以⽤来执⾏⼀些代码。 var vm = new Vue({ el: '#app', data: function() { return {

visible: false, see: true, nosee: false } }, updated: function(){ var dialog = this.$children[1] if(ed && e) { ("update: " + $("#hello p").html()) } } })需要注意的时,要对 dialog 的状态进⾏验证,确保代码的执⾏时机准确⽆误。3.4 主动更改 rendered 的值为 true在 open dialog 之前,先将 rendered 的值改为 true var vm = new Vue({ el: '#app', data: function() { return { visible: false } }, methods: { handleOpen(){ ("open: "+$("#hello p").html()); // TAG 1 } } }) vm.$children[1].rendered = true可能还有更简洁、⾼效的⽅式,知道的朋友可以告知⼀下,相互学习。