ChatGPT解决这个技术问题 Extra ChatGPT

Vue.js:如何为每个组件实例设置唯一 ID?

我想用 Vue.js 创建一个包含 labelinput 的组件。例如 :

<label for="inputId">Label text</label>
<input id="inputId" type="text" />

如何为每个组件实例设置唯一 ID?

您可以查看几个包/mixin:vue-ucidvue-component-idvue-uniq-ids
之前没有看过之前的评论,我也发布了vue-unique-id Vue plugin for this on npm

k
kissu

每个组件都有一个唯一的 ID,可以作为 this._uid 访问。

<template>
  <div>
    <label :for="id">Label text for {{id}}</label>
    <input :id="id" type="text" />
  </div>
</template>

<script>
export default {
  data () {
    return {
      id: null
    }
  }, 
  mounted () {
    this.id = this._uid
  }
}
</script>

例如,如果您想对 id 进行更多控制,可以在父组件中生成它们。


请注意,在 Vue 2.0 及更高版本中删除了 ready 方法。当 ready 方法没有执行时,我很困惑。 stackoverflow.com/a/40209796/126751
... 并且 data 必须是返回对象的函数:vuejs.org/v2/guide/components.html#data-Must-Be-a-Function
不幸的是,这在 TypeScript 中不起作用,因为 this._uid 无效。相反,请自己生成您的 id,例如 public id = uuid4();,请参阅 uuid4
当我尝试从 mount() 方法访问它时,我必须将初始化放在 beforeMount() 方法中,以确保在 DOM 中设置了 id。
k
kissu

Nihat 的观点(上图):Evan You 建议不要使用 _uid:“vm _uid 保留供内部使用,重要的是保持私有(而不是在用户代码中依赖它),这样我们就可以灵活地更改它的未来潜在用例的行为。...我建议自己生成 UID [使用模块、全局 mixin 等]"

this GitHub issue 中使用建议的 mixin 来生成 UID 似乎是一种更好的方法:

let uuid = 0;

export default {
  beforeCreate() {
    this.uuid = uuid.toString();
    uuid += 1;
  },
};

相关 GitHub 问题的链接在这里非常有用
这是 Evan 建议不要使用 _id 的 GitHub 问题:github.com/vuejs/vue/issues/5886#issuecomment-308625735
k
kissu

更新:如果实例中不存在 ._uid 属性,代码将引发错误,以便您可以更新它以使用 Vue 提供的自定义或新的唯一 id 属性。

虽然 zxzak 的回答很棒; _uid 不是已发布的 api 属性。为了避免将来发生变化时头疼,您可以使用如下的插件解决方案通过一次更改来更新您的代码。

Vue.use({
    install: function(Vue, options) {
        Object.defineProperty(Vue.prototype, "uniqId", {
            get: function uniqId() {
                if ('_uid' in this) {
                   return this._uid;
                }
                throw new Error("_uid property does not exist");
            }
        });
    }
});

这仍在使用 uid,建议不要在您自己的回答中确认。请不要发布提倡不良做法的答案。这个答案应该被删除。
是的,但如果发布的 api 被更改/删除,他们将只需要更改整个代码中的一个地方。在另一个答案中,它是每个组件。我已经在标题中强调了这一点。
此外,我刚刚更新了代码,以便在 _uid 属性不再存在的情况下引发错误。
b
bernie

更新

我发布了 vue-unique-id Vue plugin for this on npm

回答

其他解决方案都没有解决组件中包含多个表单元素的要求。这是我对基于先前给出的答案的插件的看法:

Vue.use((Vue) => {
  // Assign a unique id to each component
  let uidCounter = 0;
  Vue.mixin({
    beforeCreate: function() {
      this.uidCounter = uidCounter.toString();
      uidCounter += 1;
    },
  });

  // Generate a component-scoped id
  Vue.prototype.$id = function(id) {
    return "uid-" + this.uidCounter + "-" + id;
  };
});

这不依赖于 reserved for internal use 的内部 _uid 属性。

在您的组件中像这样使用它:

<label :for="$id('field1')">Field 1</label>
<input :id="$id('field1')" type="text" />

<label :for="$id('field2')">Field 2</label>
<input :id="$id('field2')" type="text" />

要产生这样的东西:

<label for="uid-42-field1">Field 1</label>
<input id="uid-42-field1" type="text" />

<label for="uid-42-field2">Field 2</label>
<input id="uid-42-field2" type="text" />

