Vue如何在其底层元素中存储数据以及何时存储?

huangapple go评论59阅读模式
英文:

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.....

&lt;script setup&gt;
import {  onMounted,ref,reactive } from &#39;vue&#39;
const thisData = reactive([&#39;AAA&#39;,&#39;BBB&#39;,&#39;CCC&#39;,&#39;DDD&#39;,&#39;EEE&#39;])
var items = [];
var moves = 0;
onMounted(() =&gt; {
items= document.querySelectorAll(&#39;.swappable&#39;) ;
inspect();
});
function inspect() {
for (let n=0; n &lt; 5; n++)  {
let data = items[n].dataset.transfer;
const elem = document.getElementById(idString(n+100));
elem.innerText = &#39;id: &#39; + items[n].id + &#39; :&#39; + data ;
}
}
function move() {
thisData[1] = thisData[0];
++moves;
}
function localTest(x) {
return x &gt; 2;
}
function globalTest(chip) {
return moves &gt; 0;
} 
function idString(n) {
return &#39;btn&#39;+n;
}
function dataTransfer(n) {
return &#39;data stored: &#39;+n;
}
&lt;/script&gt;
&lt;template&gt;
&lt;template v-for=&quot;(item,n) in thisData&quot; :key=&quot;n&quot; &gt;
&lt;button  v-if=&quot;localTest(n)&quot;  style=&quot;background-color: lightblue;&quot; class=&quot;swappable&quot;
:data-transfer = dataTransfer(item)
:id=idString(n)
&gt; {{ item }} 
&lt;/button&gt;
&lt;button    v-else   style=&quot;background-color: lightgreen;&quot; class=&quot;swappable&quot;
:data-transfer = dataTransfer(item)
:id=idString(n)
&gt; {{ item}} 
&lt;/button&gt;
&lt;p :id=idString(n+100) &gt;   &lt;/p&gt;
&lt;/template&gt;
&lt;button  @click=&quot;inspect()&quot;&gt;Inspect Element Data&lt;/button &gt;
&lt;p&gt;&lt;/p&gt;
&lt;button  @click=&quot;move()&quot;&gt;Copy btn 1 to btn 2&lt;/button &gt;
&lt;/template&gt;

... 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替换元素时,引用会自动更新。

这是修复后的代码:

&lt;script setup&gt;
  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;
  }

&lt;/script&gt;

&lt;template&gt;
   &lt;template v-for="(item,n) in thisData" :key="n" &gt;
     &lt;button  v-if="globalTest(n)"  style="background-color: lightblue;" class="swappable"
        :data-transfer = dataTransfer(item)
        :id=idString(n)
        ref="items"
        &gt; {{ item }} 
     &lt;/button&gt;
     &lt;button    v-else   style="background-color: lightgreen;" class="swappable"
        :data-transfer = dataTransfer(item)
        :id=idString(n)
        ref="items"
        &gt; {{ item}} 
    &lt;/button&gt;
     &lt;p :id=idString(n+100) &gt;   &lt;/p&gt;
   &lt;/template&gt;
     &lt;button  @click="inspect()"&gt;Inspect Element Data&lt;/button &gt;
    &lt;p&gt;&lt;/p&gt;
   &lt;button  @click="move()"&gt;Copy btn 1 to btn 2&lt;/button &gt;
&lt;/template&gt;
英文:

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(&#39;.swappable&#39;) ; from onMounted.
We should also add ref=&quot;items&quot; to the button.

This resolves issue, as now the references are automatically updated when Vue replaces an element.

Here is the fixed code:

&lt;script setup&gt;
  import {  onMounted,ref,reactive } from &#39;vue&#39;
  const thisData = reactive([&#39;AAA&#39;,&#39;BBB&#39;,&#39;CCC&#39;,&#39;DDD&#39;,&#39;EEE&#39;])

  var items = ref([]);
  var moves = 0;

  onMounted(() =&gt; {
    inspect();
  });
  function inspect() {
    for (let n=0; n &lt; 5; n++)  {
      let data = items.value[n].dataset.transfer;
      const elem = document.getElementById(idString(n+100));
      elem.innerText = &#39;id: &#39; + items.value[n].id + &#39; :&#39; + data ;
    }
  }
  function move() {
      thisData[1] = thisData[0];
      ++moves;
  }

  function localTest(x) {
        return x &gt; 2;
  }
  function globalTest(chip) {
    return moves &gt; 0;
    
  } 
  function idString(n) {
    return &#39;btn&#39;+n;
  }
  function dataTransfer(n) {
    return &#39;data stored: &#39;+n;
  }

&lt;/script&gt;

&lt;template&gt;
   &lt;template v-for=&quot;(item,n) in thisData&quot; :key=&quot;n&quot; &gt;
     &lt;button  v-if=&quot;globalTest(n)&quot;  style=&quot;background-color: lightblue;&quot; class=&quot;swappable&quot;
        :data-transfer = dataTransfer(item)
        :id=idString(n)
        ref=&quot;items&quot;
        &gt; {{ item }} 
     &lt;/button&gt;
     &lt;button    v-else   style=&quot;background-color: lightgreen;&quot; class=&quot;swappable&quot;
        :data-transfer = dataTransfer(item)
        :id=idString(n)
        ref=&quot;items&quot;
        &gt; {{ item}} 
    &lt;/button&gt;
     &lt;p :id=idString(n+100) &gt;   &lt;/p&gt;
   &lt;/template&gt;
     &lt;button  @click=&quot;inspect()&quot;&gt;Inspect Element Data&lt;/button &gt;
    &lt;p&gt;&lt;/p&gt;
   &lt;button  @click=&quot;move()&quot;&gt;Copy btn 1 to btn 2&lt;/button &gt;
&lt;/template&gt;

huangapple
  • 本文由 发表于 2023年5月21日 17:19:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76299147.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定