Menu

QuickStart 03 - Fragments And Emits Component Option
 (2021.02.12) - 图1

Fragments

In 2.x, multi-root components were not supported and would emit a warning when a user accidentally created one. As a result, many components are wrapped in a single <div> in order to fix this error.

  1. <!-- Layout.vue -->
  2. <template>
  3. <div>
  4. <header>...</header>
  5. <main>...</main>
  6. <footer>...</footer>
  7. </div>
  8. </template>

In Vue 3, components now have official support for multi-root node components, i.e., fragments!

  1. <!-- Layout.vue -->
  2. <template>
  3. <header>...</header>
  4. <main v-bind="$attrs">...</main>
  5. <footer>...</footer>
  6. </template>

Emits Components Option

Emitted events can be defined on the component via the emits option.

difine emitted events via Emits Option

If we emit a click event, callback will be excuted twice on the parent component. For solving this problem, we can set click via emits option. And then callback will be excuted normally.

  1. app.component('custom-component', {
  2. emits: ['click']
  3. })

validate emitted events

Just similar to prop type validation, emitted events validation is used to determine whether the emitted process is in line with expectations.

for examples:

  1. app.component('custom-form', {
  2. emits: {
  3. // No validation
  4. click: null,
  5. // Validate submit event
  6. submit: ({ email, password }) => {
  7. if (email && password) {
  8. return true
  9. } else {
  10. console.warn('Invalid submit event payload!')
  11. return false
  12. }
  13. }
  14. },
  15. methods: {
  16. submitForm() {
  17. this.$emit('submit', { email, password })
  18. }
  19. }
  20. })

v-model Arguments

By default, if we use v-model without arguments, v-model directive will use modelValue as prop and use update:modelValue as event name.

We can set an argument to target our prop name and event name. Like below:

  1. <template>
  2. <teleport to="body">
  3. <div class="modal" v-show="modal">
  4. <div class="modal__box">
  5. <div class="modal__box-content">
  6. <p>Hello, welcome to new world.</p>
  7. <p>My name is Bill Ann.</p>
  8. </div>
  9. <button class="modal__box-close" @click="closeModal">关闭</button>
  10. </div>
  11. </div>
  12. </teleport>
  13. </template>
  14. <script>
  15. export default {
  16. name: 'VModelModal',
  17. props: {
  18. modal: {
  19. type: Boolean,
  20. default: false
  21. }
  22. },
  23. emits: ['update:modal'],
  24. setup(props, { emit }) {
  25. function closeModal() {
  26. emit('update:modal', false)
  27. }
  28. return {
  29. closeModal
  30. }
  31. }
  32. }
  33. </script>
  34. <style lang="scss">
  35. .modal {
  36. width: 100%;
  37. height: 100%;
  38. background: rgba(0, 0, 0, 0.8);
  39. position: fixed;
  40. left: 0;
  41. top: 0;
  42. }
  43. .modal__box {
  44. text-align: center;
  45. width: 400px;
  46. height: 200px;
  47. line-height: 200px;
  48. background: #ffffff;
  49. border-radius: 4px;
  50. position: absolute;
  51. left: 50%;
  52. top: 50%;
  53. transform: translate(-50%, -50%);
  54. }
  55. .modal__box-content {
  56. line-height: 1;
  57. display: inline-block;
  58. }
  59. .modal__box-close {
  60. position: absolute;
  61. top: 10px;
  62. right: 10px;
  63. }
  64. </style>
  1. <template>
  2. <button class="vmodel__btn" @click="openNewWorld">v-model: 点击这里打开新世界</button>
  3. <VModelModal v-model:modal="showModal"></VModelModal>
  4. </template>
  5. <script>
  6. import { ref } from 'vue'
  7. // import components
  8. import VModelModal from './VModelModal.vue'
  9. export default {
  10. name: 'VModel',
  11. components: {
  12. VModelModal
  13. },
  14. setup() {
  15. function openNewWorld() {
  16. showModal.value = true
  17. }
  18. let showModal = ref(false)
  19. return {
  20. showModal,
  21. openNewWorld
  22. }
  23. }
  24. }
  25. </script>
  26. <style lang="scss">
  27. .vmodel__btn {
  28. margin-top: 20px;
  29. margin-bottom: 20px;
  30. }
  31. </style>

Multiple v-model Bindings

