Composition API
以后开发不再用options api 而是composition api,这个api其实就是setup函数
compositionAPI:组合API
optionsAPI:选项API
options和composition对比
1-如上optionsAPI实际上就是在操控data中的数据,但是对它的操作遍布于各种api中,如methods,computed,watch等等。 弊端就是太分散了不利于分析代码等。
2-compositionAPI就是把对同个内容的操作全部写到一个函数内,然后这个函数还可以被很简单的抽取出进行复用。
认识compositionApi
<template>
<!-- template中ref对象自动解包 -->
<h2>数字:{{ counter }}</h2>
<button @click="add">+1</button>
<button @click="sub">-1</button>
</template>
<script>
import {ref} from "vue"
//import {reactive} from "vue"
//import {reactive,ref} from "vue" 这种写法同时导入也可以
export default {
setup(){
//注意此时counter其实就是一个ref对象了,而不是简单的100,所以想使用这个100,必须通过counter.value
let counter = ref(100)
// function add(){}
const add =()=>{
counter.value +=1
}
// function sub(){}
const sub =()=>{
counter.value -=1
}
return{
counter,
add,
sub
}
}
}
</script>
<style scoped>
</style>
步骤:
1-在script标签内书写setup函数
2-在setup函数中声明变量和使用箭头函数(普通函数也可以)声明会发生的事件
3-setup中所有声明的东西,必须return返回出去才可以使用
4-我们点击按钮发现没反应,其实普通数据counter已经变化了,但是它不是响应式数据不会被劫持代理,不会在页面上实时改变
5.1-导入vue提供的ref方法,使用ref包裹住数据,即可实现响应式。
5.1.1使用ref后,在script中必须通过counter.value才能取到真正的值,在template中的插值语法中不需要,因为有vue内部会自动解包。(解包就是自动取出其对象的value属性)
//注意此时counter其实就是一个ref对象了,而不是简单的100,所以想使用这个100,必须通过counter.value
5.2通过reactive函数
react就是回应,反应的意思,reactive就是响应式,因为我们在setup中写的数据是普通数据,不会像在optionsAPI的data中的数据那样被vue劫持,所以是不会在页面中自动响应更新的。
需要我们手动通过reactive劫持(其实data响应式原理的底层也是通过reactive函数),注意:reactive函数必须传入复杂类型的参数,如上的传入对象,而不是是简单的字符串或者一个数字值。
什么时候用ref,什么时候用reactive
这里是coderwhy老师总结出的经验,我以后也就这样写,其实只要你需要响应式数据,ref和reactive都可以完成,但是各有各的优点。
<template>
<div>
<form>
账号: <input type="text" v-model="account.username">
密码: <input type="password" v-model="account.password">
</form>
<form>
账号: <input type="text" v-model="username">
密码: <input type="password" v-model="password">
</form>
<hr>
<show-info :name="name" :age="age"></show-info>
</div>
</template>
<script>
import { onMounted, reactive, ref } from 'vue'
import ShowInfo from './ShowInfo.vue'
export default {
components: {
ShowInfo
},
data() {
return {
message: "Hello World"
}
},
setup() {
// 定义响应式数据: reactive/ref
// 强调: ref也可以定义复杂的数据
const info = ref({})
console.log(info.value)
// 1.reactive的应用场景
// 1.1.条件一: reactive应用于本地的数据
// 1.2.条件二: 多个数据之间是有关系/联系(聚合的数据, 组织在一起会有特定的作用)
const account = reactive({
username: "coderwhy",
password: "1234567"
})
const username = ref("coderwhy")
const password = ref("123456")
// 2.ref的应用场景: 其他的场景基本都用ref(computed)
// 2.1.定义本地的一些简单数据
const message = ref("Hello World")
const counter = ref(0)
const name = ref("why")
const age = ref(18)
// 2.定义从网络中获取的数据也是使用ref
// const musics = reactive([])
const musics = ref([])
onMounted(() => {
const serverMusics = ["海阔天空", "小苹果", "野狼"]
musics.value = serverMusics
})
return {
account,
username,
password,
name,
age
}
}
}
</script>
<style scoped>
</style>
来自本地的,有关联的数据就用reactive,为什么呢?
因为有关联的数据,比如账号密码,你使用reactive的方法就直接account.username和account.password即可,更方便使用。
其他场景一般都用ref函数
比如来自服务器的数据,使用ref的话,如上代码直接music.value=serverMusics即可,如果是使用reactive函数,那么你没法去把服务器的数据搞进去,或者说需要一个一个遍历进去,很麻烦。
hooks文件夹是什么?
https://blog.csdn.net/JaneLittle/article/details/127127644
vue3
借鉴 react hooks
开发出了 Composition API ,所以也就意味着 Composition API 也能进行自定义封装 hooks
。
vue3
中的 hooks
就是函数的一种写法,就是将文件的一些单独功能的js
代码进行抽离出来,放到单独的js文件中,或者说是一些可以复用的公共方法/功能。其实 hooks
和 vue2
中的 mixin
有点类似,但是相对 mixins
而言, hooks
更清楚复用功能代码的来源, 更清晰易懂。
安装Vue-devtool
作用:方便调试
1-直接在chorm商店下载(某些原因正常情况下打不开)
安装后如果不生效,重启下浏览器
2-找个安装教程手动安装,去github下载源码然后打包,拖到chorm拓展内。
认识readonly只读函数(了解)
它的原理就是劫持了对象的set方法,让它无法被修改。
问题:子组件可以修改来自父组件通过父子通信,传递过来的数据,即修改props内的数据(如对象)。
规范准则:虽然这是可行的,但是不符合单项数据流的准则,如父组件将此数据传递给了3个子组件,然后其中一个子组件修改了数据,那么父组件根本不知道是谁改的,非常不利于维护。 这个准则在react内也要遵守
实在要修改这个数据呢?
在子组件内通过emit发送自定义事件给父组件,由父组件进行修改。
问题:当其他人在编写子组件的时候,他不知道这个单向数据流的准则,修改了数据应该怎么避免呢?
解决:在父组件内使用readonly函数包裹住需要传出到子组件的数据即可。那么这个数据将不能被修改。
app.vue
<template>
<h2>App: {{ info }}</h2>
<show-info :info="info"
:roInfo="roInfo"
@changeInfoName="changeInfoName"
@changeRoInfoName="changeRoInfoName">
</show-info>
</template>
<script>
import { reactive, readonly } from 'vue'
import ShowInfo from './ShowInfo.vue'
export default {
components: {
ShowInfo
},
setup() {
// 本地定义多个数据, 都需要传递给子组件
// name/age/height
const info = reactive({
name: "why",
age: 18,
height: 1.88
})
function changeInfoName(payload) {
info.name = payload
}
// 使用readOnly包裹info
const roInfo = readonly(info)
function changeRoInfoName(payload) {
info.name = payload
}
return {
info,
changeInfoName,
roInfo,
changeRoInfoName
}
}
}
</script>
<style scoped>
</style>
showinfo.vue
<template>
<div>
<h2>ShowInfo: {{ info }}</h2>
<!-- 代码没有错误, 但是违背规范(单项数据流) -->
<button @click="info.name = 'kobe'">ShowInfo按钮</button>
<!-- 正确的做法: 符合单项数据流-->
<button @click="showInfobtnClick">ShowInfo按钮</button>
<hr>
<!-- 使用readonly的数据 -->
<h2>ShowInfo: {{ roInfo }}</h2>
<!-- 代码就会无效(报警告) -->
<!-- <button @click="roInfo.name = 'james'">ShowInfo按钮</button> -->
<!-- 正确的做法 -->
<button @click="roInfoBtnClick">roInfo按钮</button>
</div>
</template>
<script>
export default {
props: {
// reactive数据
info: {
type: Object,
default: () => ({})
},
// readonly数据
roInfo: {
type: Object,
default: () => ({})
}
},
emits: ["changeInfoName", "changeRoInfoName"],
setup(props, context) {
function showInfobtnClick() {
context.emit("changeInfoName", "kobe")
}
function roInfoBtnClick() {
context.emit("changeRoInfoName", "james")
}
return {
showInfobtnClick,
roInfoBtnClick
}
}
}
</script>
<style scoped>
</style>
当readonly后,就算是父组件本身也无法修改如上代码的roInfo,但是是可以通过原始info修改的。
方法补充(了解)
什么叫shallow呢,就是浅层,shallowReactive就是浅层响应,如上图代码,当firend内的name属性变化,那么浅层响应就不会被修改。
toRefs函数
作用:如下当我们使用reactive函数后,在template中每次都需要通过info.name,info.age等等来取值,有的人想直接写name和age怎么办呢?
答:我们可以将此对象解构
出现问题:解构后,数据会变成普通数据,没有响应式的功能
解决:使用toRef函数再进行解构。
<template>
<div>
<h2>info: {{ name }} - {{ age }} - {{ height }}</h2>
<button @click="age++">修改age</button>
<button @click="height = 1.89">修改height</button>
</div>
</template>
<script>
import { reactive, toRefs, toRef } from 'vue'
export default {
setup() {
const info = reactive({
name: "why",
age: 18,
height: 1.88
})
// reactive被解构后会变成普通的值, 失去响应式
//多个解构
const { name, age } = toRefs(info)
//单个解构
const height = toRef(info, "height")
return {
name,
age,
height
}
}
}
</script>
<style scoped>
</style>
为什么setup函数内无法使用this?
所以并不是原先官方文档说的setup之前,data和computed等没有被做解析,而是:
它内部源码,没有给setup绑定this,原先的optionsApi的methods,computed什么的都通过appy和bind等绑定了this。
问题:没有this,那我如何this.$emit呢?
setup(props,context)里有两个默认参数,第一个就是父组件传递进来的数据,第二个就包含了:
setup中的对应原optionsApi的功能函数的使用
computed函数使用
<template>
<h2>{{ fullname }}</h2>
<button @click="setFullname">设置fullname</button>
<h2>{{ scoreLevel }}</h2>
</template>
<script>
import { reactive, computed, ref } from 'vue'
export default {
setup() {
// 1.定义数据
const names = reactive({
firstName: "kobe",
lastName: "bryant"
})
// const fullname = computed(() => {
// return names.firstName + " " + names.lastName
// })
const fullname = computed({
set: function(newValue) {
const tempNames = newValue.split(" ")
names.firstName = tempNames[0]
names.lastName = tempNames[1]
},
get: function() {
return names.firstName + " " + names.lastName
}
})
console.log(fullname)
function setFullname() {
fullname.value = "coder why"
console.log(names)
}
// 2.定义score
const score = ref(89)
const scoreLevel = computed(() => {
return score.value >= 60 ? "及格": "不及格"
})
return {
names,
fullname,
setFullname,
scoreLevel
}
}
}
</script>
<style scoped>
</style>
// const fullname = computed(() => {
// return names.firstName + " " + names.lastName
// })
如上即computed在setup中的基本使用,其中默认只设置了get,没有set,如果要设置set如下
const fullname = computed({
set: function(newValue) {
const tempNames = newValue.split(" ")
names.firstName = tempNames[0]
names.lastName = tempNames[1]
},
get: function() {
return names.firstName + " " + names.lastNamejs
}
})
其中computed()返回的也是一个ref对象,我们给fullname赋值,需要通过fullname.value
ref获取元素dom的使用
原先学了ref在optionsapi中是通过给标签设置ref属性,如
<div class="app">
<h2 ref="title" class="title" :style="{ color: titleColor }">{{ message }}</h2>
<button ref="btn" @click="changeTitle">修改title</button>
<banner ref="banner"/>
</div>
然后所有的拥有ref属性的标签的dom会存在与this.$ref这个对象中,如
console.log(this.$refs.title)
console.log(this.$refs.btn)
那么在setup 函数中无法使用this,应该怎么办呢?
app.vue
<template>
<!-- 1.获取元素 -->
<h2 ref="titleRef">我是标题</h2>
<button ref="btnRef">按钮</button>
<!-- 2.获取组件实例 -->
<show-info ref="showInfoRef"></show-info>
<button @click="getElements">获取元素</button>
</template>
<script>
import { ref, onMounted } from 'vue'
import ShowInfo from './ShowInfo.vue'
export default {
components: {
ShowInfo
},
setup() {
const titleRef = ref()
const btnRef = ref()
const showInfoRef = ref()
// mounted的生命周期函数
onMounted(() => {
console.log(titleRef.value)
console.log(btnRef.value)
console.log(showInfoRef.value)
showInfoRef.value.showInfoFoo()
})
function getElements() {
console.log(titleRef.value)
}
return {
titleRef,
btnRef,
showInfoRef,
getElements
}
}
}
</script>
<style scoped>
</style>
我们可以引入一个ref函数(compositionapi就是函数编程,那么optionsapi都会变成函数)
const titleRef = ref()
const btnRef = ref()
const showInfoRef = ref()
然后使用这个函数,主要变量名需要和需要获取dom的标签中ref属性的值相同(我刚学也不知道原理,只学怎么用)
然而我们发现直接获取
console.log(titleRef.value)
console.log(btnRef.value)
console.log(showInfoRef.value)
是获取不到的,因为在setup()执行的过程中,dom可能还没有被完全加载,所以需要加上onMounted已挂载的钩子函数
注意:在optionsapi中是mounted(),在setup中是onMounted()
onMounted(() => {
console.log(titleRef.value)
console.log(btnRef.value)
console.log(showInfoRef.value)
showInfoRef.value.showInfoFoo()
})
如何获取组件的实例?
showinfo.vue
<template>
<div>ShowInfo</div>
</template>
<script>
export default {
// methods: {
// showInfoFoo() {
// console.log("showInfo foo function")
// }
// }
setup() {
function showInfoFoo() {
console.log("showInfo foo function")
}
return {
showInfoFoo
}
}
}
</script>
<style scoped>
</style>
一样的,加ref属性即可,然后再使用ref()函数声明
<!-- 2.获取组件实例 -->
<show-info ref="showInfoRef"></show-info>
const showInfoRef = ref()
而且我们拿到组件的实例,也就是showInfoRef后,还可以在app.vue中通过这个实例调用shpwinfo.vue组件内的方法,如
showInfoRef.value.showInfoFoo()
组件的生命周期函数
Provide/Inject使用
watch/watchEffect
自定义Hook联系
script setup 语法糖
vue3.2版本才有