问题描述
我正在尝试在模板安装后运行一个函数,无论是计算还是观察或观察效果。
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
。
-
创建一个使用
MutationObserver
调用回调的函数:const observeMutations = (targetNode,callback) => { const config = { attributes: true,childList: true,subtree: true } const observer = new MutationObserver(callback) observer.observe(targetNode,config) return observer }
-
在
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) }) })
-
在
onUnmounted
钩子中,断开观察者作为清理:onUnmounted(() => observer?.disconnect())
澄清一下: 您正在尝试将模板引用传递给您的函数,但在您执行逻辑时它总是为空,对吗?
您可以简单地在您的 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 的元素),您可以开始对其应用实际逻辑。