请注意,此插件似乎不适用于 Vue3
使用 uuid +=1 是一种糟糕的做法。序列 (0,1,2,3,...) 本身就有足够的问题,但是将其命名为 uuid 是对 UUID 概念的嘲弄...。将 uuid 库与 uuidv4 一起使用就可以了。主要优点(除了哲学概念)是您不需要任何共享存储来存储当前值,并且代码不会有人为创建的部分乞求竞争条件问题......
uuid 是一个不幸的名称选择,因此我将其重命名为 uidCounter。 Javascript 是单线程的,所以我看不出上面的代码中可能会出现哪些竞争条件。 uidCounter 以原子方式读取和更新,因为没有其他线程并且执行不会被抢占。 Imo,对于这个用例,一个实际的 UUID 并没有比一个简单的递增计数器提供任何好处,但是它确实有效。
k
kissu
npm i -S lodash.uniqueid

然后在你的代码中......

<script>
  const uniqueId = require('lodash.uniqueid')

  export default {
    data () {
      return {
        id: ''
      }
    },
    mounted () {
       this.id = uniqueId()
    }
  }
</script>

这样您就不会加载整个 lodash 库,甚至不会将整个库保存到 node_modules


lodash.uniqueid 是轻量级的包,易于使用。 (缩小版:857B)
这个答案似乎不完整。您不必以某种方式在组件上绑定ID吗? MDN 的教程给出了类似的答案 here,但它只会生成警告并且没有元素上的 id 属性。此外,自 2017 年以来,-S(或 --save)一直是 npm 的默认设置,实际上不再执行任何操作。
k
kissu

这似乎对我在 nuxtjs 中使用有用

https://www.npmjs.com/package/uuid

生成的输出示例:元素:47bfe557-d75f-455c-9a37-85b7935b297b

包.json

"dependencies": {    
    "uuid": "^8.3.2"
 },

在子组件上,可能不是最好的方法,但似乎有效

...

<ComponentName v-if="element" />

...

import { v4 as uuidv4 } from 'uuid';

...

data() {
  return {
    element: null,
  }
}

...

mounted() {
  this.element = uuidv4();
}

g
get_php_workin

我发现最简单的方法是通过全局 mixin 手动创建 UUID (uuid package)。这样,您就不会依赖任何可能在未来发生变化或被弃用的东西,例如 this._uid

您首先必须安装 uuid 软件包:

npm i uuid

然后,在您的 main.js 文件中创建一个全局 mixin:

// rest of imports

import { v4 as uuidv4 } from 'uuid';

const app = Vue.createApp(App);

app.mixin({
    data() {
        return {
            componentId: uuidv4()
        }
    },
});

app.use(store).use(router).mount('#app');

以下是如何在组件中使用它:

<template>
   <div>
      <h1>{{ componentId }}</h1>
      <button @click="printId()">click me for componentId.</button>
   </div>
</template>

<script>
export default {
   methods: {
      printId: function() {
         console.log(this.componentId);
      }
   }
}
</script>

k
kissu

如果你使用 TypeScript,没有任何插件,你可以简单地在你的类组件中添加一个静态 id 并在 created() 方法中增加它。每个组件都有一个唯一的 id(添加字符串前缀以避免与使用相同提示的其他组件发生冲突)

<template>
  <div>
    <label :for="id">Label text for {{id}}</label>
    <input :id="id" type="text" />
  </div>
</template>

