Skip to content

Template Refs

Trong khi mô hình k rendering của Vue ẩn đi hầu hết các hoạt động trực tiếp trên DOM cho bạn, vẫn có những trường hợp mà chúng ta cần truy cập trực tiếp đến các phần tử DOM cơ bản. Để làm được điều này, chúng ta có thể sử dụng thuộc tính đặc biệt ref:

template
<input ref="input">

ref là một thuộc tính đặc biệt, tương tự như thuộc tính key được thảo luận trong chương về v-for. Nó cho phép chúng ta có được một tham chiếu trực tiếp đến một phần tử DOM cụ thể hoặc một thể hiện của thành phần con sau khi nó được mount. Điều này có thể hữu ích khi bạn muốn, ví dụ, tự động focus vào một ô nhập khi thành phần được mount, hoặc khởi tạo một thư viện bên thứ ba trên một phần tử.

Truy cập các Refs

Để có thể có được tham chiếu với Composition API, chúng ta cần khai báo một ref với tên khớp với giá trị của thuộc tính ref trong mẫu:

vue
<script setup>
import { ref, onMounted } from 'vue'

// khai báo một ref để giữ tham chiếu đến phần tử
// tên phải khớp với giá trị ref trong mẫu
const input = ref(null)

onMounted(() => {
  input.value.focus()
})
</script>

<template>
  <input ref="input" />
</template>

Nếu không sử dụng <script setup>, hãy đảm bảo cũng trả về ref từ setup():

js
export default {
  setup() {
    const input = ref(null)
    // ...
    return {
      input
    }
  }
}

Ref kết quả được tiết lộ trên this.$refs:

vue
<script>
export default {
  mounted() {
    this.$refs.input.focus()
  }
}
</script>

<template>
  <input ref="input" />
</template>

Lưu ý rằng bạn chỉ có thể truy cập ref sau khi thành phần được mount. Nếu bạn cố gắng truy cập $refs.inputinput trong một biểu thức mẫu, nó sẽ là undefinednull trong lần render đầu tiên. Điều này là do phần tử không tồn tại cho đến sau lần render đầu tiên!

Nếu bạn cố gắng theo dõi sự thay đổi của một ref trong mẫu, hãy đảm bảo xem xét trường hợp mà ref có giá trị null:

js
watchEffect(() => {
  if (input.value) {
    input.value.focus()
  } else {
    // chưa được mount, hoặc phần tử đã bị unmount (ví dụ, bằng cách sử dụng v-if)
  }
})

Xem thêm: Gõ tên các Refs

Refs trong v-for

Yêu cầu từ v3.2.25 trở lên

Khi ref được sử dụng bên trong v-for, ref tương ứng nên chứa một giá trị Mảng, sẽ được điền vào các phần tử sau khi mount:

vue
<script setup>
import { ref, onMounted } from 'vue'

const list = ref([
  /* ... */
])

const itemRefs = ref([])

onMounted(() => console.log(itemRefs.value))
</script>

<template>
  <ul>
    <li v-for="item in list" ref="itemRefs">
      {{ item }}
    </li>
  </ul>
</template>

Try it in the Playground

Khi ref được sử dụng b

ên trong v-for, giá trị ref kết quả sẽ là một mảng chứa các phần tử tương ứng:

vue
<script>
export default {
  data() {
    return {
      list: [
        /* ... */
      ]
    }
  },
  mounted() {
    console.log(this.$refs.items)
  }
}
</script>

<template>
  <ul>
    <li v-for="item in list" ref="items">
      {{ item }}
    </li>
  </ul>
</template>

Try it in the Playground

Lưu ý rằng mảng ref không đảm bảo giữ thứ tự giống nhau với mảng nguồn.

Refs Kiểu Hàm

Thay vì sử dụng một chuỗi làm khóa, thuộc tính ref cũng có thể được gắn với một hàm, sẽ được gọi sau mỗi lần cập nhật thành phần và mang lại cho bạn sự linh hoạt hoàn chỉnh về nơi lưu trữ tham chiếu đến phần tử. Hàm này nhận tham chiếu đến phần tử như đối số đầu tiên:

template
<input :ref="(el) => { /* gán el cho một thuộc tính hoặc ref */ }">

Lưu ý rằng chúng ta đang sử dụng một sự ràng buộc động :ref để chúng ta có thể truyền cho nó một hàm thay vì một chuỗi tên ref. Khi phần tử bị unmount, đối số sẽ là null. Bạn cũng có thể sử dụng một phương thức thay vì một hàm nội tuyến.

Ref trên Thành Phần

Phần này giả sử bạn đã hiểu về Components. Hãy thoải mái bỏ qua và quay lại sau.

ref cũng có thể được sử dụng trên một thành phần con. Trong trường hợp này, tham chiếu sẽ là một thể hiện của thành phần:

vue
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'

const child = ref(null)

onMounted(() => {
  // child.value sẽ giữ một thể hiện của <Child />
})
</script>

<template>
  <Child ref="child" />
</template>
vue
<script>
import Child from './Child.vue'

export default {
  components: {
    Child
  },
  mounted() {
    // this.$refs.child sẽ giữ một thể hiện của <Child />
  }
}
</script>

<template>
  <Child ref="child" />
</template>

Nếu thành phần con đang sử dụng Options API hoặc không sử dụng <script setup>,Th thể hiện được tham chiếu sẽ giống nhau với this của thành phần con, điều này có nghĩa là thành phần cha sẽ có quyền truy cập đầy đủ vào mọi thuộc tính và phương thức của thành phần con. Điều này làm cho việc tạo chi tiết triển khai chặt chẽ giữa thành phần cha và con trở nên dễ dàng, vì vậy refs của thành phần chỉ nên được sử dụng khi thực sự cần thiết - trong hầu hết các trường hợp, bạn nên cố gắng triển khai tương tác giữa cha và con bằng cách sử dụng giao diện props và emit chuẩn trước hết.

Một ngoại lệ ở đây là rằng các thành phần sử dụng <script setup>riêng tư theo mặc định: một thành phần cha tham chiếu đến một thành phần con sử dụng <script setup> sẽ không thể truy cập bất cứ thứ gì trừ khi thành phần con quyết định tiếp tục một giao diện công cộng bằng cách sử dụng macro defineExpose:

vue
<script setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

// Các macro biên dịch, như defineExpose, không cần được nhập
defineExpose({
  a,
  b
})
</script>

Khi một thành phần cha nhận được một thể hiện của thành phần này thông qua refs mẫu, thể hiện thu được sẽ có dạng { a: number, b: number } (refs tự động được giải gói giống như trên các thể hiện bình thường).

Xem thêm: Gán Kiểu Ref Cho Thành Phần

Tùy chọn expose có thể được sử dụng để giới hạn quyền truy cập vào một thể hiện con:

js
export default {
  expose: ['publicData', 'publicMethod'],
  data() {
    return {
      publicData: 'foo',
      privateData: 'bar'
    }
  },
  methods: {
    publicMethod() {
      /* ... */
    },
    privateMethod() {
      /* ... */
    }
  }
}

Trong ví dụ trên, một thành phần cha tham chiếu đến thành phần này thông qua refs mẫu chỉ có thể truy cập publicDatapublicMethod.

Template Refs has loaded