英文:
How and when does Vue store data in its underlying elements?
问题
I am attempting to make use of the dataset
attributes of elements, to enable data to be transferred from one item to another during a drag/drop process. But I am having some odd results depending on how Vue's v-if
condition is used. The code below works as expected: if you click the 'move' button, then look at the underlying elements' datasets using the 'inspect' button, the dataset for the second item changes...
<script setup>
import { onMounted, ref, reactive } from 'vue'
const thisData = reactive(['AAA', 'BBB', 'CCC', 'DDD', 'EEE'])
var items = [];
var moves = 0;
onMounted(() => {
items = document.querySelectorAll('.swappable');
inspect();
});
function inspect() {
for (let n = 0; n < 5; n++) {
let data = items[n].dataset.transfer;
const elem = document.getElementById(idString(n + 100));
elem.innerText = 'id: ' + items[n].id + ':' + data;
}
}
function move() {
thisData[1] = thisData[0];
++moves;
}
function localTest(x) {
return x > 2;
}
function globalTest(chip) {
return moves > 0;
}
function idString(n) {
return 'btn' + n;
}
function dataTransfer(n) {
return 'data stored: ' + n;
}
</script>
<template>
<template v-for="(item, n) in thisData" :key="n">
<button v-if="localTest(n)" style="background-color: lightblue;" class="swappable" :data-transfer="dataTransfer(item)" :id="idString(n)">{{ item }}</button>
<button v-else style="background-color: lightgreen;" class="swappable" :data-transfer="dataTransfer(item)" :id="idString(n)">{{ item }}</button>
<p :id="idString(n + 100)"></p>
</template>
<button @click="inspect()">Inspect Element Data</button>
<p></p>
<button @click="move()">Copy btn 1 to btn 2</button>
</template>
But if the v-if
test is changed to globalTest(n)
instead of localTest(n)
, the dataset does not change. My app needs to use a test using a 'global' (a reactive one in fact) but I also need to access the changed dataset.
The effect can be seen here, by editing the v-if
test.
英文:
I am attempting to make use of the dataset
attributes of elements, to enable data to be transferred from one item to another during a drag/drop process. But I am having some odd results depending on how Vues' v-if
condition is used. The code below works as expected: if you click the 'move' button, then look at the underlying elements' datasets using the 'inspect' button, the dataset for the second item changes.....
<script setup>
import { onMounted,ref,reactive } from 'vue'
const thisData = reactive(['AAA','BBB','CCC','DDD','EEE'])
var items = [];
var moves = 0;
onMounted(() => {
items= document.querySelectorAll('.swappable') ;
inspect();
});
function inspect() {
for (let n=0; n < 5; n++) {
let data = items[n].dataset.transfer;
const elem = document.getElementById(idString(n+100));
elem.innerText = 'id: ' + items[n].id + ' :' + data ;
}
}
function move() {
thisData[1] = thisData[0];
++moves;
}
function localTest(x) {
return x > 2;
}
function globalTest(chip) {
return moves > 0;
}
function idString(n) {
return 'btn'+n;
}
function dataTransfer(n) {
return 'data stored: '+n;
}
</script>
<template>
<template v-for="(item,n) in thisData" :key="n" >
<button v-if="localTest(n)" style="background-color: lightblue;" class="swappable"
:data-transfer = dataTransfer(item)
:id=idString(n)
> {{ item }}
</button>
<button v-else style="background-color: lightgreen;" class="swappable"
:data-transfer = dataTransfer(item)
:id=idString(n)
> {{ item}}
</button>
<p :id=idString(n+100) > </p>
</template>
<button @click="inspect()">Inspect Element Data</button >
<p></p>
<button @click="move()">Copy btn 1 to btn 2</button >
</template>
... but if the v-if
test is changed to globalTest(n)
instead of localTest(n)
, the dataset does not change.
My app needs to use a test using a 'global' (a reactive one in fact) but I also need to access the changed dataset.
The effect can be seen here, by editing the v-if
test.
答案1
得分: 1
检查使用浏览器工具箱查看结果页面表明,即使使用 globalTest
,元素也会正确更新。
问题在于你正在使用对已分离元素的引用,这显然不会更新。在使用Vue时,不应将元素存储在简单的数组中,因为Vue有时会更改并替换组件更新时的元素,导致你存储的引用变得过时。
相反,你应该使用模板引用:
> ref
是一个特殊属性,类似于 v-for
章节中讨论的 key
属性。它允许我们在挂载后获得对特定DOM元素或子组件实例的直接引用。
在你的情况下,我们可以用 var items = ref([])
替换 var items = []
,并将对 items
的所有其他引用替换为 items.value
,然后从 onMounted
中移除 items= document.querySelectorAll('.swappable');
。
我们还应该在按钮中添加 ref="items"
。
这解决了问题,现在当Vue替换元素时,引用会自动更新。
这是修复后的代码:
<script setup>
import { onMounted,ref,reactive } from 'vue'
const thisData = reactive(['AAA','BBB','CCC','DDD','EEE'])
var items = ref([]);
var moves = 0;
onMounted(() => {
inspect();
});
function inspect() {
for (let n=0; n < 5; n++) {
let data = items.value[n].dataset.transfer;
const elem = document.getElementById(idString(n+100));
elem.innerText = 'id: ' + items.value[n].id + ' :' + data ;
}
}
function move() {
thisData[1] = thisData[0];
++moves;
}
function localTest(x) {
return x > 2;
}
function globalTest(chip) {
return moves > 0;
}
function idString(n) {
return 'btn'+n;
}
function dataTransfer(n) {
return 'data stored: '+n;
}
</script>
<template>
<template v-for="(item,n) in thisData" :key="n" >
<button v-if="globalTest(n)" style="background-color: lightblue;" class="swappable"
:data-transfer = dataTransfer(item)
:id=idString(n)
ref="items"
> {{ item }}
</button>
<button v-else style="background-color: lightgreen;" class="swappable"
:data-transfer = dataTransfer(item)
:id=idString(n)
ref="items"
> {{ item}}
</button>
<p :id=idString(n+100) > </p>
</template>
<button @click="inspect()">Inspect Element Data</button >
<p></p>
<button @click="move()">Copy btn 1 to btn 2</button >
</template>
英文:
Inspecting the resulting page using the browser toolbox shows that the elements are updating correctly even when using globalTest
.
The problem is that you are using references to detached elements, which obviously don't update. You shouldn't store elements in a simple array when using Vue, as Vue sometimes changes and replaces elements when updating the component, causing the references you stored to become obsolete.
Instead, you should use template refs:
> ref
is a special attribute, similar to the key
attribute discussed in the v-for
chapter. It allows us to obtain a direct reference to a specific DOM element or child component instance after it's mounted.
In your case, we can replace var items = []
with var items = ref([])
and all other references to items
with items.value
, then remove items= document.querySelectorAll('.swappable') ;
from onMounted
.
We should also add ref="items"
to the button.
This resolves issue, as now the references are automatically updated when Vue replaces an element.
Here is the fixed code:
<script setup>
import { onMounted,ref,reactive } from 'vue'
const thisData = reactive(['AAA','BBB','CCC','DDD','EEE'])
var items = ref([]);
var moves = 0;
onMounted(() => {
inspect();
});
function inspect() {
for (let n=0; n < 5; n++) {
let data = items.value[n].dataset.transfer;
const elem = document.getElementById(idString(n+100));
elem.innerText = 'id: ' + items.value[n].id + ' :' + data ;
}
}
function move() {
thisData[1] = thisData[0];
++moves;
}
function localTest(x) {
return x > 2;
}
function globalTest(chip) {
return moves > 0;
}
function idString(n) {
return 'btn'+n;
}
function dataTransfer(n) {
return 'data stored: '+n;
}
</script>
<template>
<template v-for="(item,n) in thisData" :key="n" >
<button v-if="globalTest(n)" style="background-color: lightblue;" class="swappable"
:data-transfer = dataTransfer(item)
:id=idString(n)
ref="items"
> {{ item }}
</button>
<button v-else style="background-color: lightgreen;" class="swappable"
:data-transfer = dataTransfer(item)
:id=idString(n)
ref="items"
> {{ item}}
</button>
<p :id=idString(n+100) > </p>
</template>
<button @click="inspect()">Inspect Element Data</button >
<p></p>
<button @click="move()">Copy btn 1 to btn 2</button >
</template>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论