Vue3 新特性

Vue3 新特性

image

  • 首先是向下兼容,Vue3 支持大多数 Vue2 的特性。甚至就拿 Vue2 的语法开发 Vue3,也是没有任何问题的。

  • 性能的提升,每个人都希望使用的框架更快,更轻。Vue3 做到了,给开发者一个极致的体验。官方网站给出的数据是:打包大小减少 41%,初次渲染快 55%,更新快 133%,内存使用减少 54%。

  • 新推出的Composition API ,在 Vue2 中遇到的问题就是复杂组件的代码变的非常麻烦,甚至不可维护。说白了就是封装不好,重用不畅。这个Composition API一推出,立马解决了这个问题。它是一系列 API 的合集。

  • 其他新特性:Teleport(瞬移组件)、Suspense(解决异步加载组件问题)和全局 API 的修改和优化。

  • 更好TypeScript支持,Vue3 的源代码就是使用TypeScript进行开发的。所以在新的版本上使用TS也更加顺畅无阻。

一、composition-api

Vue在2.x中编写代码要按照一定的模板,比如数据就只能放在data()中,方法只能放在methods中,按照模板编写代码对于新手来讲可能是好事,但是一旦项目变大,维护起来就显得很困难。

下图的左边图示,即Vue2使用的Options-api,图中相同的颜色对应是组件的一种功能,可以看到为了实现一种功能,Options-api所写的代码是非常分散的。

image

如果组件逻辑复杂,代码量多,我们添加新代码不光要不停的上下滑动,而且在后期代码维护中,阅读起来也变得十分的困难,因为实现一种功能的代码并没有集中在一起。另外就是作为一个新接手的开发人员,在茫茫的 method、data、computed 等选项中一目了然的发现这个变量是属于哪个功能是比较困难的 。

而在Composition-api中,我们可以把实现一种功能的代码写在一起,甚至还可以把它们单独抽取在一个js文件或者一个函数中。在js文件中也可以引用Composition-api的生命周期函数。这将极大的提高代码的可维护性。这样就可以更好的提取和重用多个组件之间的逻辑。

优劣比较:

  • 在逻辑组织和逻辑复用方面,Composition API是优于Options API,因为Composition API几乎是函数,会有更好的类型推断。

  • Composition API对 tree-shaking 友好,代码也更容易压缩。

  • Composition API中见不到this的使用,减少了this指向不明的情况。

  • 如果是小型组件,可以继续使用Options API,也是十分友好的。

1、setup

setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。 新的 setup 选项在组件被创建之前执行,一旦 props 被解析完成,它就将被作为组合式 API 的入口。

setup函数的参数

我们先来研究一个setup函数的参数,它主要有两个参数:

  • 第一个参数:props
  • 第二个参数:context

props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取:

  • 对于定义props的类型,我们还是和之前的规则是一样的,在props选项中定义;
  • 并且在template中依然是可以正常去使用props中的属性,比如message;
  • 如果我们在setup函数中想要使用props,那么不可以通过 this 去获取(后面我会讲到为什么);
  • 因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可;

另外一个参数是context,我们也称之为是一个SetupContext,它里面包含三个属性:

  • attrs:所有的非prop的attribute;
  • slots:父组件传递过来的插槽(这个在以渲染函数返回时会有作用);
  • emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);

setup函数的返回值

setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?

  • setup的返回值可以在模板template中被使用;
  • 也就是说我们可以通过setup的返回值来替代data选项;

甚至是我们可以返回一个执行函数来代替在methods中定义的方法:

<template>
  <div>
    <div>{{ title }}</div>
    <div @click="hanldeClick">点他</div>
  </div>
</template>

<script>
  export default {
    setup() {
      const title = 'this is a title'
      
      const hanldeClick = () => {
        alert('已点!')
      }
      
      return {
        title,
        hanldeClick
      }
    },
  }
</script>

setup不可以使用this

官方关于this有这样一段描述:

  • 表达的含义是this并没有指向当前组件实例;

  • 并且在setup被调用之前,data、computed、methods等都没有被解析;

  • 所以无法在setup中获取this;

image

2、Ref 和 Reactive

