Vue3.0(四):Composition API的使用

Composition API

认识Composition API

前几篇文章中主要用Options API进行编写代码,但是有一些弊端

  • Options API弊端
    • 在 Options API中,有data、watch、computed、methods以及生命周期函数各种各样的选项
    • 而一个变量,为了实现某个功能,会分散在各个选项中,不利于代码的维护
  • 此时 Composition API 就出现了
    • 使用 setup函数可以将大部分选项代替,data、watch、computed、methods以及生命周期函数

ssetup函数的参数

  • setup函数主要有两个参数

    • props
    • context
  • props是一个对象,包含着父组件传递过来的属性

  • context主要包含三个属性

    • attrs:所有的非prop的attribute
    • **slots:**父组件传递过来的插槽(在以渲染函数返回时会有作用)
    • **emit:**当我们组件内部需要发出事件时,会用到emit(在setup函数中,不能访问this)
  • 简单实现

vue 复制代码
<template>
  <div>
    <span>{{ count }}</span>
    <button @click="add">+1</button>
    <button @click="deAdd">-1</button>
  </div>
</template>

<script>
//引入ref函数
import { ref } from "vue";
export default {
  setup() {
    //定义变量,此时count并不是响应式数据,需要用ref进行包裹
    let count = ref(0);
    //定义函数
    //要对count进行修改,需要通过.value的方式进行修改
    let add = () => count.value++;
    let deAdd = () => count.value--;

    //将变量以及函数返回出去,template中才可以使用
    return {
      count,
      add,
      deAdd,
    };
  },
};
</script>
  • 同时我们可以将内部的逻辑代码,封装单独的一个文件,而后进行引入
vue 复制代码
<script>
//引入函数
import counter from "./utils/counter";

export default {
  setup() {
    let { count, add, deAdd } = counter();
    return {
      count,
      add,
      deAdd,
    };
  },
};
</script>
-------还可以做以下优化
<script>
//引入函数
import counter from "./utils/counter";

export default {
  setup() {
   
    return {
      ...counter()
    };
  },
};
</script>

setup定义数据

  • 定义普通函数
js 复制代码
setup(){
    //可以直接使用,但不是响应式
    let message = "hello"
    
    return{
        message
    }
}
  • 定义响应式数据(reactive )
    • 定义复杂数据:对象/数组等,不能传入普通类型的,比如字符串等
    • 在 option api中data中定义的数据实际上就是交给了reactive函数
js 复制代码
import {reactive} from "vue"
setup(){
    //此时就是响应式了
    let obj = reactive({
        a:100,
        b:200
    })
    //对obj进行修改
    obj.a = 300
    return{
        obj
    }
}
  • 定义响应式数据(ref ):返回的是ref对象,需要使用.value进行获取值
    • 定义简单类型的响应式数据
    • 也可以定义复杂类型的数据
    • 但是在template中,会自动解包,即自动获取 变量.value的值,直接使用变量即可
js 复制代码
<template>
	{{count}}    
</template>

import {ref} from "vue"
setup(){
    //定义简单数据,也可以定义复杂数据
    let count = ref(0)
    //修改变量
    count.value = 100
    
    return {
    	count    
    }
}

ref定义数据和reactive定义数据的开发

  • 在实际开发中,使用ref 的频次比较多
    • ref可以定义复杂数据
  • reactive 应用场景
    • 应用于本地数据const obj = reactive({username:"123"}),不是从数据库中得来的
    • 多个数据之间是有关系/联系的 const user = reactive({username:"admin",password:"123456"})
  • ref 应用场景:其余的场景基本都用ref
    • 定义本地的简单数据
    • 定义从网络获取的数据
js 复制代码
const music = ref([])   
onMounted(()=>{
    const serverMusic = ["野狼","小苹果"]
    music.value = serverMusic
})

单向数据流的规范和做法

  • 父组件传递给子组件一个对象/数组,此时,子组件能否对其进行修改?
  • 在代码层面,子组件是可以进行修改的 ---但是,不符合规范
  • 因为父组件引用的子组件很多的情况下 ,若子组件擅自修改了对象中的数据,那么就不清楚到底是谁对数据进行了修改
  • 正确的做法
    • 父组件将数据传递给了子组件
    • 子组件抛出事件,以及要修改的值
    • 父组件使用 @子组件事件,来将数据进行修改

