Vue3中Compositions API的使用(一)
文章目录
- Options API的弊端
- Composition API
- setup函数的参数
- setup函数返回值
- setup响应式函数
- 1.Reactive API
- 2.Ref API
- setup其他的函数
- 1.readonly函数
- 2.reactive的其他函数
- 3.toRefs函数
- 4.ref的其他函数
Options API的弊端
在Vue2中,我们编写组件的方式是Options API:
- Options API的一大特点就是在对应的属性中编写对应的功能模块;
- 比如data定义数据、methods中定义方法、computed中定义计算属性、watch中监听属性改变,也包括生命周期钩子;
但是这种代码有一个很大的弊端:
- 当我们实现某一个功能时,这个功能对应的代码逻辑会被拆分到各个属性中;
- 当我们组件变得更大、更复杂时,逻辑关注点的列表就会增长,那么同一个功能的逻辑就会被拆分的很分散;
- 尤其对于那些一开始没有编写这些组件的人来说,这个组件的代码是难以阅读和理解的(阅读组件的其他人);
下面我们来看一个非常大的组件,其中的逻辑功能按照颜色进行了划分:
- 这种碎片化的代码使用理解和维护这个复杂的组件变得异常困难,并且隐藏了潜在的逻辑问题;
- 并且当我们处理单个逻辑关注点时,需要不断的跳到相应的代码块中;
Composition API
Composition API能帮助我们, 将同一个逻辑关注点相关的代码收集在一起。
- 也有人把Vue Composition API简称为VCA。
那么既然知道Composition API想要帮助我们做什么事情,接下来看一下到底是怎么做呢?
为了开始使用Composition API,我们需要有一个可以实际使用它(编写代码)的地方;
在Vue组件中,这个位置就是 setup 函数;
setup其实就是组件的另外一个选项:
只不过这个选项强大到我们可以用它来替代之前所编写的大部分其他选项;
比如methods、computed、watch、data、生命周期等等;
接下来我们一起学习这个函数的使用:
函数的参数
函数的返回值
setup函数的参数
我们先来研究一个setup函数的参数,它主要有两个参数
第一个参数:props
第二个参数:context
props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取:
对于定义props的类型,我们还是和之前的规则是一样的,在props选项中定义;
并且在template中依然是可以正常去使用props中的属性,比如message;
如果我们在setup函数中想要使用props,那么不可以通过 this 去获取(后面我会讲到为什么);
因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可;
另外一个参数是context, context是一个对象,我们也称之为是一个SetupContext,它里面包含三个属性:
attrs:所有的非prop的attribute;
slots:父组件传递过来的插槽(这个在以渲染函数返回时会有作用,后面会讲到);
emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);
setup函数返回值
setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?
setup的返回值可以在模板template中被使用;
也就是说我们可以通过setup的返回值来替代data选项;
甚至是我们可以返回一个执行函数来代替在methods中定义的方法, 接下来我们使用setup函数完成一个计数器, 对setup进行一个初体验 :
<template><div class="app"><h2>当前计数: {{ counter }}</h2><button @click="increment">+</button><button @click="decrement">-</button></div>
</template><script>export default {// 使用setup函数setup() {// setup函数定义普通的数据, 缺点数据不是响应式的let counter = 100// setup函数定义函数(方法)const increment = () => {counter++}// setup函数定义函数(方法)const decrement = () => {counter--}// 外部要使用的数据需要通过return导出return {counter,increment,decrement}}}
</script>
但是,如果我们将 counter 在 increment 或者 decrement进行操作时,是否可以实现界面的响应式呢?
答案是不可以, 我们发下上面计数器值其实是改变了的, 但是并没有渲染到界面上;
这是因为对于一个定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作;
setup响应式函数
如果想为在setup中定义的数据提供响应式的特性,那么我们有如下两种常见的方案
1.Reactive API
方式一: 我们可以使用reactive的函数, 为在setup中定义的数据提供响应式的特性 :
reactive函数常用于定义复杂类型的数据
如下定义完成后, account这个数据就是定义成响应式的了
<template><div class="app"><h2>账号: {{ account.userName }}</h2><h2>密码: {{ account.passWord }}</h2></div>
</template><script>// 引入reactive函数import { reactive } from 'vue'export default {setup() {// 使用reactive函数定义数据// reactive函数要求 传入一个复杂数据类型, 数组对象都可以const account = reactive({userName: "chenyq",passWord: "123456"})// 将定义的数据account返回return {account}}}
</script>
那么这是什么原因呢?为什么就可以变成响应式的呢?
这是因为当我们使用reactive函数处理我们的数据之后,数据再次被使用时就会进行依赖收集;
当数据发生改变时,所有收集到的依赖都是进行对应的响应式操作(比如更新界面);
事实上,我们编写的data选项,也是在内部交给了reactive函数将其变成响应式对象的;
2.Ref API
方式二: 使用ref函数
reactive API对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型:
如果我们传入一个基本数据类型(String、Number、Boolean)会报一个警告;
这个时候Vue3给我们提供了另外一个API:ref API
ref函数可以定义一个简单类型的数据, 也可以定义一个复杂类型的数据
ref 会返回一个可变的响应式对象,该对象作为一个 响应式的引用 维护着它内部的值,这就是ref名称的来源;
它内部的值是在ref的 value 属性中被维护的;
这里有两个注意事项:
在模板中引入ref的值时,Vue会自动帮助我们进行解包操作,所以我们并不需要在模板中通过 ref.value 的方式来使用;
但是在 setup 函数内部,它依然是一个 ref引用, 所以对其进行操作时,我们依然需要使用 ref.value的方式;
// 引入ref函数
import { ref } from 'vue'export default {setup() {// 使用ref定义数据, 此时message已经是响应式的了let message = ref("hello ref")// 返回定义的数据return {message}}
}
例如我们给添加一个按钮测试 :
<template><div class="app"><!-- template模板中使用ref, Vue会自动帮助我们解包 --><h2>{{ message }}</h2><button @click="btnClick">按钮</button></div></template><script>// 引入reactive函数import { reactive } from 'vue'import { ref } from 'vue'export default {setup() {// 使用ref定义数据let message = ref("hello ref")const btnClick = () => {// setup中需要通过message.vue的方式拿到ref里面的值message.value = "你好 ref"}// 将定义的数据account返回return {message,btnClick}}}
</script>
setup其他的函数
1.readonly函数
我们先来看如这样一个案例:
定义一个父组件App.vue, 再定义一个子组件ShowInfo
通过reactive获取到一个响应式的对象info, 在父组件中展示info对象, 并将对象info传出
<template><div class="app"><h2>app: {{ info }}</h2><show-info :info="info" /></div>
</template><script>
import { reactive } from 'vue'
import ShowInfo from './ShowInfo.vue'export default {components: {ShowInfo},setup() {const info = reactive({name: "chenyq",age: 18,height: 1.88})return {info}}}
</script>
子组件ShowInfo对接收父组件传递的info对象并展示, 在子组件定义一个button按钮, 点击button使info对象的name属性修改
<template><div class="info"><h2>ShowInfo: {{info}}</h2><button @click="info.name = 'kaisa'">按钮</button></div>
</template><script>export default {props: {info: {type: Object,default: () => ({})}}}
</script>
我们发现, 由于组件传递对象时, 是传递的对象的引用, 因此当子组件内容修改时, 父组件中的内容同样会被修改
上面这种做法是可行的, 但是它好用吗? 符合规范吗
这种做法是不符合规范的, 在Vue中有单向数据流的规范, 指的是子组件中只能拿到数据, 不能修改
如果确实需要修改, 按照规范我们在子组件中将事件传递到父组件, 由父组件来修改数据
setup(props, context) {// 发送自定义 事件让父组件修改数据function showInfoClick() {context.emit("changeInfo", "kaisa")}return {showInfoClick}
}
我们知道单项数据流规范是不允许子组件修改的, 但是在公司实际开发中, 其他开发者如果不知道这个规范, 就有可能在子组件中修改数据, 那么我们如何避免这种情况呢?
Vue3为我们提供了readonly的方法, 可以避免他人子组件修改父组件的数据;
readonly会返回原始对象的只读代理(也就是它依然是一个Proxy,这是一个proxy的set方法被劫持,并且不能对其进行修改);
在开发中常见的readonly方法会传入三个类型的参数:
类型一:普通对象;
类型二:reactive返回的对象;
类型三:ref的对象;
在readonly的使用过程中,有如下规则:
readonly返回的对象都是不允许修改的, 但是经过readonly处理的原来的对象是允许被修改的;
比如
const info = readonly(obj)
,info对象是不允许被修改的;但是可以通过修改obj来修改info, obj被修改时,readonly返回的info对象也会被修改;
但是我们不能去修改readonly返回的对象info;
setup() {const info = reactive({name: "chenyq",age: 18,height: 1.88})// 为info包裹一个readonly, 子组件就无法修改const newInfo = readonly(info)console.log(newInfo)return {info,changeInfo,newInfo}
}
2.reactive的其他函数
isProxy
检查对象是否是由 reactive 或 readonly创建的 proxy。
isReactive
检查对象是否是由 reactive创建的响应式代理:
如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
isReadonly
检查对象是否是由 readonly 创建的只读代理。
toRaw
返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。
shallowReactive
创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。
shallowReadonly
创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。
3.toRefs函数
如果我们使用ES6的解构语法,对reactive返回的对象进行解构获取值,那么解构之后无论是修改结构后的变量,还是修改reactive返回的state对象,数据都不再是响应式的:
setup() {const info = reactive({name: "chenyq",age: 18,height: 1.88})const { name, age, height } = info
}
那么有没有办法让我们解构出来的属性是响应式的呢?
Vue为我们提供了一个toRefs的函数,可以将reactive返回的对象中的属性都转成ref;
那么我们再次进行结构出来的 name 和 age 本身都是 ref的;
setup() {const info = reactive({name: "chenyq",age: 18,height: 1.88})// 当我这样来做的时候, 会分别返回三个ref对象, 它们是响应式的const { name, age, height } = toRefs(info)
}
这种做法相当于已经在state.name和ref.value之间建立了 链接,任何一个修改都会引起另外一个变化;
toRef函数
如果我们只希望转换一个reactive对象中的属性为ref, 那么可以使用toRef的方法
setup() {const info = reactive({name: "chenyq",age: 18,height: 1.88})// 单独解构某一属性且具有响应式const height = toRef(info, "height")
}
4.ref的其他函数
unref
如果我们想要获取一个ref引用中的value,那么也可以通过unref方法:
如果参数是一个 ref,则返回内部值,否则返回参数本身;
这是
val = isRef(val) ? val.value : val
的语法糖函数;
isRef
判断值是否是一个ref对象。
shallowRef
创建一个浅层的ref对象;
triggerRef
手动触发和 shallowRef 相关联的副作用:
Vue3中Compositions API的使用(一)相关推荐
- 【Vue3】vue3中组合式Api的setup写法快速入门上手起步
要使用Vue3,那必须得会setup,因为setup是组合式API表演的舞台. 安装volar 如果你的VScode之前安装有vuter插件,请先把他禁用或者卸载掉,然后安装volar. 因为,vut ...
- Vue3中的API——选项式API、组合式API
Vue 的组件可以按两种不同的风格书写:选项式 API 和组合式 API. 对于熟悉Vue2的人来说,选项式api是一个很好的选择,但Vue3提供的组合式api对于代码复用效果更为突出.博主之前是用的 ...
- 13个Vue3中的全局API的源码浅析汇总整理
前言 不知不觉vue-next的版本已经来到了3.1.2,最近对照着源码学习vue3的全局Api,边学习边整理了下来,希望可以和大家一起进步. 我们以官方定义.用法.源码浅析三个维度来一起看看它们.下 ...
- Vue3 中使用组合式API替换mixins,实现代码复用并解决隐患
我们在vue mixin混入–基础中聊过mixins可以使我们的代码进行复用,非常的灵活方便. 但是在vue3中却不推荐使用了,因为它存在一些问题. mixins问题 不清晰的数据来源:当使用了多个 ...
- Webpack的代码分包Vue3中定义异步组件分包refs的使用
一.默认的打包过程: 默认情况下,在构建整个组件树的过程中,因为组件和组件之间是通过模块化直接依赖的,那么webpack在打包时就会将组件模块打包到一起(比如一个app.js文件中): 这个时候随着项 ...
- vue3 中使用动画技术
vue3 中使用动画技术 作者: jcLee95 邮箱 :291148484@163.com CSDN 主页:https://blog.csdn.net/qq_28550263?spm=1001.21 ...
- Vue3中的父子、子父组件通信
Vue3中的父子.子父组件通信方式总结 李俊才的CSDN博客 CSDN用户名:jcLee95 邮箱:291148484@163.com 1. 父组件 => 子组件 | 使用props 父组件(分 ...
- 补充记录vue3中rrweb-player组件实现网页录屏的一个BUG解决
项目场景: vue3中rrweb-player组件实现网页录屏功能 问题描述 提示:这里描述项目中遇到的问题: 录制的视频会重复记录录制中这个按钮的状态,每打开一次开始录制视频中就会多一个按钮,bug ...
- vue2、vue3中自定义v-model的使用和区别
在我们的日常开发中,时常需要写一些自定义组件,而其中可能就会使用到v-model,v-model是Vue中的一个指令,用来实现数据的双向绑定,实现数据.视图更新,v-model是一个语法糖,,我们可以 ...
- vue3中使用swiper7轮播图插件
文章目录 项目场景: 普通vue3项目: vue3+typeScript项目: 组件样式修改: 关于垂直方向: 关于自动播放: 关于无限循环: 关于获取swiper实例: 项目场景: 前不久刚在我的v ...
最新文章
- 优雅地分离tableview回调
- 《精通ArcGIS Server 应用与开发》——第 1 章 ArcGIS 10简介1.1 ArcGIS 10体系结构1...
- 04——确定对象使用前被初始化
- python重命名异常_python异常处理
- 40条提升编程技能的小妙招
- 模板类的全特化、偏特化
- 洛谷 P1101 单词方阵
- 统计相关系数r与r2的区别_什么是相关系数? 统计解释中的r值
- linux对硬盘进行分区吗,linux对4T硬盘进行分区
- Redis从安装到简单使用(windows)
- html播放rtmp直播,video.js实现浏览器播放rtmp协议直播流的问题
- LeetCode罗马数字转整数
- 【云云怪】第4个项目:20以内加减法(剧情版)
- 网课/网校/知识付费/在线教育系统,100%全功能开源,可免费商用
- 11个资源强大的网站!知乎超20万人强烈推荐,再也不怕资源难找
- 会计科目类词汇(中英)
- 夜暗方显万颗星,灯明始见一缕尘
- 康托展开(hdu1430)
- English语法_不定式 - 常用句型
- 本地笔记软件mybase8.x破解试用用时长限制
热门文章
- KMPlayer怎么加速播放 KMPlayer加速播放方法
- VtigerCRM 点击拨号和来电弹屏 PBX Manager Module
- 推荐一款微信小程序《诗词万卷》
- 小米 网络位置服务器,小米科普:一文看懂路由器上的 Mesh 组网是什么
- centos win xp双系统 安装手记 9660 grldr U盘安装
- 本周AI热点回顾:波士顿动力机器狗去新西兰放羊了、微软WSL将支持GPU、ERNIE-GEN刷新SOTA
- No buffer space available 和windows 2003复制文件时:配额不足,无法处理该命令
- 闹钟函数alarm()的解释与实践
- 测试场景设计-登录设计
- Java实现坦克大战小游戏(源码+注释)