组合式api,在before create之前执行( 没法用this ), 属性,方法,生命周期钩子函数都写在这里面,要想在模版中使用setup中定义的属性或方法,记得要return出去
另外,在setup中使用vue的api或者一些函数时,要先引入
import {
defineComponent, getCurrentInstance, ref, onMounted } from 'vue';
参数: setup( props, context ) { }
props 一个对象,里边有父组件向子组件传递的数据,子组件中使用props接受到的所有属性
context 上下文对象 setup( props, { attrs, slots, emit } )
attrs 是属性,相当于this.$attrs
slots 插槽 相当于this.$slots
emit 自定义事件 相当于this.$emit
ref 定义简单类型的响应式数据
const name = ref('张三')
另外,ref 获取元素
vue2中 获取
直接this.$refs.testvue3中 获取
需要 setup () {
const test = ref(null) // 先创建
onMounted(() => {
// 挂载之后在onMounted才能获取到
console.log('test.value', test.value);
})
}
reactive 定义复杂类型的响应式数据
const obj = reactive({
name: '张三',
age: 18
})
toRef 转换响应式对象中某个属性为单独响应式数据
// 定义响应式数据对象
const obj = reactive({
name: '张三',
age: 18
})
// !!!从响应式数据对象中解构出的属性数据,不再是响应式数据
// let { name } = obj 不能直接解构,出来的是一个普通数据
const name = toRef(obj, 'name') // 这样name 在模版中可以直接使用
toRefs 转换响应式对象中所有属性为单独响应式数据,对象成为普通对象
setup () {
// 1. 响应式数据对象
const obj = reactive({
name: '张三',
age: 10
})
console.log(obj)
// 2. 解构或者展开响应式数据对象
// const {name,age} = obj
// console.log(name,age)
// const obj2 = {...obj}
// console.log(obj2)
// 以上方式导致数据就不是响应式数据了
const obj3 = toRefs(obj)
console.log(obj3)
const updateName = () => {
// obj3.name.value = '张三'
obj.name = '李四'
}
return {
...obj3, updateName}
}
computed 计算属性
<template>
<div class="container">
<div>今年:{
{
age}}岁</div>
<div>后年:{
{
newAge}}岁</div>
</div>
</template>
<script>
import {
computed, ref } from 'vue'
export default {
name: 'App',
setup () {
// 1. 计算属性:当你需要依赖现有的响应式数据,根据一定逻辑得到一个新的数据。
const age = ref(16)
// 得到后年的年龄
const newAge = computed(()=>{
// 该函数的返回值就是计算属性的值
return age.value + 2
})
// -------------------------------------------------------
// 另外还可以设置get,set方法
// 计算属性高级用法,传入对象
const newAge = computed({
// get函数,获取计算属性的值
get(){
return age.value + 2
},
// set函数,当你给计算属性设置值的时候触发
set (value) {
age.value = value - 2
}
})
return {
age, newAge}
}
}
</script>
watch 监听器
监听ref定义的响应式数据
监听多个响应式数据数据
监听reactive定义的响应式数据
监听reactive定义的响应式数据,某一个属性
深度监听 deep: true
默认执行 immediate: true
<template>
<div class="container">
<div>
<p>count的值:{
{
count}}</p>
<button @click="add">改数据</button>
</div>
<hr>
<div>
<p>{
{
obj.name}}</p>
<p>{
{
obj.age}}</p>
<p>{
{
obj.brand.name}}</p>
<button @click="updateName">改名字</button>
<button @click="updateBrandName">改品牌名字</button>
</div>
</div>
</template>
<script>
import {
reactive, ref, watch } from 'vue'
export default {
name: 'App',
setup () {
const count = ref(0)
const add = () => {
count.value++
}
// 当你需要监听数据的变化就可以使用watch
// 1. 监听一个ref数据
// 1.1 第一个参数 需要监听的目标
// 1.2 第二个参数 改变后触发的函数
// watch(count, (newVal,oldVal)=>{
// console.log(newVal,oldVal)
// })
const obj = reactive({
name: 'ls',
age: 10,
brand: {
id: 1,
name: '宝马'
}
})
const updateName = () => {
obj.name = 'zs'
}
const updateBrandName = () => {
obj.brand.name = '奔驰'
}
// 2. 监听一个reactive数据
watch(obj, ()=>{
console.log('数据改变了')
})
watch(()=>obj.brand, ()=>{
console.log('brand数据改变了')
},{
// 5. 需要深度监听
deep: true,
// 6. 默认触发
immediate: true
})
// 3. 监听多个数据的变化
// watch([count, obj], ()=>{
// console.log('监听多个数据改变了')
// })
// 4. 此时监听对象中某一个属性的变化 例如:obj.name
// 需要写成函数返回该属性的方式才能监听到
// watch(()=>obj.name,()=>{
// console.log('监听obj.name改变了')
// })
return {
count, add, obj, updateName, updateBrandName}
}
}
</script>
父子组件之间通讯 props emit
父子组件之间传值,在vue2中,父传子是通过在子组件中props接受父组件中传递的值,子传父是通过this.$emit 抛出一个事件
在vue3中,大概也是这样,只是写法不同
父传子 在setup种使用props数据 setup(props){ // props就是父组件数据 }
<template>
<div class="container">
<h1>父组件</h1>
<p>{
{
money}}</p>
<hr>
<Son :money="money" />
</div>
</template>
<script>
import {
ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'App',
components: {
Son
},
// 父组件的数据传递给子组件
setup () {
const money = ref(100)
return {
money }
}
}
</script>
<template>
<div class="container">
<h1>子组件</h1>
<p>{
{
money}}</p>
</div>
</template>
<script>
import {
onMounted } from 'vue'
export default {
name: 'Son',
// 子组件接收父组件数据使用props即可
props: {
money: {
type: Number,
default: 0
}
},
setup (props) {
// 获取父组件数据money
console.log(props.money)
}
}
</script>
子传父 触发自定义事件的时候emit来自 setup(props,{emit}){ // emit 就是触发事件函数 }
<template>
<div class="container">
<h1>父组件</h1>
<p>{
{
money}}</p>
<hr>
+ <Son :money="money" @change-money="updateMoney" />
</div>
</template>
<script>
import {
ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'App',
components: {
Son
},
// 父组件的数据传递给子组件
setup () {
const money = ref(100)
+ const updateMoney = (newMoney) => {
+ money.value = newMoney
+ }
+ return {
money , updateMoney}
}
}
</script>
<template>
<div class="container">
<h1>子组件</h1>
<p>{
{
money}}</p>
+ <button @click="changeMoney">花50元</button>
</div>
</template>
<script>
import {
onMounted } from 'vue'
export default {
name: 'Son',
// 子组件接收父组件数据使用props即可
props: {
money: {
type: Number,
default: 0
}
},
// props 父组件数据
// emit 触发自定义事件的函数
+ setup (props, {
emit}) {
// 获取父组件数据money
console.log(props.money)
// 向父组件传值
+ const changeMoney = () => {
// 消费50元
// 通知父组件,money需要变成50
+ emit('change-money', 50)
+ }
+ return {
changeMoney}
}
}
</script>
依赖注入 provide和inject
使用场景:有一个父组件,里头有子组件,有孙组件,有很多后代组件,共享父组件数据。
<template>
<div class="container">
<h1>父组件 {
{
money}} <button @click="money=1000">发钱</button></h1>
<hr>
<Son />
</div>
</template>
<script>
import {
provide, ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'App',
components: {
Son
},
setup () {
const money = ref(100)
const changeMoney = (saleMoney) => {
console.log('changeMoney',saleMoney)
money.value = money.value - saleMoney
}
// 将数据提供给后代组件 provide
provide('money', money)
// 将函数提供给后代组件 provide
provide('changeMoney', changeMoney)
return {
money }
}
}
</script>
<style scoped lang="less"></style>
<template>
<div class="container">
<h2>子组件 {
{
money}}</h2>
<hr>
<GrandSon />
</div>
</template>
<script>
import {
inject } from 'vue'
import GrandSon from './GrandSon.vue'
export default {
name: 'Son',
components: {
GrandSon
},
setup () {
// 接收祖先组件提供的数据
const money = inject('money')
return {
money }
}
}
</script>
<style scoped lang="less"></style>
<template>
<div class="container">
<h3>孙组件 {
{
money}} <button @click="fn">消费20</button></h3>
</div>
</template>
<script>
import {
inject } from 'vue'
export default {
name: 'GrandSon',
setup () {
const money = inject('money')
// 孙组件,消费50,通知父组件App.vue组件,进行修改
// 不能自己修改数据,遵循单选数据流原则,大白话:数据谁定义谁修改
const changeMoney = inject('changeMoney')
const fn = () => {
changeMoney(20)
}
return {
money, fn}
}
}
</script>
<style scoped lang="less"></style>
官方文档,如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false
。 what ???
可以这样说,当 inheritAttrs 为false时,在渲染出的dom上,不会有写在 父组件中的 子组件标签上的 属性
如果为默认true, 在渲染出的dom上,就会有这个属性
可以参考 https://www.cnblogs.com/luyuefeng/p/11106172.html
getCurrentInstance 支持访问内部组件实例
只能在setup 或生命周期钩子中调用
官网文档中给出一个warning
getCurrentInstance
只暴露给高阶使用场景,典型的比如在库中。强烈反对在应用的代码中使用 getCurrentInstance
。请不要把 它当作在组合式 API 中获取 this
的替代方案来使用。
可以参考 https://my.oschina.net/u/4355717/blog/4767509