readonly函数

创建出来的数据也是响应式数据,但是不能对数据进行修改,但是我们可以对元数据进行修改

  • 其返回的也是一个Proxy对象,只是setter方法受到了限制
js 复制代码
setup(){
    let obj = ref({
        a:100
    })
    //此时readonlyObj不能进行修改,可以将其传给子组件
    let readonlyObj = readonly(obj)
    //若我们向修改其内容,可以对obj进行修改
    
}

Reactive判断的API

  • isProxy

    • 检查对象 是否由reactive或者readonly创建的Proxy
  • isReactive

    • 检查对象是否由 reactive创建的响应式代理
  • isReadonly

    • 检查对象 是否由readonly创建的只读代理
  • toRaw

    • 返回proxy代理对象的原始数据
  • shallowReactive

    • 创建一个响应式代理,但是只响应浅层次的数据
  • shallowReadonly

    • 创建一个响应式代理,但是不执行嵌套对象的深度只读转换

toRef和toRefs

  • 现在有如下代码
    • 我们在使用user的时候,想直接通过name age height的方式直接展示
    • 不是通过 user.name user.age user.height的方式展示
vue 复制代码
<template>
  <div>
    <div>{{ user.name }}-{{ user.age }}-{{ user.height }}</div>
    <button @click=""></button>
  </div>
</template>

<script>
//引入函数
import { reactive, ref, toRef, toRefs } from "vue";

export default {
  setup() {
    let user = reactive({
      name: "zhangcheng",
      age: 18,
      height: 1.88,
    });
    

    return {
      user,
    };
  },
};
</script>

<style scoped></style>
  • 为了实现上述要求可以将user进行解构
    • 之后将解构的数据返回出去
js 复制代码
 const {name,age,height} = user
  • 但是解构出来的变量,就是普通的变量,不再是响应式数据了
    • 因此我们可以通过 toRef和toRefs方法进行操作
js 复制代码
let user = reactive({
    name: "zhangcheng",
    age: 18,
    height: 1.88,
});

//toRefs对多个值进行ref转化
const { name, age } = toRefs(user);
// toRef对某个属性进行ref转化
const height = toRef(user, "height");
  • 这种做法实际上是将 name.value与user.name做了某种联系,使其中一方修改,另外一方也会进行修改

ref定义数据其他的API

unref

  • unref实际上是 语法糖
    • 其内部会先判断传进来的参数是否是ref对象,是的化就返回 ref.value,不是的化就直接返回值本身
js 复制代码
const a = ref(2)
const b = unref(a)//内部实际上是 b = isRef(a)?a.value:a

isRef

  • 判断值 是否是一个ref对象

setup函数中不能使用this

  • 在官方文档中,有对setup函数中不能使用this进行过提示
  • 大概意思如下
    • this并没有指向当前组件的实例
    • 在 setup被调用之前,data,computed、methods等都没有被解析

computed函数使用

当某些数据需要进行某些逻辑转化,再显示,就会用到计算属性 computed

  • 在里面传入一个回调函数,默认调用的是其中的getter方法
    • 此时 computedStr变量就是计算属性
    • 且这个计算属性,是只读的
js 复制代码
import {computed} from "vue"
setup(){
    const computedStr = computed(()=>{
        return "a+b"
    }) 
    
    return {
        computedStr
    }
}
  • 如果想要调用computed中的getter方法 ,需要在computed函数 中传入对象
    • computed函数返回值实际上是ref对象
    • 因此想要修改的话,需要使用 .value进行修改
js 复制代码
import {computed} from "vue"
//可以调用setter方法
const computedObjStr = computed({
    set: function (newValue) {
        const temp = newValue.split(" ");
        str.first = temp[0];
        str.last = temp[1];
    },
    get: function () {
        return str.first + str.last;
    },
});
//若想修改computedObjStr的话,需要computedObjStr.value="li si"