在我看来 ref 和 reactice 都是用来创建响应式对象的。

  • reactive 接受的参数是一个对象或数组类型。如果是数组类型会转换成proxy对象。
  • ref 一般创建一个基本类型变量,有一个 .value属性,可以通过其对值进行读取或修改。
  • reactive 在组合函数返回时记得添加上 ’...toRefs(state)‘以保持响应性,对对象解构或展开后会失去响应性,所以需要使用roRefs()把reactive类型转为ref类型。
  • ref 可以用于子组件的ref属性使用。(后面会提到)
<template>
  <div>
    <h2>{{ title }}</h2>
    <h2>{{ rTitle }}</h2>
    <div @click="hanldeClick">点他</div>
  </div>
</template>

<script>
import {reactive, ref, toRefs} from "vue";
export default {
    setup() {
      const title = ref('this is a title')
      const isTrye = ref(true)
      const state = reactive({
         rTitle: 'this is reactive title'
      })
      
      const hanldeClick = () => {
        state.rTitle = '111'
        title.value = '222'
        isTrye.value = false
      }
      
      return {
        ...toRefs(state)
        title,
        isTrye,
        hanldeClick
        
      }
    },
  }
</script>

以下是两种关于Ref ,Reactive的风格建议

  • 就像你在普通 JavaScript 中区别声明基础类型变量与对象变量时一样区别使用 ref 和 reactive。我们推荐你在此风格下结合 IDE 使用类型系统。
  • 所有的地方都用 reactive,然后记得在组合函数返回响应式对象时使用 toRefs。这降低了一些关于 ref 的心智负担,但并不意味着你不需要熟悉这个概念。

3、computed 、watch、watchEffect

<script>
import {reactive, ref, toRefs, computed, watch} from "vue";
export default {
    setup() {
        // computed
        const count = ref(1)
        const plusOne = computed(() => count.value + 1)
        const state = reactive({
            title: computed(() => {
                return count.value + 1
            }),
        })
        // watch
        // watch 可以接收三个参数  第一个是要监听的对象 第二个是数据处理变化,第三个是配置项(options)
        // options: immediate:true是刚一进去就监听一次
        // 1:监听ref定义的响应式数据
        const num = ref(0)
        const msg = ref('watch')
        watch(
            num,
            (newV, oldV) => {
                console.log(newV)
            },
            { immediate:true }
        )
        // 2:监听多个ref声明的响应式数据
        watch(
            [num, msg],
            (newV, oldV) => {
                console.log(newV)
            },
            { immediate:true }
        )
        // 3:监听reactive定义的响应式的全部数据
        // 注意:使用reactive定义的数据,无法正确获取odlV;并且强制开启了深度检测(deep配置无效)
        const state = reactive({
            age: 18,
            name: 'fc'
        })
        watch(
            state,
            (newV, oldV) => {
                console.log(newV)
            },
            { deep: true } // 此配置无效
        )
        // 4:监听reactive所定义的一个响应式数据的一个属性
        watch(
            () => state.age,
            (newV, oldV) => {
                console.log(newV)
            }
        )
        // 5:监听reactive所定义的一个响应式数据的一些属性
        watch(
            [() => state.age, () => state.name],
            (newV, oldV) => {
                console.log(newV)
            }
        )
        // 6:当监听的声明对象内的属性还是一个对象,因为不是使用reactive直接声明的
        const person = {
            job: {
                j1: 1
            }
        }
        watch(
            () => peison.job,
            (newV, oldV) => {
                console.log(newV)
            },
            { deep: true }//此处由于监视的是reactive定义的对象内的某个属性,所以deep配置有效
        )
    
        return {
            ...toRefs(state)
        }
    },
  }
</script>

watchEffect

  • watch是:既要指明监视的属性,也要指明监视的回调。
  • watchEffect是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

这个函数的功能和计算属性差不多,但是

  • computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
  • watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
<script>
import {watchEffect} from "vue";
export default {
    setup() {
        watchEffect(() => {
            if (state.value) {
                // do something ...
                setDates(state.value)
            }
        })
    }
}

4、生命周期

  • setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
  • onBeforeMount() : 组件挂载到节点上之前执行的函数。
  • onMounted() : 组件挂载完成后执行的函数。
  • onBeforeUpdate(): 组件更新之前执行的函数。
  • onUpdated(): 组件更新完成之后执行的函数。
  • onBeforeUnmount(): 组件卸载之前执行的函数。
  • onUnmounted(): 组件卸载完成后执行的函数
  • onActivated(): 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行。
  • onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行。
  • onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。
  • onRenderTracked(): 直译过来就是状态跟踪,它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,他都会跟踪。只要页面有update的情况,他就会跟踪,然后生成一个event对象,我们通过event对象来查找程序的问题所在。
  • onRenderTriggered(): 直译是状态触发,它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。

