Vuetify元素内$ref失效求助:轮播项自定义HTML渲染异常
问题根源
Vuetify的v-carousel默认做了懒加载优化:仅当前活跃的轮播项会被渲染到DOM中,非活跃项的DOM节点处于未挂载状态。这就导致除第一个轮播项外,其他组件在mounted钩子执行时,<div ref="carousel_html">还未被创建,$refs.carousel_html为undefined,触发设置innerHTML的报错。
解决方案
方案1:用v-html指令替代手动DOM操作(推荐)
直接使用Vue内置的v-html指令绑定解码后的HTML内容,Vue会自动在DOM节点存在时完成渲染,无需手动操作DOM:
<template> <v-carousel-item> <v-img :lazy-src="item.image.sizes.landing_carousel_bg_xs" :src="item.image.sizes.landing_carousel_bg_fhd"> <!-- 用v-html直接绑定解码后的HTML --> <div style="height: 100%" class="html" v-html="decodedHtml"></div> </v-img> </v-carousel-item> </template> <script> export default { props: ["item"], computed: { decodedHtml() { return Base64.decode(this.item.html) } } } </script>
方案2:监听轮播项活跃状态,按需渲染
如果必须手动操作DOM,可以通过父组件的活跃索引,给子组件传递active状态,在组件变为活跃时再执行DOM赋值:
子组件(LandingPageCarouselItem.vue)修改:
<template> <v-carousel-item> <v-img :lazy-src="item.image.sizes.landing_carousel_bg_xs" :src="item.image.sizes.landing_carousel_bg_fhd"> <div ref="carousel_html" style="height: 100%" class="html"></div> </v-img> </v-carousel-item> </template> <script> export default { props: ["item", "isActive"], data: () => ({ html: undefined, }), created() { this.html = Base64.decode(this.item.html) }, watch: { isActive(newVal) { if (newVal) { this.$nextTick(() => { this.$refs.carousel_html?.innerHTML = this.html }) } } }, mounted() { if (this.isActive) { this.$nextTick(() => { this.$refs.carousel_html.innerHTML = this.html }) } } } </script>
父组件(LandingPage.vue)修改:
<v-carousel v-model="activeIndex"> <landing-page-carousel-item v-for="(item,i) in page_data.front_page.main_view.carousel_items" :key="i" :item="item" :is-active="activeIndex === i"> </landing-page-carousel-item> </v-carousel> <script> export default { data() { return { activeIndex: 0 } } } </script>
方案3:用v-intersect检测元素可见性
利用Vuetify的v-intersect指令,在轮播项DOM进入视口时再执行渲染:
<template> <v-carousel-item> <v-img :lazy-src="item.image.sizes.landing_carousel_bg_xs" :src="item.image.sizes.landing_carousel_bg_fhd"> <div ref="carousel_html" style="height: 100%" class="html" v-intersect="handleRender"> </div> </v-img> </v-carousel-item> </template> <script> export default { props: ["item"], data: () => ({ html: undefined, rendered: false // 避免重复渲染 }), created() { this.html = Base64.decode(this.item.html) }, methods: { handleRender(entries) { if (entries[0].isIntersecting && !this.rendered) { this.$refs.carousel_html.innerHTML = this.html this.rendered = true } } } } </script>
内容的提问来源于stack exchange,提问作者sal-k
相关产品推荐
相关产品推荐

