挂载vue后如何更新模板引用

问题描述

我正在尝试在模板安装后运行一个函数,无论是计算还是观察或观察效果。 watcheffect 只执行一次,计算当然在安装之前运行。 我在 watchEffect 中尝试过 flush: 'post' 并在 watch 中刷新,但我被困住了。 查看它应该按预期工作的文档: https://v3.vuejs.org/guide/composition-api-template-refs.html#watching-template-refs

因此,使用模板引用的观察者应该使用 flush: 'post' 选项来定义。这将在 DOM 更新后运行效果,并确保模板引用与 DOM 保持同步并引用正确的元素。

app.vue

<template>
  <div ref="target">
    <h1>my title</h1>
    <p>Tenetur libero aliquam at distinctio.</p>
    <h1>hello</h1>
    <p class="fuckyeah yolo">quia nam voluptatem illum ratione ipsum.</p>
    <img src="img.jpg" />
    " title="hello" alt />
    <h2>hello</h2>
    <ol>
      <li>hello inital</li>
      <li v-for="i in inc">hello</li>
    </ol>
  </div>
  <div>
    <button @click="inc++">inc</button>
  </div>
  <pre>
  <code>
    {{ toJson }}
  </code>
</pre>
</template>

<script>
import { ref } from '@vue/reactivity'
import { templateRef } from '@vueuse/core'
import { useParser } from './markcomposable.js'
import { onMounted,computed,watchEffect } from '@vue/runtime-core';

export default {
  setup() {

    const inc = ref(0);

    const target = ref(null);


    const { toJson } = useParser(target);


    return {
      inc,target,toJson
    }
  }
}
</script>

//composable.js

import { parse,validate } from "fast-xml-parser"
import { ref,reactive,watchEffect,toRef,nextTick } from 'vue'

const useParser = (target) => {


    const toJson = ref(null);


    const jsonoptions = reactive({
        //defaults
        attributeNamePrefix: "",ignoreAttributes: false,textNodeName: "text",arrayMode: true
    })

    const dumpJson = (target,options) =>
        validate(target.outerHTML) ? parse(target.outerHTML,options) : isValid.value;


    watchEffect(() => {
        if (target.value) {
            toJson.value = dumpJson(target.value,jsonoptions)
            console.log(toJson.value)
        }
    },{
        flush: 'post',})

    return {
        target,toJson,}

}

export { useParser }

解决方法

如果我理解正确,您正在尝试观察模板引用的 outerHTML,并且您希望在插入节点时(通过按钮回调)调用模板引用观察器,但是它只被调用一次。

发生这种情况是因为观察者实际上只观察模板引用而不是它的属性。仅当使用组件/元素引用初始化模板引用时才会调用观察器。模板引用无法重新分配,因此不会再次调用观察者。此外,模板 ref 的属性不是响应式的,因此如果目标节点的 HTML 更改,则不会调用观察器。

解决方案

代替观察者,使用 MutationObserver 观察对目标节点所做的更改,包括 outerHTML

  1. 创建一个使用 MutationObserver 调用回调的函数:

    const observeMutations = (targetNode,callback) => {
      const config = { attributes: true,childList: true,subtree: true }
      const observer = new MutationObserver(callback)
      observer.observe(targetNode,config)
      return observer
    }
    
  2. onMounted hook 中,使用该函数观察 target.value 中的模板引用,传递设置 toJson.value 的回调:

    let observer = null
    
    onMounted(() => {
      toJson.value = dumpJson(target.value.outerHTML,jsonOptions)
    
      observer = observeMutations(target.value,() => {
        toJson.value = dumpJson(target.value.outerHTML,jsonOptions)
      })
    })
    
  3. onUnmounted 钩子中,断开观察者作为清理:

    onUnmounted(() => observer?.disconnect())
    

demo

,

澄清一下: 您正在尝试将模板引用传递给您的函数,但在您执行逻辑时它总是为空,对吗?

您可以简单地在您的 composable.js 文件中使用 onMounted(() => {}) 钩子您可以实现 templateRef(您已经尝试从它的外观中包含)和until(https://vueuse.org/shared/until/#usage)。

因此,您将执行 const target = ref(null) 而不是 const target = templateRef('target',null) 并将其传递给您的 composable.js。

您会在那里观看直到 ref 为真。 所以在你的实际逻辑之前,你会这样做:

await until(unrefElement(target)).toBeTruthy() 之后 ref 应提供一个实际元素(使用 unrefElement 获取 templateRef 的元素),您可以开始对其应用实际逻辑。