5、模板指令

  • 组件上 v-model 用法已更改,支持双向绑定多个属性,例如v-model.title="title"
  • key 属性
    1. Vue 3.x 不建议在 v-if/v-else/v-else-if 的分支中使用 key,如果非要使用,请设置唯一的key值。
    2. Vue 3.x 可以将 key值 设置在template 上 (Vue2.x 需要将key值设置到子节点上)
  • v-if 与 v-for 的优先级对比
    1. 2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用。Vue2.x v-for 优先级高
    2. 3.x 版本中 v-if 总是优先于 v-for 生效。Vue3.x v-if 优先级高
  • v-bind 合并行为
    1. 在 2.x,如果一个元素同时定义了 v-bind=“object” 和一个相同的单独的 property,那么这个单独的 property 总是会覆盖 object 中的绑定。 单独的属性覆盖v-bind
    2. 在 3.x,如果一个元素同时定义了 v-bind=“object” 和一个相同的单独的 property,那么声明绑定的顺序决定了它们如何合并。
<!-- vue 2.x -->
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div
<!-- result -->
<div id="red"></div>

<!-- vue 3.x -->
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div
<!-- result -->
<div id="blue"></div>

<!-- template -->
<div v-bind="{ id: 'blue' }" id="red" ></div
<!-- result -->
<div id="red"></div>
  • v-for 中的 Ref 数组
    1. 在 Vue 2 中,在 v-for 里使用的 ref attribute 会用 ref 数组填充相应的 $refs property。当存在嵌套的 v-for 时,这种行为会变得不明确且效率低下。
    2. 在 Vue 3 中,这样的用法将不再在 $ref 中自动创建数组。要从单个绑定获取多个 ref,请将 ref 绑定到一个更灵活的函数上 (这是一个新特性)
<div v-for="item in list" :key="item" :ref="setItemRef"></div>

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el => {
      if (el) {
        itemRefs.push(el)
      }
    }

    return {
      setItemRef
    }
  },
}
</script>

- itemRefs 不必是数组:它也可以是一个对象,其 ref 会通过迭代的 key 被设置。
- 如果需要,itemRef 也可以是响应式的且可以被监听。

6、props emit ref

直接上代码吧 ~

<!-- 父组件 -->
<template>
  <div>
    <h2>{{ title }}</h2>
    <child :toChildTitle="toChildTitle" @handleChange="handleChange" ref="childRef"></child>
  </div>
</template>

<script>
import {onMounted, reactive, toRefs, ref} from "vue";

export default {
  setup() {
    // 需要定义 ref 注意需要return出去
    const childRef = ref()
    
    const state = reactive({
      title: '原来的title',
      toChildTitle: 'fa传递过去title'
    })
    
    onMounted(() => {
      // 父组件 获取/修改 子组件数据
      console.log(childRef.value.val)
      // 父组件 触发 子组件方法
      childRef.value.childFun()
    })
    
    const handleChange = (val) => {
      state.title = handleChange
    }

    return {
      ...toRefs(state),
      childRef,
      handleChange
    }
  },
}
</script>



<!-- 子组件 -->
<template>
  <div>
    <h2>{{ toChildTitle }}</h2>
    <div @click="changeBtn">点它</div>
  </div>
</template>

<script>
import {reactive, ref, toRefs, watch} from "vue";

export default {
  props: {
    toChildTitle: String,
  },

  setup(props, context) {
    const state = reactive({
      toChildTitle: props.toChildTitle, // 将prop传递来的值转换成响应式数据,这样修改这个toChildTitle的时候 父组件的toChildTitle值也会变化
      val: '其他参数', // 父组件需要使用的数据 需要return出去
    })
    // 触发父组件的方法
    const changeBtn = () => {
      context.emit('handleChange', '从child修改后的title')
    }
    // 父组件需要使用的方法 也需要return出去
    const childFun = () => {
        alert('child 的方法')
    }

    return {
      ...toRefs(state),
      changeBtn,
      childFun
    }
  },
}
</script>

7、provide 和 inject

provide 和inject 是vue提供的一对API 这对API 可以实现组件之间的通信 无论层级有多深 都可以通过这对API 来实现

provide