ref引入元素

此ref不是定义数据的ref

在vue中,我们可以通过ref this.$refs.ref进行获取元素,那么在setup函数中怎么使用

  • 直接在setup函数内部打印 ref引用的元素是不会获取到的
  • 因此,需要在onMounted生命周期中查看
html 复制代码
<template>
  <div>
    <div ref="table">123</div>
    <div ref="divEl">456</div>
  </div>
</template>

<script>
//引入函数
import { onMounted, ref, reactive, computed } from "vue";

export default {
  setup() {
    const refTable = ref();
    const divEl = ref();
    onMounted(() => {
      //需要使用.value获取值
      //对于组件使用也是如此
      console.log(refTable.value);
      console.log(divEl.value);
    });
    return {
      //两种返回方式,一种是对象的增强写法
      table: refTable,
      divEl,
    };
  },
};
</script>

<style scoped></style>

生命周期钩子

在setup中使用生命周期的方式 onX

  • 需要进行导入 生命周期的函数调用
  • 之后再 其内部传入函数,才是生命周期的函数
  • 在 vue的官网中,有这样的提示,setup就是围绕 beforeCreate和created进行的,因此之前在该生命周期中做的事,可以直接写在setup中
选项式API Hook inside setup
beforeCreate 不需要
created 不需要
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
activated onActivated
deactivated onDeactivated
js 复制代码
import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onActivated,
  onDeactivated,
} from "vue";
setup() {
    onMounted(() => {
        console.log("onMounted 执行");
    });
    return {};
},

provide和inject函数

  • 在 option API中我们用到了provide和inject用于组件之间传递数据
  • 那么 在 composition API中要怎么使用
  • provide(key,value):key是传递参数的代表,value 是具体数据
  • inject(key,default):key是父组件传递参数的代表,default是父组件没有传递数据的时候的默认值
js 复制代码
import { provide, inject } from "vue";
//父组件
const obj = ref({ a: 100 })
provide("objStr", obj)

//子组件
inject("objStr",{a:0})

侦听数据的变化

在option API中使用的是watch中进行监听数据的变化,那么在composition中怎么进行监听

watch

  • 首先导入 watch函数
  • 基本使用
    • 可以监听多个数据
    • newValue, oldValue返回的都是proxy对象
js 复制代码
import { reactive, ref, watch } from "vue";

export default {
  setup() {
    /**
     * 参数
     * 1.数据源
     *    针对于ref包裹的需要监听.value
     *    使用数组可以监听多个数据
     * 2.执行的逻辑
     * 3.配置项:配置deep以及immediate
     *    默认就是深度监听
     */
     //定义数据
    let obj = ref({ a: 100, b: { c: 200 } });
    let objRea = reactive({ username: "zhangcheng" });
      
      //对数据进行监听
    watch([objRea, obj.value], (newValue, oldValue) => {
      console.log(newValue, oldValue);
    },
      {
        immediate: true,
      });
    return { obj, objRea };
  },
};
};
  • 若 newValue, oldValue返回的是普通对象
    • 数据源需要传递一个函数
    • 且配置项中需要进行深度监听的配置,默认不启用深度监听
js 复制代码
    /**
     * 参数
     * 1.数据源
     *    针对于ref包裹的需要监听.value
     *    使用数组可以监听多个数据
     * 2.执行的逻辑
     * 3.配置项:配置deep以及immediate
     *    默认就是深度监听
     */
    let obj = ref({ a: 100, b: { c: 200 } });
    let objRea = reactive({ username: "zhangcheng", b: { c: 200 } });
    watch(
      () => ({ ...obj.value, ...objRea }),
      (newValue, oldValue) => {
        console.log(newValue, oldValue);
      },
      {
        deep: true,
        immediate: true,
      }
    );

watchEffect

相较于watch而言,watchEffect可以自动监听依赖变量的变化

  • 传入warchEffect中的回调函数,在页面初次加载的时候会先执行一次
  • 而后在依赖的数据发生变化的时候,也会执行回调函数
  • 同时,达到某一个条件的时候,可以停止数据的监听