<script lang="ts">
  ...
  @Component
  export default class MyComponent extends Vue {
    private id!: string;
    private static componentId = 0;
    ...
    created() {
      MyComponent.componentId += 1;
      this.id = `my-component-${MyComponent.componentId}`;
    }
</script>

定义 Vue 组件的非基于 class 的语法的等价物是什么?例如使用 `export default defineComponent({ created() { ... }, ... });
k
kissu

我在回复中没有看到的一个简单方法是:

<template>
  <div>
    <label :for="id">Label text for {{id}}</label>
    <input :id="id" type="text" />
  </div>
</template>

<script>
import uniqueId from 'lodash-es/uniqueId'

export default {
  computed: {
    id () {
      # return this._uid
      return uniqueId('id')
    }
  }
}
</script>

Vue.js 的创建者说你应该避免使用 _uid,因为它是供内部使用的,有一天他们可能会删除它或重命名它或改变它的行为。
谢谢,我认为这是正确的。我已经用不同的解决方案更新了代码,希望仍然足够简单。无论如何,这个例子的想法是使用计算属性。
在我看来,来自 lodash 的 uniqueId 是最好的方法
k
kissu

在 Vue2 中,使用 v-bind

假设我有一个投票对象

<div class="options" v-for="option in poll.body.options">
  <div class="poll-item">
    <label v-bind:for="option._id" v-bind:style="{color: option.color}">
      {{option.text}}
    </label>
    <input type="radio" v-model="picked" v-bind:value="option._id" v-bind:id="option._id">
  </div>
</div>

您应该使用 v-for="(option, index) in poll.body.options",并在 v-bind 中使用 index
l
luvejo

根据 MDN,您可以只进行隐式 label 绑定。

<label>
  Label text
  <input type="text" />
</label>

这样你甚至不需要分配一个ID。


在您的示例中,您不需要 id 属性,但这并不能真正回答问题。在某些情况下,您不可避免地需要使用 id。此外,MDN 有一个 Vue 教程 (here),其中提供了一个无效的示例,说明如何在组件上设置唯一 ID……这就是把我带到这里的原因。
C
Community

对于 DOM 中跨多个组件的非唯一 ID 的潜在问题,这个包似乎是一个很好的解决方案:

vue-uniq-ids

使用组件是一种趋势。组件很酷,它们很小、很明显、易于使用和模块化。直到涉及到 id 属性。一些 HTML 标签属性需要使用 id 属性,如 label[for]、input[form] 和许多 aria-* 属性。 id 的问题在于它不是模块化的。如果页面上的多个 id 属性具有相同的值,它们会相互影响。 VueUniqIds 可以帮助你摆脱这个问题。它提供了一组与 id 相关的指令,通过添加唯一字符串自动修改其值,同时保持属性易于阅读。


k
kissu

使用 https://www.npmjs.com/package/uuid 这似乎对我有用

生成的输出示例:元素:47bfe557-d75f-455c-9a37-85b7935b297b

包.json

"dependencies": {    
    "uuid": "^8.3.2"
 },

组件.vue

v-if="element"

...

import { v4 as uuidv4 } from 'uuid';

...

data() {
  return {
    element: null,
  }
}

...

mounted() {
  this.element = uuidv4();
}

B
Bombarder70

对于 Vue.js v3,您可以像这样获取 id:

在模板中{{ $.uid }}

在脚本中this.$.uid

或者使用你自己的函数或者混合它们:

this.componentUid = ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
          (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
        );

这将返回例如:

aa174375-5b75-4919-acd0-980fcd54003c


Y
YinPeng.Wei

如果您的 uid 未被其他组件使用,我有一个想法。

uid: Math.random()

简单而足够。


当确实有可能发生 id 冲突时,很难证明这种方法的合理性......
@Shadow 很好 - 有足够大的可能值空间,它比使用 +=1 的一些“解决方案”要好得多。
@RadekHladík 如果 id 只需要在应用程序的运行实例中是唯一的,那么 += 1 方法实际上会更加健壮。您将永远不会发生冲突,因为页面无疑会在数字用完之前刷新很久。随机方法可以在第二个 id 后立即复制 id,并且由于访问的数字更少,因此工作时间也短得多......
@shadow 我不想冒犯,但你不知道你在说什么。在 JS 中,Math.Random 返回相同的类型(数字)。因此,如果 Random 将使用其全部范围(cca 2 ^ 53),则仅取决于实施。或者您可以使用 128 位长的 uuid。甚至与 2^32 发生碰撞的可能性很小,因此并不重要。然而,最大的问题不是数量用完,而是一个事实,即您可以意外生成两个相同的 id - 即使用最后一页加载的 id 和新的 id。
@Shadow,但第二个问题是您正在创建一个无法并行化的代码。我知道 JS 是一个单线程环境,但在代码中创建不必要的限制被认为是一个坏习惯 - 即当您试图解决的问题不需要它时。现在看起来不错,但后来“开发人员”可能会认为页面上的顶部按钮的 ID 为 5(因为它一直都有),然后您在不同的浏览器中运行它或在某处添加另一个 id,它会改变。如果您不需要,基本上不要对您的代码施加不必要的限制......
p
pitaside

它也可以使用这种模式(Vue 2.0 v-bind)来实现,所以假设你有一个要迭代的项目列表,并且你想给一些 dom 元素 uninque id。

new Vue({

  el:body,
  data: {
     myElementIds : [1,2,3,4,5,6,8]
   }
})

html

<div v-for="id in myElementIds">
    <label v-bind:for="id">Label text for {{id}}</label>
    <input v-bind:id="id" type="text" />
<div> 

希望能帮助到你


您只是在数组中定义 ID。根本解决不了原来的问题。