在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 来定义每个 property。 provide 函数允许你通过两个参数定义 property:

  • name ( 类型)
  • value
<script>
import { provide } from 'vue'

export default {
  setup() {
    provide('location', 'North Pole')
    provide('geolocation', {
      longitude: 90,
      latitude: 135
    })
  }
}
</script>

inject

在 setup() 中使用 inject 时,也需要从 vue 显式导入。导入以后,我们就可以调用它来定义暴露给我们的组件方式。 inject 函数有两个参数:

  • 要 inject 的 property 的 name
  • 默认值 (可选)
<script>
import { inject } from 'vue'

export default {
  setup() {
    const userLocation = inject('location', 'The Universe')
    const userGeolocation = inject('geolocation')

    return {
      userLocation,
      userGeolocation
    }
  }
}
</script>

响应式

  • 添加响应性: 为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive。
<script>
import { provide, reactive, ref } from 'vue'

export default {
  setup() {
    const location = ref('North Pole')
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })

    provide('location', location)
    provide('geolocation', geolocation)
  }
}
</script>
现在,如果这两个 property 中有任何更改,子组件中的值也将自动更新!
  • 修改响应式 property:

当使用响应式 provide / inject 值时,官方中建议尽可能将对响应式 property 的所有修改限制在定义 provide 的组件内部。

// 父组件
export default {
  setup() {
    const location = ref('North Pole')
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })
    
    // 修改provide中值的方法
    const updateLocation = () => {
      location.value = 'South Pole'
    }

    provide('location', location)
    provide('geolocation', geolocation)
    provide('updateLocation', updateLocation)
  }
}
</script>

// 子组件
export default {
  setup() {
    const userLocation = inject('location', 'The Universe')
    const userGeolocation = inject('geolocation')
    // 子组件调用父组件中修改值的方法
    const updateUserLocation = inject('updateLocation')

    return {
      userLocation,
      userGeolocation,
      updateUserLocation
    }
  }
}
</script>

如果要确保通过 provide 传递的数据不会被 inject 的组件更改,官方建议对提供者的 property 使用 readonly。

import { provide, reactive, readonly, ref } from 'vue'
export default {
  setup() {
    const location = ref('North Pole')
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })

    const updateLocation = () => {
      location.value = 'South Pole'
    }

    provide('location', readonly(location))
    provide('geolocation', readonly(geolocation))
    provide('updateLocation', updateLocation)
  }
}
</script>

8、

单文件组件 Composition API 语法糖 (

  • 组件引入了还要注册

  • 属性和方法都要在setup函数中返回,有的时候仅一个return就十几行甚至几十行

  • 不想写啊怎么办

好办,Vue3官方提供了script setup语法糖

<script setup>其实是使用组合式 API 的编译时语法糖。相比于普通的

  • 更少的样板内容,更简洁的代码。

  • 能够使用纯 Typescript 声明 props 和抛出事件。

  • 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。

  • 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)

只需要在script标签中添加setup,组件只需引入不用注册,属性和方法也不用返回,setup函数也不需要,甚至export default都不用写了,不仅是数据,计算属性和方法,甚至是自定义指令也可以在我们的template中自动获得。

但是这么过瘾的语法糖,还是稍微添加了一点点心智负担,因为没有了setup函数,那么props,emit,attrs怎么获取呢,就要介绍一下新的语法了。

setup script 语法糖提供了几个新的API来供我们使用:definePropsdefineEmits

  • defineProps 用来接收父组件传来的值props。

  • defineEmits 用来声明触发的事件表。

<template>
    <div>
        <h1 @click="handleClick">{{ data }}</h1>
        <span>{{ count }}</span>
        <son></son>
    </div>
</template>

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

// 属性和方法不需要返回,直接用
const data = 'this is data'
const count = ref(0)
function handleClick() {
    console.log('点击...')
}

// 组件不需要components注册
import son form './son.vue'

// 组件数据传递 props 和 emit 语法改变 => defineProps 、defineEmits
const props = defineProps({
  foo: String
})
const emit = defineEmits(['change', 'delete'])

// 对外暴露属性 - defineExpose
const childText = ref('子组件数据')
defineExpose({
    childText
})

// 不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup 
const post = await fetch('/api').then(() => {})

// nextTick
import { nextTick } from 'vue'
nextTick(() => {
    // ,,,
})

// css 变量注入
const state = reactive({
    color: 'red'
})
</script>
<style scoped>
    span {
        // 使用 v-bind 绑定state中的变量
        color: v-bind('state.color')
    }
