1、组合式API(composition API)概念
- 选项式API(options API)随着开发会日益臃肿,且数据分散
- 组合式API数据集中不分散,比较好维护
- 组合式API主要是以函数的形式呈现的
2、组合式API基本语法–setup
- setup可以理解为一个调度仓库,以往的data,computed,methods,watch都可以写在这里
- 取代了beforeCreate()和created()钩子函数
setup(props,context){
return {
msg:'测试文本',
say:()=>{
console.log('张三凉透了')
}
}
},
案例:
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
//组合式API,props属性,cotext上下文
//不仅可以返回数据,还可以返回方法
//setup可以理解为一个调度仓库,以往的data,computed,methods,watch都可以写在这里
setup(props,context){
return {
msg:'测试文本',
say:()=>{
console.log('张三凉透了')
}
}
},
template:`
<div>
<h1>{{msg}}</h1>
<button @click="say">张三怎么了</button>
</div>
`
})
app.mount('#app')
</script>
</body>
- setup不能调用声明周期的所有函数,只是部分
- setup存在$options的里
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
//组合式API,props属性,cotext上下文
//不仅可以返回数据,还可以返回方法
//setup可以理解为一个调度仓库,以往的data,computed,methods,watch都可以写在这里
setup(props,context){
return {
msg:'测试文本',
say:()=>{
console.log('张三凉透了')
}
}
},
methods:{
deal(){
console.log(this.$options.setup())
}
},
template:`
<div>
<h1>{{msg}}</h1>
<button @click="say">张三怎么了</button>
<button @click="deal">dela</button>
</div>
`
})
app.mount('#app')
</script>
</body>
3、组合式API–ref基础类型具备相应式
- ref:让基础类型的数据具备响应式
- 使用ref(基础类型数据)进行包装,包装后得到一个proxy对象
- 要取到这个基础类型的值必须动过包装后的对象.value来获取
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
// data(){
// return {
// msg:100
// }
// },
//引入ref
setup(props,context){
//底层通过proxy代理,具备了响应式的引用
const {ref}=Vue;//从Vue静态类中拿到ref
// let msg=ref(100)
let msg=ref('hello')
setTimeout(()=>{
msg.value='qinghua daxue'
},2000)
return {
msg,
}
},
template:`
<div>
<h1>{{msg}}</h1>
</div>
`
})
app.mount('#app')
</script>
</body>
又例
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
const {ref}=Vue
let msg=ref(100)
return {
msg,
add(){
msg.value++
}
}
},
template:`<h3>{{msg}}</h3><br/><button @click="add">add</button>`
})
app.mount('#app')
</script>
</body>
4、组合式API–reactive让引用类型具有响应式
reactive让引用类型具有响应式
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue中结构出reactive
const {reactive}=Vue
//用reactiove包装一下数据,到到一个pobj对象
let pobj=reactive({ name:'zhangsan',age:34,address:'beijing' })
let pArray=reactive(['qinghua','beida','hagongda'])
return {
//返回这个pobj包装好的对象
pobj,
pArray,
add(){
console.log(pobj)
pobj.name='lisi'
pArray[0]='beihang'
}
}
},
//在模板中象一个正常的对象一样对其进行遍历及使用
template:`<h3 v-for="(item,index) in pobj" :key="index">
{{item}}
</h3>
<br/>
<h3 v-for="item in pArray">
{{item}}
</h3>
<div>{{pobj.name}} {{pobj.age}} {{pobj.address}}</div>
<div>{{pArray[0]}} {{pArray[1]}} {{pArray[2]}}</div>
<button @click="add">add</button>`
})
app.mount('#app')
</script>
</body>
5、组合式API–readonly只读,单向传递数据
- 单向传递数据,实际就是只读,readonly包装后只允许展现,不允许修改数据
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue中结构出reactive
const {readonly}=Vue
//用reactiove包装一下数据,到到一个pobj对象
let pobj=readonly({ name:'zhangsan',age:34,address:'beijing' })
return {
//返回这个pobj包装好的对象
pobj,
add(){
console.log(pobj)
pobj.name='lisi'//vue会报无法修改,因为是readonly
}
}
},
//在模板中象一个正常的对象一样对其进行遍历及使用
template:`<h3 v-for="(item,index) in pobj" :key="index">
{{item}}
</h3>
<br/>
<div>{{pobj.name}} {{pobj.age}} {{pobj.address}}</div>
<button @click="add">add</button>`
})
app.mount('#app')
</script>
</body>
6、组合式API–toRefs从对象中结构属性
步骤:
- 1、从vue中解构出reactive及toRefs
- 2、使用reactive及toRefs包装出一个对象obj
- 3、使用{属性1,属性2,…}=obj解构出这个对象的属性,并用结构出来的toRefs包装这个obj对象
- 4、在setup中返回这些属性
- 5、模板中使用这些属性
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue中解构出reactive及toRefs
const {reactive,toRefs}=Vue
//用reactiove包装一下数据,到到一个pobj对象
let pobj=reactive({ name:'zhangsan',age:34,address:'beijing' })
//从对象中解构属性
const {name,age,address}=toRefs(pobj)
return {
//返回结构出来的三个属性
name,
age,
address,
add(){
console.log(pobj)
pobj.name='lisi'//可以顺利修改,并及时反映到页面
}
}
},
template:`
<div>{{name}} {{age}} {{address}}</div>
<button @click="add">add</button>`
})
app.mount('#app')
</script>
</body>
7、组合式API–toRef
toRef针对一些可传可不传,最后很可能不传的数据,在引用是不会因为没有传过来而保存
步骤
- 通过Vue把reactive,toRefs,toRef解构出来
- 再把数据用reactive包装成对象,这时候可能出现数据传过来不完整的情形(这也正是toRef起作用的地方)
- 再使用toRefs(对象)解构出对象的属性(这个属性应当是必传属性)
- 再使用toRef包装可能取不到(没传来)的数据,包装成一个对象toRef(pobj,‘major’)
- 再通过return返回所有必传,可能传的属性
- 最后在模板中使用这些属性
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue中解构出reactive及toRefs
const {reactive,toRefs,toRef}=Vue
//用reactiove包装一下数据,到到一个pobj对象
let pobj=reactive({ name:'zhangsan',age:34,address:'beijing'})
//从对象中解构属性(必传属性)
let {name,age,address}=toRefs(pobj)
//从对象中结构可能没有传过来的属性
let major=toRef(pobj,'major')
return {
//返回结构出来的三个属性
name,
age,
address,
major,
add(){
console.log(pobj)
pobj.name='lisi'//可以顺利修改,并及时反映到页面
pobj.major='news'//可以顺利修改,但无法反映到页面上,如果没有toRef会提示major没有定义
}
}
},
template:`
<div>{{name}} {{age}} {{address}} {{major}}</div>
<button @click="add">add</button>`
})
app.mount('#app')
</script>
</body>
8、组合式API–context上下文环境
- context大致包含了父组件传给子组件的$attrs,slot插槽,emit发射的事件可以通过context将这三者解构出来 const {attrs,slot,emit}=context
1、解构attrs案例
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
},
template:`
<subcom gender='female' style="background-color: red; width: 100px; height: 100px"></subcom>
`
})
app.component('subcom',{
setup(props,context){
//解构出所有属性,插槽,发射出去的自定义事件
const {attrs,slot,emit}=context
console.log(attrs)//打印出一个proxy代理对象
console.log(attrs.style)
console.log(attrs.gender)
},
template: `<div style="attrs.style">我是子组件</div>`
})
app.mount('#app')
</script>
</body>
2、解构slot案例
通过slots.default()获取插槽中插入的内容
<body>
<div id="app">
<subcom>
<button>我是补位的btn</button>
</subcom>
<subcom>
<input type="text"/>
<button>我是补位的btn</button>
</subcom>
</div>
<script>
//这里是根组件
const app=Vue.createApp({
})
app.component('subcom',{
setup(props,context){
//解构出所有属性,插槽,发射出去的自定义事件
const {attrs,slots,emit}=context
console.log(slots)//打印出一个proxy
//通过slots.default()获取插槽中插入的内容(实际上是虚拟dom对象)
console.log(slots.default())
//可以进一步的取内容
console.log(slots.default()[0].type)
},
template: `
<slot>默认内容</slot>
<div >我是子组件</div>`
})
app.mount('#app')
</script>
</body>
3、解构emit案例
使用从上下文结构出来的emit来替代$emit
<body>
<div id="app">
<mysubcom @sub-Click="deal"></mysubcom>
</div>
<script>
const mysub={
setup(props,context){
//从context中解构出emit
const {emit}=context;
//取代了原有的methods内容
function btnClick () {
alert('你点击了')
const dataObj={
name:'my course',
intro:'some course about vue from youyuxi'
}
//emit替代了原有的this.$emit
emit('subClick',dataObj)
}
//setup里面定义的函数一定要通过return 返回出去
return {
btnClick
}
},
template: `
<div style="background-color: red; width: 200px; height: 200px" >
<button @click="btnClick">我要发射事件</button>
</div>`
}
//这里是根组件
const app=Vue.createApp({
data(){
return{
msg:''
}
},
components:{
'mysubcom':mysub
},
methods:{
deal(item) {
console.log('子组件中的按钮发生了点击')
console.log(item)
}
}
})
app.mount('#app')
</script>
</body>
9、组合式API–计算属性新用法1
计算属性写在setup里面
步骤
1、从vue中结构出ref和computed
2、使用computed函数创建一个comouted计算属性
3、使用return返回这个计算属性
4、在模板中或者页面中使用这个计算属性
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数
const {ref,computed}=Vue
//自定义一个变量,并用ref包装一下
let num1 = ref(10);
//使用刚才结构出来的computed函数定义一个计算属性
let num2=computed(()=>{
return num1.value *10
})
//定义给一个函数
const add=()=>{
num1.value+=10
}
return {
num1,
num2,
add,
}
},
template:`
<h1>普通属性num1:{{num1}}</h1>
<h1>计算属性num2:{{num2}}</h1>
<p>-----</p>
<button @click="add">累加</button>
`
})
app.mount('#app')
</script>
</body>
10、组合式API–计算属性新用法2使用get和set
使用getter和set来双向绑定值(获取和改变值)
步骤
- 在computed函数中传入一个get/set对象
- 再修改计算属性的值,则会触发set对应的函数
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数
const {ref,computed}=Vue
//自定义一个变量,并用ref包装一下
let num1 = ref(10);
//使用刚才结构出来的computed函数定义一个计算属性
let num2=computed({
get: ()=>{return num1.value *10},
set:(res)=>{
console.log(res)
num1.value=res / 10
}
})
setTimeout(()=>{
num2.value=500
},2000)
//定义给一个函数
const add=()=>{
num1.value+=10
}
return {
num1,
num2,
add,
}
},
template:`
<h1>普通属性num1:{{num1}}</h1>
<h1>计算属性num2:{{num2}}</h1>
<p>-----</p>
<button @click="add">累加</button>
`
})
app.mount('#app')
</script>
</body>
11、组合式API–计算属性新用法3针对引用数据类型
- 直接使用reactive解构数据即可
- 通过对象.属性来get或者set值
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数
const {reactive,computed}=Vue
//自定义一个变量,并用ref包装一下
let numobj = reactive({ count:10 });
//使用刚才结构出来的computed函数定义一个计算属性
let num2=computed({
get: ()=>{return numobj.count *10},
set:(res)=>{
console.log(res)
numobj.count=res / 10
}
})
setTimeout(()=>{
numobj.count=500
},2000)
//定义给一个函数
const add=()=>{
numobj.count+=10
}
return {
numobj,
num2,
add,
}
},
template:`
<h1>普通属性num1:{{numobj.count}}</h1>
<h1>计算属性num2:{{num2}}</h1>
<p>-----</p>
<button @click="add">累加</button>
`
})
app.mount('#app')
</script>
</body>
12、组合式API–watch侦听器1-侦听简单数据类型
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数
const {ref,watch}=Vue
let brand=ref('')
let url=ref('')
//创建一个侦听器,brand为侦听对象
// watch(brand,(currentValue,preValue)=>{
// //相应业务逻辑
// console.log('当前的值'+currentValue)
// console.log('之前的值'+preValue)
// },{})
//侦听多个属性的写法
watch([brand, url],([currentbrand,currenturl],[prebrand,preurl])=>{
//相应业务逻辑
console.log('当前的值',currentbrand,currenturl)
console.log('之前的值'+prebrand,preurl)
},{})
return {
brand,
url
}
},
template:`
<div>
品牌:<input v-model="brand"><br/>
输入的品牌是:{{brand}}<br/>
网址:<input v-model="url"><br/>
输入的网址是:{{url}}
</div>
`
})
app.mount('#app')
</script>
</body>
13、组合式API–watch侦听器2-侦听引用数据类型
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数
const {ref,reactive,watch,toRefs}=Vue
let eduobj=reactive({name:'zhangsan',age:32})
//创建一个侦听器,brand为侦听对象,侦听对象的写法请注意
watch(()=>eduobj.name,(currentValue,preValue)=>{
//相应业务逻辑
console.log('当前的值'+currentValue)
console.log('之前的值'+preValue)
},{})
const {name,age}=toRefs(eduobj)
return {
name,
age
}
},
template:`
<div>
品牌:<input v-model="name"><br/>
输入的品牌是:{{name}}<br/>
网址:<input v-model="age"><br/>
输入的网址是:{{age}}
</div>
`
})
app.mount('#app')
</script>
</body>
14、组合式API–watchEffect侦听器副作用函数
特点:
- 没有惰性(数据改变才侦听)
- 更加抽象(侦听所有属性)
- 不能访问先前值()
- 可配置的(是否非惰性,深度监视(多层监视))
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数
const {ref,reactive,watch,toRefs,watchEffect}=Vue
let brand = ref('')
let url = ref('')
let obj=reactive({
cc:'2121',
arr:[{
name:'zhangsan'
}]
})
//创建一个侦听器,brand为侦听对象,侦听对象的写法请注意
watch(brand,(currentValue,preValue)=>{
//相应业务逻辑
console.log('当前的值'+currentValue)
console.log('之前的值'+preValue)
},{
//是否立即监视,还是发生变化才侦听
immediate:true,
//是否深度监视
deep:true
})
//侦听器副作用函数,watchEffect侦听了整个实例的属性
watchEffect(()=>{
console.log('开始侦听了!')
console.log(brand.value)//侦听到后,直接取值
console.log(url.value)//侦听到后,直接取值
})
return {
brand,
url
}
},
template:`
<div>
品牌:<input v-model="brand"><br/>
输入的品牌是:{{brand}}<br/>
网址:<input v-model="url"><br/>
输入的网址是:{{url}}
</div>
`
})
app.mount('#app')
</script>
</body>
15、组合式API–provide和inject1
- 不是父子组件,而是孙子组件的数据传递,可以跨多级传递数据,而不用一级一级的传递
- provide把孙子组件的数据发射出去,inject则是接受发射来的数据
- 发射出去的数据依然要在接收位置的通过return返回出去
<body>
<div id="app">
</div>
<script>
//创建孙组件
const subson={
setup(){
//把inject结构出来
const {inject}=Vue
//订阅数据
const gname=inject('brand','默认值')
const gcollege=inject('college')
return{
gname,
gcollege
}
},
template:`<div style="width: 200px; height: 200px; background-color: red">
我是孙组件<br/>
{{gname}}---{{gcollege.city}}---{{gcollege.url}}
</div>`
}
//创建父组件(也是根组件的儿子)
const subfather={
components:{
'subson':subson
},
template: `
<div style="width: 400px; height: 400px; background-color: green">
这个是父组件
<subson></subson>
</div>
`
}
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数,provide发射出去,inject接收过来
const {ref,reactive,provide,inject}=Vue
let brand = ref('海尔')
let college = reactive({city:'上海',url:'www.sina.com' })
//发射(发布)根组件的数据到子组件
//把数据brand以brand的名字发射出去
provide('brand',brand)
provide('college',college)
return {
}
},
components: {
'subfather':subfather
},
template:`
<div>我是根组件</div>
<subfather></subfather>
`
})
app.mount('#app')
</script>
</body>
16、组合式API–provide和inject2规避孙子组件修改根组件值(因为要保证单向数据流)
- 规避孙子组件修改根组件值(因为要保证单向数据流)要从根组件这个源头来进行规避,在解构的时候就要设置readonly
- 数据源发射的时候要指定readonly()
- 如果非要修改,也要在数据源头设定一个修改函数并发射出去
- 同时也要在孙子组件(使用位置)订阅这个函数,并且需要return出去
- 孙子组件通过函数的参数来传值
<body>
<div id="app">
</div>
<script>
//创建孙组件
const subson={
setup(){
//把inject结构出来
const {inject}=Vue
//订阅数据
const gname=inject('brand','默认值')
const gcollege=inject('college')
const changeBrand=inject('changeBrand')
const changeNum=()=>{
// gname.value='格力'
changeBrand('小米')
}
return{
gname,
gcollege,
changeNum,
changeBrand
}
},
template:`<div style="width: 200px; height: 200px; background-color: red">
我是孙组件<br/>
{{gname}}---{{gcollege.city}}---{{gcollege.url}}<br/>
<button @click="changeNum">修改</button>
</div>`
}
//创建父组件(也是根组件的儿子)
const subfather={
components:{
'subson':subson
},
template: `
<div style="width: 400px; height: 400px; background-color: green">
这个是父组件
<subson></subson>
</div>
`
}
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数,provide发射出去,inject接收过来
const {ref,reactive,provide,inject,readonly}=Vue
let brand = ref('海尔')
let college = reactive({city:'上海',url:'www.sina.com' })
//如果非要更改,也要在源头改
const changeBrand=(params)=>{
brand.value=params
}
//发射(发布)根组件的数据到子组件
//把数据brand以brand的名字发射出去
//reanonly设定了不可更改
provide('brand',readonly(brand))
provide('college',college)
//改变数据的方法也要发射出去
provide('changeBrand',changeBrand)
return {
}
},
components: {
'subfather':subfather
},
template:`
<div>我是根组件</div>
<subfather></subfather>
`
})
app.mount('#app')
</script>
</body>
17、组合式API-生命周期钩子的新写法
- 在原有钩子函数的名字前面加on
beforeMouted----onBeforeMouted
mouted—onMouted
-
用到的钩子函数都需要提前从vue里解构出来
const {ref,onBeforeMount}=Vue
<body>
<div id="app">
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(props,context){
//从vue里结构一个computed函数,provide发射出去,inject接收过来
const {ref,onBeforeMount,onMounted}=Vue
let msg=ref('hello world')
onBeforeMount(()=>{
console.log('onBeforeMount')
})
onMounted(()=>{
console.log('onMount')
})
return {
msg,
}
},
template:`
<div>我是根组件</div>
`
})
app.mount('#app')
</script>
</body>
更多的钩子函数:略
18、、组合式API-获取真实DOM
步骤
- 从vue中结构出onmouted函数、ref,视情况解构出来
- 使用ref包装数据
- 在页面标签中通过ref="数据名"进行关联绑定
- 通过钩子或其他函数对包装且绑定后的对象获取.value属性值,拿到其真实的dom
- 一定要把包装好并绑定后的对象return出去
案例
<body>
<div id="app">
<div ref="brand">撩课学院</div>
<div><input ref="abc" type="text"/> </div>
<div ref="url">www.sina.cn</div>
</div>
<script>
//这里是根组件
const app=Vue.createApp({
setup(){
//从vue里结构一个computed函数,provide发射出去,inject接收过来
const {onMounted,ref}=Vue
const brand=ref(null)
const url=ref(null)
const abc=ref(null)
onMounted(()=>{
console.log(brand.value)
console.log(url.value)
console.log(abc.value)
})
return {
brand,
url
}
},
})
app.mount('#app')
</script>
</body>