1. Fragment

在vue2中:组件必须有一个跟标签
在vue3中:组件可以没有跟标签, 内部会将多个标签包含在一个Fragment虚拟元素
好处:减少标签层级, 减小内存占用。

2. teleport

什么是Teleport? ——Teleport是一种能够将我们的组件html结构移动到我们指定的位置的技术.
场景:像modaltost等这样的元素, 很多情况下, 我们将它完全和我们的vue应用的DOM完全剥离,管理起来反而会方便容器很多。
原因在于如果我们嵌套在VUe的某个部件内部, 那么处理嵌套组件的定位、z-index和样式就变得很困难。
使用方法:to后面可以直接写body也可以写ID选择器。

  1. <button @click="showToast" class="btn">打开 toast</button>
  2. <!-- to 属性就是目标位置 -->
  3. <teleport to="#teleport-target">
  4. <div v-if="visible" class="toast-wrap">
  5. <div class="toast-msg">我是一个 Toast 文案</div>
  6. </div>
  7. </teleport>
  1. //parent
  2. <template>
  3. <div class="app">
  4. <div id="childBox"></div>
  5. <h2>姓名:{{name}}</h2>
  6. <h2>年龄:{{age}}</h2>
  7. <h2>性别:{{sex}}</h2>
  8. <h2>薪资:{{job.salery}}</h2>
  9. <hr />
  10. <button @click="name += '@'">修改姓名</button>
  11. <button @click="age ++">修改年龄</button>
  12. <button @click="job.salery ++">修改薪资</button>
  13. <my-child></my-child>
  14. </div>
  15. <div id='modal'></div>
  16. </template>
  17. <script>
  18. import MyChild from './MyChild.vue'
  19. import {
  20. reactive,
  21. toRefs,
  22. provide,
  23. isReactive,
  24. readonly,
  25. isReadonly,
  26. ref,
  27. isRef,
  28. isProxy,
  29. } from 'vue'
  30. export default {
  31. components: {
  32. MyChild,
  33. },
  34. setup() {
  35. let person = reactive({
  36. name: 'IRIC',
  37. age: 12,
  38. sex: '女',
  39. job: {
  40. salery: 20,
  41. },
  42. address: '',
  43. })
  44. provide('person', person)
  45. const p = readonly(person)
  46. console.log(isReactive(person)) //true
  47. console.log(isReadonly(p)) //treu
  48. const count = ref(10)
  49. console.log(isRef(count)) //treu
  50. console.log(isProxy(p)) //treu
  51. return { ...toRefs(person), count }
  52. },
  53. }
  54. </script>
  55. <style>
  56. .app {
  57. padding: 10px;
  58. background: orange;
  59. }
  60. .childBox {
  61. background: yellowgreen;
  62. height: 300px;
  63. width: 100%;
  64. }
  65. </style>
  1. //child
  2. <template>
  3. <div class="child">
  4. <h2>我是子组件</h2>
  5. <h1>{{p.name}}</h1>
  6. <h1>{{p.age}}</h1>
  7. <hr />
  8. <my-son></my-son>
  9. </div>
  10. </template>
  11. <script>
  12. import MySon from './MySon.vue'
  13. import { inject, ref } from 'vue'
  14. export default {
  15. components: {
  16. MySon,
  17. },
  18. setup() {
  19. const p = inject('person')
  20. const dialogVisible = ref(false)
  21. return {
  22. p,
  23. dialogVisible
  24. }
  25. },
  26. }
  27. </script>
  28. <style scoped>
  29. .child {
  30. background: pink;
  31. padding: 10px;
  32. }
  33. </style>
  1. //son
  2. <template>
  3. <div class="son">
  4. <h2>{{p.name}}</h2>
  5. <h2>{{p.age}}</h2>
  6. <h2>我是孙辈组件</h2>
  7. <button @click="visible=!visible">显示弹窗</button>
  8. <div class="mask" v-show="visible">
  9. <div class="modal">
  10. <h3>我是一个弹窗</h3>
  11. <h4>一些内容</h4>
  12. <h4>一些内容</h4>
  13. <h4>一些内容</h4>
  14. <button @click="visible=!visible">关闭弹窗</button>
  15. </div>
  16. </div>
  17. </div>
  18. </template>
  19. <script >
  20. import { inject, ref } from 'vue'
  21. export default {
  22. setup() {
  23. const p = inject('person')
  24. const visible = ref(false)
  25. return {
  26. p,
  27. visible,
  28. }
  29. },
  30. }
  31. </script>
  32. <style scoped>
  33. .son {
  34. padding: 10px;
  35. background: green;
  36. }
  37. .mask {
  38. position: absolute;
  39. top: 0;
  40. bottom: 0;
  41. left: 0;
  42. right: 0;
  43. background-color: rgba(0, 0, 0, 0.5);
  44. }
  45. .modal {
  46. width: 300px;
  47. height: 300px;
  48. background: #fff;
  49. position: absolute;
  50. left: 50%;
  51. top: 50%;
  52. transform: translate(-50%, -50%);
  53. }
  54. </style>

image.png
元素结构:
image.png
加了teleport标签:

  1. <teleport to="body">
  2. <div class="mask" v-show="visible">
  3. <div class="modal">
  4. <h3>我是一个弹窗</h3>
  5. <h4>一些内容</h4>
  6. <h4>一些内容</h4>
  7. <h4>一些内容</h4>
  8. <button @click="visible=!visible">关闭弹窗</button>
  9. </div>
  10. </div>
  11. </teleport>

image.png
image.png

3. Suspense

  • 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
  • 使用步骤:
    • 异步引入组件
      1. import {defineAsyncComponent} from 'vue'
      2. const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
      使用Suspense包裹组件,并配置好defaultfallback ```javascript

      1. **default:**就是组件要显示的内容<br />**fallback:**就是组件没加载完全的“备胎”<br />如果在**child**组件中返回一个**promise**,则出现该情况:
      2. ```javascript
      3. <template>
      4. <div class="child">
      5. <h2>我是子组件</h2>
      6. <h2>sum当前的值:{{sum}}</h2>
      7. <hr />
      8. <my-son></my-son>
      9. </div>
      10. </template>
      11. <script>
      12. import MySon from './MySon.vue'
      13. import { ref } from 'vue'
      14. export default {
      15. components: {
      16. MySon,
      17. },
      18. setup() {
      19. const sum = ref(100)
      20. return new Promise((resolve) => {
      21. setTimeout(() => {
      22. resolve({sum})
      23. }, 1000)
      24. })
      25. },
      26. }
      27. </script>

      image.png
      1s后显示child组件。
      或者返回一个await

      1. <script>
      2. import MySon from './MySon.vue'
      3. import { ref } from 'vue'
      4. export default {
      5. components: {
      6. MySon,
      7. },
      8. async setup() {
      9. const sum = ref(100)
      10. const p = new Promise((resolve) => {
      11. setTimeout(() => {
      12. resolve({sum})
      13. }, 3000)
      14. })
      15. return await p
      16. },
      17. }
      18. </script>