</style>

9、style 特性

scoped

当 标签带有 scoped,它的 CSS 只会应用到当前组件的元素上。

深度选择器:处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类:

<style scoped>
.a :deep(.b) {
  /* ... */
}
</style>

TIP

通过 v-html 创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。

module

<style module> 标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件:

<template>
  <p :class="$style.red">
    This should be red
  </p>
</template>

<style module>
.red {
  color: red;
}
</style>

自定义注入名称:

<template>
  <p :class="classes.red">red</p>
</template>

<style module="classes">
.red {
  color: red;
}
</style>

与组合式 API 一同使用:        注入的类可以通过 useCssModule API 在 setup() 和

// 默认,返回 <styel module> 中的类
useCssModule()

// 命名,返回 <style module="classes"> 中的类
useCssModule('classes')

v-bind

单文件组件的 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上:

<template>
  <div class="text">hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style>
.text {
  color: v-bind(color);
}
</style>

这个语法同样也适用于

<template>
  <p>hello</p>
</template>

<script setup>
const theme = {
  color: 'red'
}
</script>

<style scoped>
p {
  color: v-bind('theme.color');
}
</style>

单文件组件状态驱动的 CSS 变量 () (非官方文档中看到)
<template>
  <div class="text">hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style vars="{ color }">
.text {
  color: var(--color);
}
</style>

10、getCurrentInstance

getCurrentInstance()是Vue3.x中的核心方法,用于访问实例上下文的router及vuex等。

  1. 概述:一个很重要的方法,获取当前组件的实例、上下文来操作router和vuex等。
  2. 引入:由vue提供,按需引入:import { getCurrentInstance} from 'vue';
  3. 使用:获取当前组件的上下文,推荐使用:const { proxy } = getCurrentInstance()。
import { getCurrentInstance } from 'vue';
// 获取当前组件实例
const instance = getCurrentInstance();
 
// 获取当前组件的上下文,下面两种方式都能获取到组件的上下文。
const { ctx }  = getCurrentInstance();  //  方式一,这种方式只能在开发环境下使用,生产环境下的ctx将访问不到
const { proxy }  = getCurrentInstance();  //  方式二,此方法在开发环境以及生产环境下都能放到组件上下文对象(推荐)
// ctx 中包含了组件中由ref和reactive创建的响应式数据对象,以及以下对象及方法;
proxy.$attrs
proxy.$data
proxy.$el
proxy.$emit
proxy.$forceUpdate
proxy.$nextTick
proxy.$options
proxy.$parent
proxy.$props
proxy.$refs
proxy.$root
proxy.$slots
proxy.$watch

二、vue3周边生态

1、vue-router

  • vue2.x中,可以通过this. r o u t e r 或者 t h i s . router或者this. route来获取或者操作路由。

  • 在vue3.0中,引入了Composition-api。在setup函数中无法使用this获取组件实例。新版本的vue-router也提供了支持Composition-api的hooks,例如useRouter,useRoute函数。

import {onMounted} from 'vue'
import {useRoute, useRouter} from "vue-router";

export default {
  setup() {
    const router = useRouter()
    const route = useRoute()
    
    onMounted(() => {
      const {id = ''} = route.params
    })

    function pushWithQuery(query) {
      router.push('/index')
    }
  },
}

另外,vue-router还提供了支持的Composition-api的两个路由守卫:update and leave

  • beforeRouteLeave:离开当前页面路由时触发,return false则阻止跳转,next中不能写参数
  • beforeRouteUpdate:动态路由 只有参数发生变化是才执行(通俗理解及跳转页面时)
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

export default {
  setup() {
    onBeforeRouteLeave((to, from) => {
      const answer = window.confirm(
        'Do you really want to leave? you have unsaved changes!'
      )
      if (!answer) return false
    })

    const userData = ref()

    onBeforeRouteUpdate(async (to, from) => {
      if (to.params.id !== from.params.id) {
        userData.value = await fetchUser(to.params.id)
      }
    })
  },
}

2、vuex

import { useStore } from 'vuex'

export default {
  setup() {
    const store = useStore()
    
    onMounted(() => {
      const data = store.state.someData
    })
    
    const handleClick = () => {
        store.commit('CHANGE_DATA', { a: 1})
    }
  },
}

三、其他

1、Teleport

