Menus

QuickStart 07 - Internal template directives (2021.03.10) - 图1

v-model

What has changed in vue3:

  • When using v-model directly with no arguments on custom components, its prop and event’s default names are changed like below:
    • prop: value -> modelValue
    • event: input -> update:modelValue
  • v-bind‘s .sync modifier and component’s model option are removed and replaced with an argument on v-model. Like below:

    1. <custom-comp v-model:compProp="compProp"></custom-comp>

    custom-com.vue:

    1. {
    2. ...
    3. props: {
    4. compProp: {
    5. type: Boolean,
    6. default: false,
    7. }
    8. },
    9. emits: ['update:compProp'],
    10. setup(props, { emit }) {
    11. ...
    12. function notifyParent() {
    13. emit('update:compProp', true)
    14. }
    15. ...
    16. },
    17. ...
    18. }
  • Multiple v-model bindings on the same component are possible now base on the last change.

  • Added the ability to create custom v-model modifiers. (You can see the last demo in “QuickStart 03 - Fragments And Emits Component Option (2021.02.12)“)

key

Changes:

  • keys are no longer necessary on v-if / v-else / v-else-if branches, since Vue now automatically generates unique keys. (If you manually provide keys, then each brach must use a unique key. You can no longer intentionlly use the same key to force branch reuse.)
  • <template v-for> key should be placed on the <template> tag (rather than on its children).


v-if

Changes:

  • If used on the same element, v-if will have higher precedence than v-for.

v-bind

Changes:

  • Order of bindings for v-bind will affect the rendering result.

In vue2, if an element has both v-bind="object" and an identical individual property defined, the individual property would always overwrite bindings in the object.

  1. <!-- template -->
  2. <div id="red" v-bind="{ id: 'blue' }"></div>
  3. <!-- result -->
  4. <div id="red"></div>
  5. <!-- template -->
  6. <div v-bind="{ id: 'blue' }" id="red"></div>
  7. <!-- result -->
  8. <div id="red"></div>

In vue3, the final result depends on the order of v-bind.

  1. <!-- template -->
  2. <div id="red" v-bind="{ id: 'blue' }"></div>
  3. <!-- result -->
  4. <div id="blue"></div>
  5. <!-- template -->
  6. <div v-bind="{ id: 'blue' }" id="red"></div>
  7. <!-- result -->
  8. <div id="red"></div>

v-on

Changes:

  • The .native modifier for v-on has been removed.

The new emits option allows the child to define which events it does indeed emit. For example:

  1. <custom-comp @click="handleClick"></custom-comp>

custom-com.vue:

  1. {
  2. ...
  3. emits: ['click'],
  4. setup() {
  5. function notifyParent() {
  6. emit('click')
  7. }
  8. }
  9. ...
  10. }

Consequently, Vue will now add all event listeners that are not defined as component-emitted events in the child as native event listeners to the child’s root element (unless inheritAttrs: false has been set in the child’s options).

  1. <custom-comp
  2. v-on:close="handleClose"
  3. v-on:click="handleClick"
  4. />
  1. {
  2. emits: ['close']
  3. }

template ref

Changes:

  • Different from vue2, we no longer use $refs to get the target element or component. We can get them by using reactive refs instead.
  • If we use template ref while using v-for, it no longer automatically create an array which contains every elements or components. What we get finally is the last target element or component in v-for. (You can just test it in onMounted hook.)
  • If we still want to get an array, we can bind a function to do custom actions.

Now, let’s try it.

Below, we will get template’s root element in root variable by using ref api.

  1. <template>
  2. <div ref="root">This is a root element</div>
  3. </template>
  4. <script>
  5. import { ref, onMounted } from 'vue'
  6. export default {
  7. setup() {
  8. const root = ref(null)
  9. onMounted(() => {
  10. // the DOM element will be assigned to the ref after initial render
  11. console.log(root.value) // <div>This is a root element</div>
  12. })
  13. return {
  14. root
  15. }
  16. }
  17. }
  18. </script>

If we use render function or JSX syntax to do it, we can try like this:

  1. <template>
  2. <div ref="root">This is a root element</div>
  3. </template>
  4. <script>
  5. import { ref, onMounted, h } from 'vue'
  6. export default {
  7. setup() {
  8. const root = ref(null)
  9. // render function
  10. return () => {
  11. h('div', {
  12. ref: root
  13. })
  14. }
  15. // with JSX
  16. return () => <div ref={root}></div>
  17. }
  18. }
  19. </script>

In v-for scene, we will get an array base on a function. For example:

  1. <div v-for="item in list" :ref="setItemRef"></div>
  1. import { onBeforeUpdate, onUpdated } from 'vue'
  2. export default {
  3. setup() {
  4. let itemRefs = []
  5. const setItemRef = el => {
  6. if (el) {
  7. itemRefs.push(el)
  8. }
  9. }
  10. onBeforeUpdate(() => {
  11. itemRefs = []
  12. })
  13. onUpdated(() => {
  14. console.log(itemRefs)
  15. })
  16. return {
  17. setItemRef
  18. }
  19. }
  20. }

If you want to use a watcher to watch changes, you can’t use watch or watchEffect directly because they run before the DOM is mounted or updated. You can do it by using watchEffect with the flush: 'post' option. For example:

  1. <template>
  2. <div ref="root">This is a root element</div>
  3. </template>
  4. <script>
  5. import { ref, watchEffect } from 'vue'
  6. export default {
  7. setup() {
  8. const root = ref(null)
  9. watchEffect(() => {
  10. console.log(root.value) // => <div></div>
  11. },
  12. {
  13. flush: 'post'
  14. })
  15. return {
  16. root
  17. }
  18. }
  19. }
  20. </script>