js 复制代码
const stopWatch = watchEffect(() => {
    //当obj.value发生变化的时候,会自动调用这个回调函数
    console.log(obj.value);

    //执行watchEffect的返回值,就可以停止对数据的监听
    if (obj.value > 10) {
        stopWatch();
    }
});

script setup语法

本质就是setup函数的语法糖

  • 就是在 script的标签中加入setup的attribute
    • 这种写法有更好的运行效率,因为内部会将script与template模板放在同一个作用域中
    • 代码会更加的简洁
    • 有更好的提示
html 复制代码
<script setup>
    //实际上就是在setup中在编写代码
    //不用写return
</script>

顶层的绑定会直接暴露给模板

  • 在 script setup标签中直接写的内容,在template中可以直接使用
    • 若是在函数中,定义一个变量,则不属于顶层
html 复制代码
<template>
  {{ message }}
</template>

<script setup>
//引入函数
import { ref } from "vue";

let message = ref(123);
</script>

<style scoped></style>

导入组件直接使用

  • 导入的组件,无需注册, 直接在template中使用即可
html 复制代码
<template>
 <home-vue></home-vue>
</template>

<script setup>
//导入组件直接使用
import HomeVue from "./components/HomeVue";

</script>

<style scoped></style>
  • 子组件通过 defineProps()接受父组件传进来的参数,defineEmits()发送事件
html 复制代码
<template>
  <button @click="messageClick">{{ message }}</button>
</template>

<script setup>
//defineEmits和defineProps都是内部绑定的,因此不用注册

//接受参数
defineProps({
  message: {
    type: String,
    default: "默认信息",
  },
});

//定义发射的事件
const messageEmit = defineEmits(["message-click"]);
function messageClick() {
  messageEmit("message-click", "传递参数");
}
</script>

<style scoped></style>

defineExpose将子组件中的数据暴露

默认都是在 <script setup></script>标签中写的代码

  • 子组件
    • 定义变量,函数等,之后暴露出去
js 复制代码
import { ref } from "vue";

function expsoeFun() {
  console.log("暴露出去了");
}
const message = ref("我是变量");
//子组件中有一个函数想被父组件直接调用,需要用到 defineExpose将函数暴露,或者将变量暴露
defineExpose({ expsoeFun, message });
  • 父组件
    • 首先通过ref获取子组件,之后调用其暴露的内容即可
js 复制代码
import { onMounted, ref } from "vue";
import HomeVue from "./components/HomeVue.vue";

//绑定实例
const homeVue = ref();
onMounted(() => {
    //调用实例中的方法
  homeVue.value.exposeFun();
    //获取实例中的变量
  console.log(homeVue.value.message);
});
相关推荐
梦帮科技2 分钟前
Web 漏洞攻防深度:WAF 规则逃逸技术(畸形分块传输、双重编码)与对抗加固
linux·前端·网络安全·ssl·web
兰亭妙微UI设计公司9 分钟前
兰亭妙微软件界面设计公司的解锁按钮设计10大密码
开发语言·前端·javascript
迪幻32 分钟前
封装自己的 npm 依赖并存储到个人 GitHub
前端·npm·github
u0111026751 小时前
WebP 转成 PNG 后为什么更大?图片格式转换不等于压缩
前端·javascript·vue.js
万少1 小时前
Qoder sonus模型 20分钟,复刻 马斯克 飞天汽车
前端·后端·算法
seconp1 小时前
基于Spring Boot和Vue 3的智慧旅游网站:住宿预订、行程规划与千问接入
vue.js·spring boot·毕业设计·软件工程·课程设计·旅游·毕设
java1234_小锋1 小时前
【技术专题】HTML5 网页技术 - HTML5 列表标签
前端·html·html5
强二小哥帅1 小时前
Kotlin 协程复习笔记
前端
不可能片场1 小时前
工程实践 原子替换改了 ctime 怎么修
前端·electron
暧暧内含光1 小时前
HTTP 文件传输体系全解:从协议标头、OSS 架构到流媒体分片
前端