ChatGPT解决这个技术问题 Extra ChatGPT

Vue.js: How to set a unique ID for each component instance?

I want to create a component with Vue.js containing a label and an input. For example :

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

How can I set a unique ID for each component instance?

There are several packages/mixins which you can look into: vue-ucid, vue-component-id, vue-uniq-ids.
Having not seen the previous comment before, I also published the vue-unique-id Vue plugin for this on npm.

k
kissu

Each component has a unique id which can be accessed as 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>

If you want more control over the ids you can for example, generate them inside a parent component.


Note that the ready method was removed in Vue 2.0 and above. I was very confused when the ready method wasn't executing. stackoverflow.com/a/40209796/126751
... and data must be a function that returns an object: vuejs.org/v2/guide/components.html#data-Must-Be-a-Function
Unfortunately, this does not work in TypeScript, as this._uid is not valid. Instead, generate your id yourself, e.g. public id = uuid4();, see uuid4.
I had to put the initialization in the beforeMount() method to make sure the id was set in the DOM when I tried to access it from the mounted() method.
k
kissu

To Nihat's point (above): Evan You has advised against using _uid: "The vm _uid is reserved for internal use and it's important to keep it private (and not rely on it in user code) so that we keep the flexibility to change its behavior for potential future use cases. ... I'd suggest generating UIDs yourself [using a module, a global mixin, etc.]"

Using the suggested mixin in this GitHub issue to generate the UID seems like a better approach:

let uuid = 0;

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

a link to the pertinent GitHub issue would be very useful here
Here is the GitHub issue where Evan advise against using _id: github.com/vuejs/vue/issues/5886#issuecomment-308625735
k
kissu

Update: Code will throw an error if ._uid property does not exist in the instance so that you can update it to use something custom or new unique id property if provided by Vue.

Although zxzak's answer is great; _uid is not a published api property. To save a headache in case it changes in the future, you can update your code with just one change with a plugin solution like below.

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");
            }
        });
    }
});

This is still using the uid, which in your own answer acknowledges is advised against. Please don't post answers advocating bad practices. This answer should be removed.
Yes but in case the published api is changed / removed, they will have to change only one place in the entire code. In the other answer, it was per component. I already emphasized that in the title.
Also, I just updated the code so that it will throw an error in case _uid property does not exist anymore.
b
bernie

Update

I published the vue-unique-id Vue plugin for this on npm.

Answer

None of the other solutions address the requirement of having more than one form element in your component. Here's my take on a plugin that builds on previously given answers:

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;
  };
});

This doesn't rely on the internal _uid property which is reserved for internal use.

Use it like this in your component:

<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" />

To produce something like this:

<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" />

A note that this plugin does not appear to work for Vue3
Using uuid +=1 is terrible practice. The sequence (0,1,2,3,...) is problematic enough by itself but naming it uuid is mockery of the UUID concepts.... Use uuid library with uuidv4 and you will be fine. The main advantage (apart from philosophical concepts) is that you do not need any shared storage for the current value and the code will not have artificially created sections begging for race condition issues...
uuid was an unfortunate name choice so I've renamed it to uidCounter. Javascript is single threaded so I don't see which race conditions could possibly creep in the code above. uidCounter is read and updated atomically because no there are no other threads and execution isn't preempted. Imo, an actual UUID doesn't offer any benefit over a simple incrementing counter for this use-case, but yes it works.
k
kissu
npm i -S lodash.uniqueid

Then in your code...

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

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

This way you're not loading the entire lodash library, or even saving the entire library to node_modules.


lodash.uniqueid is lightweight package and easy to use. (MINIFIED: 857B)
This answer seems incomplete. Don't you have to bind the ID on the component somehow? MDN's tutorial gives a similar answer here, but that just generates warnings and no id properties on elements. Also, -S (or --save) has been the default on npm since 2017 and doesn't actually do anything any more.
k
kissu

This seem to work for me using in nuxtjs

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

example of generated output: element: 47bfe557-d75f-455c-9a37-85b7935b297b

package.json

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

on child component, might not be the best way but seem to work

...

<ComponentName v-if="element" />

...

import { v4 as uuidv4 } from 'uuid';

...

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

...

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

g
get_php_workin

