• vue3 Composition API 组合式api


    1、Setup

    (1)参数

    第一个参数:props
    第二个参数:context 包括:attrs、slots、emit

    (2)返回值

    返回一个对象{},对象中的数据可以在template中被使用,来替代data。

    (3)setup函数执行时机和注意点

    ① setup执行时机

    beforeCreate: 组件刚刚被创建出来, 组件的data和methods还没有初始化好setup
    Created : 组件刚刚被创建出来, 组件的data和methods已经初始化好

    ② setup注意点

    • 由于在执行setup函数的时候, 还没有执行Created生命周期方法,所以在setup函数中,是无法使用data和methods。
    • 由于我们不能在setup函数中使用data和methods,所以Vue为了避免我们错误的使用, 它直接将setup函数中this修改成了undefined

    2、Reactive

    (1)什么是reactive

    • reactive是Vue3中提供的实现响应式数据的方法
    • 在Vue2中响应式数据是通过defineProperty来实现的
      而在Vue3中响应式数据是通过ES6的Proxy来实现的

    (2)reactive注意点:

    reactive可以实现数据响应式。reactive 对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型,如果我们传入一个基本数据类型,会报一个警告。

     const account = reactive({
         username: "xhl",
         password: "123456"
      })
    
    • 1
    • 2
    • 3
    • 4

    (3)Reactive的应用场景:

    条件一: reactive应用于本地的数据
    条件二: 多个数据之间是有关系/联系(聚合的数据, 组织在一起会有特定的作用)

    3、Ref

    (1)什么是ref

    ref和reactive一样, 也是用来实现响应式数据的方法,由于reactive必须传递一个对象, 所以导致在企业开发中,如果我们只想让某个变量实现响应式的时候会非常麻烦,所以Vue3就给我们提供了ref方法, 实现对简单值的监听。

    (2)ref本质

    ref底层的本质其实还是reactive系统会自动根据我们给ref传入的值将它转换成

    ref(xx) -> reactive({value:xx})
    
    • 1

    (3)Ref实现数据响应式,可以定义简单类型数据,也可以定义复杂类型数据。

     const counter = ref(0)
     function increment() {
         counter.value++
      }
    
    • 1
    • 2
    • 3
    • 4

    (4) ref注意点

    • 在Vue template中使用ref的值不用通过value获取
    • 在JS中使用ref的值必须通过value获取

    (5)ref的应用场景: 其他的场景基本都用ref(computed)

    定义本地的一些简单数据:

     const message = ref("Hello World")
     const counter = ref(0)
     const name = ref("why")
     const age = ref(18)
    
    • 1
    • 2
    • 3
    • 4

    定义从网络中获取的数据也是使用ref:

     const musics = ref([])
     onMounted(() => {
       const serverMusics = ["海阔天空", "小苹果", "野狼"]
       musics.value = serverMusics
       console.log(musics.value);
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4、ref和reactive的区别

    (1) ref和reactive区别

    如果在template里使用的是ref类型的数据, 那么Vue会自动帮我们添加.value
    如果在template里使用的是reactive类型的数据, Vue不会自动帮我们添加.value

    (2) Vue是如何决定是否需要自动添加.value的

    Vue在解析数据之前, 会自动判断这个数据是否是ref类型的,
    如果是就自动添加.value, 如果不是就不自动添加.value

    (3) Vue是如何判断当前的数据是否是ref类型的

    通过当前数据的__v_ref来判断的
    如果有这个私有的属性, 并且取值为true, 那么就代表是一个ref类型的数据

    (4) 我们如何判断数据到底是ref还是reactive

    通过isRef / isReactive 方法

    5、Vue2和vue3响应式区别

    Vue2:利用object.defineproperty实现,无法监听数组的修改和新增object属性,需使用set方法更新
    Vue3:利用es6的proxy实现,可以实现数组的修改和新增object属性的监听,实现响应式。

    6、Readonly

    readonly返回的对象都是不允许修改的,readonly会返回原生对象的只读代理(也就是它依然是一个Proxy,这是一个proxy的set方法被劫持,并且不能对其进行修改)。
    readonly方法会传入三个类型的参数:

    • 类型一:普通对象;
    • 类型二:reactive返回的对象;
    • 类型三:ref的对象;
    	 const info = reactive({
    	      name: "why",
    	      age: 18,
    	      height: 1.88
    	    })
    	 const roInfo = readonly(info)
    	 function changeRoInfoName (payload) {
    	    info.name = payload
    	 }
    	roInfo不能被修改,但是info可以被修改
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7、Reactive其它函数

    ◼isProxy
    检查对象是否是由 reactive 或 readonly创建的 proxy。
    ◼ isReactive
    检查对象是否是由 reactive创建的响应式代理:如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
    ◼ isReadonly
    检查对象是否是由 readonly 创建的只读代理。
    ◼ toRaw
    返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。从Reactive 或 Ref中得到原始数据, 做一些不想被监听的事情(提升性能)。

    	  let obj = {name:'lnj', age:18};
    	  let state = reactive(obj);
    	  let obj2 = toRaw(state);
    	  console.log(obj === obj2); // true
    	  console.log(obj === state); // false
    	  // state和obj的关系:
    	  // 引用关系, state的本质是一个Proxy对象, 在这个Proxy对象中引用了obj	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ◼ shallowReactive
    创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。
    ◼ shallowReadonly
    创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。

    8、toRefs、toRef

    	const info = reactive({
    	   name: "why",
    	   age: 18,
    	   height: 1.88
    	})
    	  // reactive被解构后会变成普通的值, 失去响应式
    	  const { name, age } = toRefs(info)  
    	//toRefs:把对象里面的所有key全部转化成ref
    	  const height = toRef(info, "height")
    	//toRef:把对象里面的某个key转化成ref
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    9、ref其它函数

    ◼unref
    如果我们想要获取一个ref引用中的value,那么也可以通过unref方法:如果参数是一个 ref,则返回内部值,否则返回参数本身; 这是 val = isRef(val) ? val.value : val 的语法糖函数;
    ◼ isRef
    判断值是否是一个ref对象。
    ◼ shallowRef
    创建一个浅层的ref对象;
    ◼ triggerRef
    手动触发和 shallowRef 相关联的副作用

    10、Computed

    	import { reactive, computed, ref } from 'vue'
    	const names = reactive({
    	  firstName: "kobe",
    	  lastName: "bryant"
    	})
    	const fullname1 = computed(() => {
    	  return names.firstName + " " + names.lastName
    	})
    	const score = ref(89)
    	const scoreLevel = computed(() => {
    	  return score.value >= 60 ? "及格": "不及格"
    	})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    11、ref获取元素和组件

    我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;

    	 <!-- 1.获取元素 -->
    	 <h2 ref="titleRef">我是标题</h2>
    	 <button ref="btnRef">按钮</button>
    	 <!-- 2.获取组件实例 -->
    	 <show-info ref="showInfoRef"></show-info>
    	
    	import { ref, onMounted } from 'vue'
    	import ShowInfo from './ShowInfo.vue'
    	
    	const titleRef = ref()
    	const btnRef = ref()
    	const showInfoRef = ref()
    	
    	onMounted(() => {
    	  console.log(titleRef.value)
    	  console.log(btnRef.value)
    	  console.log(showInfoRef.value)
    	showInfoRef.value.showInfoFoo()
    	})
    	
    	return {
    	  titleRef,
    	  btnRef,
    	  showInfoRef,
    	  getElements
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    12、生命周期钩子

    beforeCreate 、created -> setup
    以下生命周期都改成一个方法,在setup中使用:

    	beforeMount -> onBeforeMount
    	mounted -> onMounted
    	beforeUpdate -> onBeforeUpdate
    	updated -> onUpdated
    	beforeDestory-> onBeforeUnmount
    	destoryed-> onUnmounted
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    新增:
    onErrorCaptured:每当事件处理程序或生命周期钩子抛出错误时,Vue 会调用该钩子。
    onRenderTracked:注册一个钩子,在捕获了后代组件传递的错误时调用。
    onRenderTriggered:注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。

    	<script lang="ts">
    	import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,
    	        onBeforeUnmount,onUnmounted,onErrorCaptured,
    	        onRenderTracked,onRenderTriggered } from 'vue'
    	export default{
    	  name:'App',
    	  setup(){
    	    onBeforeMount(()=>{
    	      console.log('onBeforeMount'); 
    	    })
    	    onMounted(()=>{
    	      console.log('onMounted'); 
    	    })
    	    onBeforeUpdate(()=>{
    	      console.log('onBeforeUpdate'); 
    	    })
    	    onUpdated(()=>{
    	      console.log('onUpdated'); 
    	    })
    	    onBeforeUnmount(()=>{
    	      console.log('onBeforeUnmount'); 
    	    })
    	    onUnmounted(()=>{
    	      console.log('onUnmounted'); 
    	    })
    	    onErrorCaptured((err)=>{
    	      console.log('onErrorCaptured',err);
    	    })
    	    onRenderTracked((event)=>{
    	      console.log('onErrorCaptured',event);
    	    })
    	    onRenderTriggered((event)=>{
    	      console.log('onRenderTriggered',event);
    	    })
    	  }
    	}
    	</script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    13、provide函数和inject函数

    通过 provide来提供数据,provide可以传入两个参数:
    name:提供的属性名称;
    value:提供的属性值;

    	<template>
    	  <show-info></show-info>
    	</template>
    	
    	<script>
    	  import { provide, ref } from 'vue'
    	  import ShowInfo from './ShowInfo.vue'
    	  export default {
    	    components: {
    	      ShowInfo
    	    },
    	    setup() {
    	      const name = ref("why")
    	      provide("name", name)
    	      provide("age", 18)
    	      return {
    	        name
    	      }
    	    }
    	  }
    	</script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    ShowInfo:

    	<template>
    	  <div>ShowInfo: {{ name }}-{{ age }}-{{ height }} </div>
    	</template>
    	
    	<script>
    	  import { inject } from 'vue'
    	  export default {
    	    setup() {
    	      const name = inject("name")
    	      const age = inject("age")
    	      const height = inject("height", 1.88)
    	      return {
    	        name,
    	        age,
    	        height
    	      }
    	    }
    	  }
    	</script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    14、Watch、watchEffect

    watchEffect:用于自动收集响应式数据的依赖;
    watch:需要手动指定侦听的数据源。

    (1)Watch:

    watch需要侦听特定的数据源,并且执行其回调函数;
    默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;

    	  import { reactive, ref, watch } from 'vue'
    	  // 1.定义数据
    	  const message = ref("Hello World")
    	  const info = reactive({
    	    name: "why",
    	    age: 18,
    	    friend: {
    	      name: "kobe"
    	    }
    	  })
    	  // 2.侦听数据的变化
    	  watch(message, (newValue, oldValue) => {
    	    console.log(newValue, oldValue)
    	  })
    	  watch(info, (newValue, oldValue) => {
    	    console.log(newValue, oldValue)
    	    console.log(newValue === oldValue)
    	  }, {
    	    immediate: true
    	  })
    	  // 3.监听reactive数据变化后, 获取普通对象
    	  watch(() => ({ ...info }), (newValue, oldValue) => {
    	    console.log(newValue, oldValue)
    	  }, {
    	    immediate: true,
    	    deep: true
    	  })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    Watch可以使用数组侦听多个数据源

    	 const message = ref("Hello World")
    	 const name=ref('naixi')
    	 watch([message,name], (newValue, oldValue) => {
    	    console.log(newValue, oldValue)
    	 })
    
    • 1
    • 2
    • 3
    • 4
    • 5

    watchEffect:只要依赖的数据发生变化,就会监听到并执行回调函数。
    首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
    其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
    如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。

    	      import { watchEffect, ref } from 'vue'
    	 	  const counter = ref(0)
    	      const name = ref("why")
    	
    	      // 1.watchEffect传入的函数默认会直接被执行
    	      // 2.在执行的过程中, 会自动的收集依赖(依赖哪些响应式的数据)
    	      const stopWatch = watchEffect(() => {
    	        console.log("-------", counter.value, name.value)
    	        // 判断counter.value > 10
    	        if (counter.value >= 10) {
    	          stopWatch()  //停止监听
    	        }
    	      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    15、Teleport-封装对话框,挂载在全局dom上

    <template>
    <teleport to="#modal">
          <div id="center" v-if="isOpen">
            <h2><slot>this is a modal</slot></h2>
            <button @click="buttonClick">Close</button>
          </div>
    </teleport>
    </template>
    <script lang="ts">
    import { defineComponent } from 'vue'
    export default defineComponent({
      props: {
        isOpen: Boolean,
      },
      emits: {
        'close-modal': null
      },
      setup(props, context) {
        const buttonClick = () => {
          context.emit('close-modal')
        }
        return {
          buttonClick
        }
      }
    })
    </script>
    <style>
      #center {
        width: 200px;
        height: 200px;
        border: 2px solid black;
        background: white;
        position: fixed;
        left: 50%;
        top: 50%;
        margin-left: -100px;
        margin-top: -100px;
      }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    调用:

      const modalIsOpen = ref(false)
        const openModal = () => {
          modalIsOpen.value = true
        }
        const onModalClose = () => {
          modalIsOpen.value = false
        }
    <button @click="openModal">Open Modal</button><br/>
    <modal :isOpen="modalIsOpen" @close-modal="onModalClose">
     My Modal !!!!</modal>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    16、Suspense - 异步请求

    Suspense 中可以添加多个异步组件,只有default中所有内容加载完成,才会显示。

    	<Suspense>
    	  <template #default>
    	    <async-show />
    	    <dog-show />
    	  </template>
    	  <template #fallback>
    	    <h1>Loading !...</h1>
    	  </template>
    	</Suspense>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    17、SetUp语法糖

    当使用 setup语法 的时候,任何在script中声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容,包括组件) 都能在模板中直接使用。

    	<template>
    	  <show-info></show-info>
    	</template>
    	<script setup>
    	  // 1.所有编写在顶层中的代码, 都是默认暴露给template可以使用
    	  import { ref, onMounted } from 'vue'
    	  import ShowInfo from './ShowInfo.vue'   //组件可以直接使用
    	  // 2.定义响应式数据
    	  const message = ref("Hello World")
    	  console.log(message.value)
    	  // 3.定义绑定的函数
    	  function changeMessage() {
    	    message.value = "你好啊, 李银河!"
    	  }
    	  function infoBtnClick(payload) {
    	    console.log("监听到showInfo内部的点击:", payload)
    	  }
    	  // 4.获取组件实例
    	  const showInfoRef = ref()
    	  onMounted(() => {
    	    showInfoRef.value.foo()
    	  })
    	</script>
    	defineProps() 、defineEmits()、defineExpose():
    	<template>
    	  <div>
    	    <div>ShowInfo: {{ name }}-{{ age }}</div>
    	    <button @click="showInfoBtnClick">showInfoButton</button>
    	  </div>
    	</template>
    	
    	<script setup>
    	// 定义props
    	const props = defineProps({
    	  name: {
    	    type: String,
    	    default: "默认值"
    	  },
    	  age: {
    	    type: Number,
    	    default: 0
    	  }
    	})
    	// 绑定函数, 并且发出事件
    	const emits = defineEmits(["infoBtnClick"])
    	function showInfoBtnClick () {
    	  emits("infoBtnClick", "showInfo内部发生了点击")
    	}
    	// 定义foo的函数
    	function foo () {
    	  console.log("foo function")
    	}
    	defineExpose({  
    	  foo   //只有通过defineExpose暴露出去的值,外界调用组件时才能调用到
    	})
    	</script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  • 相关阅读:
    LAXCUS分布式操作系统相比LINUX的优势
    第一套模拟.py
    visual studio禁用qt-vsaddin插件更新
    mysql 查询在一张表不在另外一张表的记录
    WPF中DataContext的绑定技巧
    Vue-router路由
    多线程编程【条件变量】
    词云简单生成
    逆向-还原代码之url_encode (Interl 64)
    通过示例详细了解ES6导入导出模块
  • 原文地址:https://blog.csdn.net/weixin_45002586/article/details/127958160