Composition API也称为组合式API,实现代码的共享以及重用
Vue3.0 暴露变量必须 return 出来,template中才能使用;
Vue3.2 中 只需要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需return,template可直接使用
新的 setup 组件选项在创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点
由于在执行 setup 时尚未创建组件实例,因此在 setup 选项中没有 this。
这意味着,除了props 之外,将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
setup()接收两个参数 props 和 context
props是响应的 当传入的props更新时会同步更新
但是因为 props 是响应式的,所以不能使用 ES6 解构,因为它会消除 prop 的响应性。
如果需要解构 prop,可以通过使用 setup 函数中的 toRefs 来安全地完成
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
context暴露三个属性
export default {
setup(props, context) {
// Attribute (非响应式对象)
// 可以读取所有除了在props当中声明的自定属性以外传递的 自定义属性的值 this.$attrs
// 即所有从父组件传递过来的属性 除去使用props中声明过的
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
// 用于触发绑定的自定义事件 this.$emit
console.log(context.emit)
}
}
执行 setup 时组件实例尚未被创建。因此只能访问props,attrs,slots,emit属性
用来定义响应式的 字符串、 数值、 数组、Bool类型
对于ref对象而言,读写它的值都需要通过它的value属性
但在模板中使用时不需要.value属性,模板会自行的对于它的结构进行解包
export default {
data() {
return {
}
},
setup() {
let msg = ref("这是setup中的msg");
let list = ref(["1", "2", "3"])
let updateMsg = () => {
alert("触发方法");
msg.value = "改变后的值"
}
return {
msg,
list,
updateMsg
}
},
}
用来定义响应式的对象
export default {
data() {
return {
}
},
setup() {
let msg = ref("这是setup中的msg");
let setupData = reactive({
title: "reactive定义响应式数据的title",
userinfo: {
username: "张三",
age: 20
}
})
let updateMsg = () => {
alert("触发方法");
msg.value = "改变后的值"
}
let updateTitle = () => {
alert("触发方法");
setupData.title = "我是改变后的title"
}
return {
msg,
setupData,
updateMsg,
updateTitle
}
},
}
要改变ref定义的属性名称需要通过 属性名称.value来修改
要改变reactive中定义的对象名称可以直接修改
在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。
与侦听器watch类似 不过在组件创建时会默认执行一次
export default {
name: "WatchEffect",
setup() {
const data = reactive({
count: 1,
num: 1
});
const stop = watchEffect(() => console.log(`侦听器:${data.count}`));
// data.count变化时才会执行
setInterval(() => {
data.count++;
}, 1000);
return {
data,
stop
};
},
};
与原先的侦听器类似,接收三个参数,第一个时要监听的状态,第二个是处理函数,第三个是配置项
对比watchEffect,watch允许我们:
懒执行,也就是说仅在侦听的源变更时才执行回调;
更明确哪些状态的改变会触发侦听器重新运行;
访问侦听状态变化前后的值
export default {
name: "Watch",
setup() {
const a = ref('cc')
const data = reactive({
count1: 0,
count2: 0
});
// 侦听单个数据源
// 不能直接侦听data.count1
const stop1 = watch(data, () =>
console.log("watch1", data.count1, data.count2)
);
// 监听ref属性且进行配置
watch(a, () => {
console.log(d.value)
}, {
immediate: true
})
// 侦听多个数据源
const stop2 = watch([data], () => {
console.log("watch2", data.count1, data.count2);
});
setInterval(() => {
data.count1++;
}, 1000);
return {
data,
stopAll: () => {
stop1();
stop2();
},
};
},
};
与原先的计算属性相同
export default {
name: "解构响应式对象数据",
setup() {
const user = reactive({
firstName: "",
lastName: "",
});
const fullName = computed(() => {
return user.firstName + " " + user.lastName
})
return {
...toRefs(user),
fullName
};
},
};
解构响应式对象数据而不丢失响应性
响应式对象数据如果通过es6…运算符解构就会消除响应性
把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应
export default {
name: "解构响应式对象数据",
setup() {
const user = reactive({
username: "张三",
age: 10000,
});
return {
...toRefs(user)
};
},
};
传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理。一个只读的代理是“深层的”
对象内部任何嵌套的属性也都是只读的
export default {
name: "Readonly",
setup() {
const original = reactive({ count: 0 });
const copy = readonly(original);
setInterval(() => {
original.count++;
copy.count++;
// 报警告,Set operation on key "count" failed: target is readonly. Proxy {count: 1}
}, 1000);
return { original, copy };
},
};
选项式 API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。
换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。
export default {
setup() {
// mounted
onMounted(() => {
console.log('Component is mounted!')
})
}
}
provide 和 inject 对父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。
父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这个数据
在非组合式api中
可以传值 但子组件的值改变不能同步到父组件
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
provide: {
location: 'North Pole',
geolocation: {
longitude: 90,
latitude: 135
}
}
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
export default {
inject: ['location', 'geolocation']
}
</script>
组合式api中
可以传值 且子组件的值改变可以同步到父组件
provide 函数允许通过两个参数定义 property:
property 的 name ( 类型)
property 的 value
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide } from 'vue'
import MyMarker from './MyMarker.vue
export default {
components: {
MyMarker
},
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
</script>
inject 函数有两个参数:
要注入的 property 的名称,一个默认的值 (可选)
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe') // 缺省值(默认值):The Universe
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>