The simplest way I found was to create a UUID (uuid package) manually through a global mixin. That way you won't rely on anything that can potentially change or become deprecated in the future like this._uid.

You first have to install the uuid package:

npm i uuid

Then, in your main.js file create a global 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');

And here is how you can us it in a component:

<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

If you're using TypeScript, without any plugin, you could simply add a static id in your class component and increment it in the created() method. Each component will have a unique id (add a string prefix to avoid collision with another components which use the same tip)

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

What is the equivalent for the non-class-based syntax for defining Vue components? For example using `export default defineComponent({ created() { ... }, ... });
k
kissu

A simple approach that I haven't seen in the replies is:

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

The creator of Vue.js says you should avoid using _uid because it's for internal use and someday they may remove it or rename it or change its behaviour.
Thanks, I think that is correct. I have updated the code with a different solution, hopefully still simple enough. Anyway, the idea of this example was to use a computed property.
uniqueId from lodash is the best approach in my opinion
k
kissu

In Vue2, use v-bind.

Say I have an object for a poll

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

What you should go for is v-for="(option, index) in poll.body.options", and use index in you v-bind.
l
luvejo

According to MDN, you can just make an implicit label binding.

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

This way you don't even need to assign an id.


In your example, you don't need an id property, but this doesn't really answer the question. There are cases in which you can't avoid needing to use an id. Also, MDN has a Vue tutorial (here) where they give an non-working example of how to set unique IDs on components... That's what brought me here.
C
Community

This package seems to be a good solution for the underlying issue of having non-unique IDs in your DOM across multiple components:

vue-uniq-ids

It is a trend to use components. Components are cool, they are small, obvious, easy to use and modular. Untill it comes to the id property. Some HTML tag attributes requires using an id property, like label[for], input[form] and many of aria-* attributes. And the problem with the id is that it is not modular. If several id properties on the page will has the same value they can affect each other. VueUniqIds helps you to get rid of this problem. It provides the set of id-related directives which value is automatically modified by adding unique string while keeping the attrbitue easy to read.


k
kissu

This seem to work for me using https://www.npmjs.com/package/uuid

example of generated output: element: 47bfe557-d75f-455c-9a37-85b7935b297b

package.json

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

component.vue

v-if="element"

...

import { v4 as uuidv4 } from 'uuid';

...

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

...

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

B
Bombarder70

For Vue.js v3 you can get id like this:

In template: {{ $.uid }}

In script: this.$.uid

Or use your own function or mix them:

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

This will return e.g:

aa174375-5b75-4919-acd0-980fcd54003c


Y
YinPeng.Wei

If your uid is not used by other compoment, I have an idea.

uid: Math.random()

Simple and enough.


It's difficult to justify this approach when there is a real chance of id collision...
@Shadow well - with large enough space of possible values it is much better than some "solutions" using +=1.
@RadekHladík The += 1 method is actually going to be far more robust if the id only needs to be unique within the running instance of the application. You'll never get a collision as the page will no doubt be refreshed far before running out of numbers. The random method could have the ids duplicated as soon as the second id, and would also work for far less time due to having access to less numbers...
@shadow I do not want to be offensive but you have no idea what you are talking about. In JS the Math.Random returns the same type (Number). So it is only up to implementation if the Random will use its full range (cca 2^53). Or you can use the uuid which is 128bit long. Collision with even 2^32 is so unprobable that it is not important. However the biggest problem is that is is not about the running out of number but about the fact, that you can generate two same ids by accident - i.e. using the id from last page load in combination with the new id.
@Shadow but the second problem is that you are creating a code that can not be paralelized. I know that JS is a single thread environment but it is considered a bad habit to create unnecessary limitations in your code - i.e. when it is not needed by the problem that you are trying to solve. Now it may seem fine but later the "developer" may think that the top button on the page has ID of 5 (because it has always had) and then you run it in different browser or add another id somewhere and it will change. Basically do not impose unnecessary limitations on your code if you do not have to...
p
pitaside

It can also be achieved using this pattern (Vue 2.0 v-bind) , so let say you have a list of items to iterate over and you want to give some dom element uninque id's.

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> 

Hope it helps


You are simply defining the ID in the array. Doesn't solve the original question at all.