Teleport 在国内大部分都翻译成了瞬间移动组件或任意传送门,也有把这个函数叫独立组件。 是一种能够将我们的模板移动到 DOMVue app 之外的其他位置的技术。

场景:像 modals,toast 等这样的元素,很多情况下,我们将它完全的和我们的 Vue 应用的 DOM 完全剥离,管理起来反而会方便容易很多。

原因在于如果我们嵌套在 Vue 的某个组件内部,那么处理嵌套组件的定位、z-index 和样式就会变得很困难。

另外,像 modals,toast 等这样的元素需要使用到 Vue 组件的状态(data 或者 props)的值。

这就是 Teleport 派上用场的地方。我们可以在组件的逻辑位置写模板代码,这意味着我们可以使用组件的 data 或 props。然后在 Vue 应用的范围之外渲染它。

使用

Teleport方法,可以把Dialog组件渲染到你任意想渲染的外部Dom上,不必嵌套再#app里了,这样就不会互相干扰了。你可以把Teleport看成一个传送门,把你的组件传送到你需要的地方。 teleport组件和其它组件没有任何其它的差异,用起来都是一样的。

  • 首先我们在 index.html 中添加我们需要传送到的位置。
<div id="app"></div>
<div id="modal"></div>
  • 将编写的组件包装在 teleport 组件中,还需要指定一个 to 属性,为该属性分配一个查询选择器,以标识目标元素。
<!-- to 属性就是目标位置 -->
<teleport to="#modal">
    <div v-if="visible" class="modal-wrap">
      <div class="modal-msg">我是一个 modal 文案</div>
    </div>
</teleport>

2、Suspense

等待异步组件时渲染一些额外内容,让应用有更好的用户体验。

试验性

Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。

生产环境请勿使用。

以上是官方的警告!


使用:

  • 首先我们先写一个异步组件

注意点:如果你要使用Suspense的话,要返回一个promise对象,而不是原来的那种JSON对象。

<template>
    <div>{{ result }}</div>
</template>
<script>
import axios from 'axios'
import { defineComponent } from 'vue'
export default {
    setup() {  //promise 语法糖  返回之后也是promise对象
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                return resolve({ result: "this is result" });
            }, 2000);
        });
    }
}
</script>
  • 使用suspense组件
<template>
  <div>
    <Suspense>
      // 这两个插槽名称是固定的
      // defalut:这里面写的就是我们正常要显示的组件和代码
      // fallback:这里写的就是应急代码,就是正常代码没有显示的时候的代码
      <template #default>
        <AsyncShow />
      </template>
      <template #fallback>
        <h1>Loading...</h1>
      </template>
    </Suspense>
  </div>
</template>

export default {
  components: {
    AsyncShow: defineAsyncComponent(() => import('./AsyncShow.vue'))
  }
}

  • 处理异步请求错误

在异步请求中必须要作的一件事情,就是要捕获错误,因为我们没办法后端给我们返回的结果,也有可能服务不通,所以一定要进行捕获异常和进行处理。

在vue3.x的版本中,可以使用onErrorCaptured这个钩子函数来捕获异常。在使用这个钩子函数前,需要先进行引入.

<template>
  <div>
    <h1 v-if="error">load async component error</h1>
    <Suspense>
      <template #default>
        <AsyncShow />
      </template>
      <template #fallback>
        <h1>Loading...</h1>
      </template>
    </Suspense>
  </div>
</template>

import { ref, defineAsyncComponent, onErrorCaptured} from "vue";

export default {
  components: {
    AsyncShow: defineAsyncComponent(() => import('./AsyncShow.vue'))
  },
  setup() {
    const error = ref(null);
    onErrorCaptured(e => {
      error.value = e;
      return false;
    });
    return { error };
  }
}

有了onErrorCaptured就可以直接在setup()函数中直接使用了。钩子函数要求我们返回一个布尔值,代表错误是否向上传递。这里的false一方面表示:错误不会冒泡给父组件;另一方面表示vue将停止该错误的传播。

3、片段(Fragment)

在 Vue2.x 中, template中只允许有一个根节点:

<template>
    <div>
        <span></span>
        <span></span>
    </div>
</template>

但是在 Vue3.x 中,你可以直接写多个根节点:

<template>
    <span></span>
    <span></span>
</template>

最后的最后

最后的最后给大家推荐几个vue3相关网址

猜你喜欢

转载自juejin.im/post/7103740259925491742