Vue3 新变化

插槽语法糖

在 Vue2 中,我们使用 v-slot:name 来替换插槽,如下:

  • Demo1.vue
<template>
  <slot name="slot1"></slot>
  <slot name="slot2"></slot>
</template>

<script>
  export default {
  
  }
</script>

<style>

</style>
  • App.vue
<template>
  <Demo1>
    <template v-slot:slot1>
      <span>插槽1替换</span>
    </template>
    <template v-slot:slot2>
      <span>插槽2替换</span>
    </template>
  </Demo1>

</template>

<script>
  import Demo1 from './components/Demo1.vue'
  export default {
    components: {
      Demo1
    }
  }
</script>

<style>
</style>

在 Vue3 中,可以使用 # 来替换 v-slot,如下:

<template>
  <Demo1>
    <template #slot1>
      <div>插槽1替换</div>
    </template>
    <template #slot2>
      <span>插槽2替换</span>
    </template>
  </Demo1>
</template>

Fragment 片段

在 Vue2 中,组件必须有一个根标签。而在 Vue3 中,组件可以没有根标签,内部会将多个标签包含在一个 Fragment 虚拟元素中,这样的好处是减少标签层级,减小内存占用。

Teleport 组件

Teleport 也称为传送门,它的作用是把嵌套陈词较深的元素移动到其它 html 元素内。比如 App.vue 中使用了 Child.vueChild.vue 中使用了 Son.vueSon.vue 中有一个弹窗,如果没有特殊处理,那么这个弹窗的展示会影响 Son 元素的所有父组件(如宽高拉伸)。为了更加方便解决这个问题,我们可以使用 Vue3 中提供的 <teleport> 标签,如下:

  • Son.vue
<template>
  <div class="son">
    <span>孙子组件</span>
    {{ data }}
  </div>
  <!-- 把 teleport 中的内容放入 body 元素中, 当然也能写 css 选择器 -->
  <teleport to="body">
    <h1>Son 的元素</h1>
  </teleport>
  
  <teleport to="#container">
    <h1>Son 的元素</h1>
  </teleport>
</template>

Suspense 组件

该方法用于异步(动态)引入组件。Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。

<template>
  <div class="app">
    <h2>App.Vue</h2>
    {{ data }}
    
    <!-- 使用 Suspense 组件 -->
    <Suspense>
    
      <!-- 异步组件加载成功时显示的内容 -->
      <template #default>
        <Child></Child>
      </template>
      
      <!-- 异步组件加载失败时显示的内容 -->
      <template #fallback>
        <h2>Child 组件加载中...</h2>
      </template>
    </Suspense>
  </div>
</template>

<script>
  import { reactive } from '@vue/reactivity'
  // import Child from './components/Child' // 同步引入
  import { defineAsyncComponent, provide } from '@vue/runtime-core'

  let Child = defineAsyncComponent(() => import('./components/Child')) // 异步引入

  export default {
    components: {
      Child
    },

    setup() {
      let data = reactive({
        name: 'zs',
        age: 23,
      })

      return {
        data
      }
    }
  }
</script>

<style>
  .app {
    background: blue;
    height: 500px;
    color: #fff;
  }
</style>

如果使用了异步方式来引入组件,那么 setup 函数可以返回一个 Promise,如下:

  • Child.vue
<template>
  <div class="child">
    <span>子组件</span><br>
    {{ num }}
  </div>
</template>

<script>
  import { ref } from '@vue/reactivity'

  export default {
    setup() {
      let num = ref(123)

      return new Promise((resolve, reject) => {
        setTimeout(() => {
          // 注意这里的 resolve 要返回对象
          resolve({num})
        }, 3000)
      })
    }
  }
</script>

<style scoped>
  .child {
    height: 300px;
    background: green;
    color: #fff;
  }
</style>

同时,如果使用异步组件,那么引入的组件的 setup 函数也可以是 async 函数,如下:

  • Child.vue
<template>
  <div class="child">
    <span>子组件</span><br>
    {{ num }}
  </div>
</template>

<script>
  import { ref } from '@vue/reactivity'

  export default {

    async setup() {
      let num = ref(123)

      let promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          // 注意这里的 resolve 要返回对象
          resolve({num})
        }, 3000)
      })

      return await promise
    }
  }
</script>

<style scoped>
  .child {
    height: 300px;
    background: green;
    color: #fff;
  }
</style>

全局 API 的变动

Vue2 全局 API (Vue)Vue3 实例 API (app)
Vue.config.xxXXapp.config.xxXX
Vue.config.productionTip移除
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties
  • main.js
import { createApp } from 'vue'
import App from './App.vue'

let app = createApp(App)

app.use(xxx)

app.mount('#app')