By setting arguments, we can create multiple v-model bindings on a single component instance.

  1. <template>
  2. <teleport to="body">
  3. <div class="modal" v-show="modal">
  4. <div class="modal__box">
  5. <div class="modal__box-content">
  6. <input type="text" :value="name" @input="changeName">
  7. <p>Hello, welcome to new world.</p>
  8. <p>My name is Bill Ann.</p>
  9. </div>
  10. <button class="modal__box-close" @click="closeModal">关闭</button>
  11. </div>
  12. </div>
  13. </teleport>
  14. </template>
  15. <script>
  16. export default {
  17. name: 'VModelModal',
  18. props: {
  19. modal: {
  20. type: Boolean,
  21. default: false
  22. },
  23. name: {
  24. type: String,
  25. default: ''
  26. }
  27. },
  28. emits: ['update:modal', 'update:name'],
  29. setup(props, { emit }) {
  30. function closeModal() {
  31. emit('update:modal', false)
  32. }
  33. function changeName(ev) {
  34. emit('update:name', ev.target.value)
  35. }
  36. return {
  37. closeModal,
  38. changeName
  39. }
  40. }
  41. }
  42. </script>
  43. <style lang="scss">
  44. .modal {
  45. width: 100%;
  46. height: 100%;
  47. background: rgba(0, 0, 0, 0.8);
  48. position: fixed;
  49. left: 0;
  50. top: 0;
  51. }
  52. .modal__box {
  53. text-align: center;
  54. width: 400px;
  55. height: 200px;
  56. line-height: 200px;
  57. background: #ffffff;
  58. border-radius: 4px;
  59. position: absolute;
  60. left: 50%;
  61. top: 50%;
  62. transform: translate(-50%, -50%);
  63. }
  64. .modal__box-content {
  65. line-height: 1;
  66. display: inline-block;
  67. }
  68. .modal__box-close {
  69. position: absolute;
  70. top: 10px;
  71. right: 10px;
  72. }
  73. </style>
  1. <template>
  2. <button class="vmodel__btn" @click="openNewWorld">v-model: 点击这里打开新世界</button>
  3. <VModelModal v-model:modal="showModal" v-model:name="name"></VModelModal>
  4. </template>
  5. <script>
  6. import { ref, watch } from 'vue'
  7. // import components
  8. import VModelModal from './VModelModal.vue'
  9. export default {
  10. name: 'VModel',
  11. components: {
  12. VModelModal
  13. },
  14. setup() {
  15. function openNewWorld() {
  16. showModal.value = true
  17. }
  18. let showModal = ref(false)
  19. let name = ref('')
  20. watch(name, (val, oldVal) => {
  21. console.log(val)
  22. })
  23. return {
  24. showModal,
  25. openNewWorld,
  26. name
  27. }
  28. }
  29. }
  30. </script>
  31. <style lang="scss">
  32. .vmodel__btn {
  33. margin-top: 20px;
  34. margin-bottom: 20px;
  35. }
  36. </style>

Handing v-model Modifiers

By default, v-model has some modifiers such as trim, number and lazy. We can set custom v-model modifiers by modelModifers prop.

  1. <template>
  2. <teleport to="body">
  3. <div class="modal" v-show="modal">
  4. <div class="modal__box">
  5. <div class="modal__box-content">
  6. <input type="text" :value="name" @input="changeName">
  7. <p>Hello, welcome to new world.</p>
  8. <p>My name is Bill Ann.</p>
  9. </div>
  10. <button class="modal__box-close" @click="closeModal">关闭</button>
  11. </div>
  12. </div>
  13. </teleport>
  14. </template>
  15. <script>
  16. export default {
  17. name: 'VModelModal',
  18. props: {
  19. modal: {
  20. type: Boolean,
  21. default: false
  22. },
  23. name: {
  24. type: String,
  25. default: ''
  26. },
  27. nameModifiers: {
  28. type: Object,
  29. default: () => {
  30. return {}
  31. }
  32. }
  33. },
  34. emits: ['update:modal', 'update:name'],
  35. setup(props, { emit }) {
  36. function closeModal() {
  37. emit('update:modal', false)
  38. }
  39. function changeName(ev) {
  40. let value = ev.target.value
  41. if (props.nameModifiers.capitalize) {
  42. value = value.charAt(0).toUpperCase() + value.substring(1)
  43. }
  44. emit('update:name', value)
  45. }
  46. return {
  47. closeModal,
  48. changeName
  49. }
  50. }
  51. }
  52. </script>
  53. <style lang="scss">
  54. .modal {
  55. width: 100%;
  56. height: 100%;
  57. background: rgba(0, 0, 0, 0.8);
  58. position: fixed;
  59. left: 0;
  60. top: 0;
  61. }
  62. .modal__box {
  63. text-align: center;
  64. width: 400px;
  65. height: 200px;
  66. line-height: 200px;
  67. background: #ffffff;
  68. border-radius: 4px;
  69. position: absolute;
  70. left: 50%;
  71. top: 50%;
  72. transform: translate(-50%, -50%);
  73. }
  74. .modal__box-content {
  75. line-height: 1;
  76. display: inline-block;
  77. }
  78. .modal__box-close {
  79. position: absolute;
  80. top: 10px;
  81. right: 10px;
  82. }
  83. </style>
  1. <template>
  2. <button class="vmodel__btn" @click="openNewWorld">v-model: 点击这里打开新世界</button>
  3. <VModelModal v-model:modal="showModal" v-model:name.capitalize="name"></VModelModal>
  4. </template>
  5. <script>
  6. import { ref, watch } from 'vue'
  7. // import components
  8. import VModelModal from './VModelModal.vue'
  9. export default {
  10. name: 'VModel',
  11. components: {
  12. VModelModal
  13. },
  14. setup() {
  15. function openNewWorld() {
  16. showModal.value = true
  17. }
  18. let showModal = ref(false)
  19. let name = ref('')
  20. watch(name, (val, oldVal) => {
  21. console.log(val)
  22. })
  23. return {
  24. showModal,
  25. openNewWorld,
  26. name
  27. }
  28. }
  29. }
  30. </script>
  31. <style lang="scss">
  32. .vmodel__btn {
  33. margin-top: 20px;
  34. margin-bottom: 20px;
  35. }
  36. </style>

tips: By default, we should set a prop named modelModifiers. But if we want to set modifiers on custom v-model bindings, we should set a prop named [custom prop name]['Modifiers']. Just like above codes.