Composition API

Composition API


Composition API也称为组合式API,实现代码的共享以及重用

Vue3.0 暴露变量必须 return 出来,template中才能使用;

Vue3.2 中 只需要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需return,template可直接使用


Composition-api提供的函数


setup

新的 setup 组件选项在创建组件之前执行,一旦 props 被解析,并充当合成 API 的入口点

由于在执行 setup 时尚未创建组件实例,因此在 setup 选项中没有 this。

这意味着,除了props 之外,将无法访问组件中声明的任何属性——本地状态、计算属性或方法。


setup()接收两个参数 propscontext

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属性



ref

用来定义响应式的 字符串、 数值、 数组、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
        }
    },
}



reactive

用来定义响应式的对象

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中定义的对象名称可以直接修改


watchEffect

在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。

与侦听器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
        };
    },
};



watch

与原先的侦听器类似,接收三个参数,第一个时要监听的状态,第二个是处理函数,第三个是配置项

对比watchEffect,watch允许我们:

懒执行,也就是说仅在侦听的源变更时才执行回调;

更明确哪些状态的改变会触发侦听器重新运行;

访问侦听状态变化前后的值

export default {
    name: "Watch",
    setup() {
    	const a = ref('cc')
        const data = reactive({
            count1: 0,
            count2: 0
        });
        
        // 侦听单个数据源
        // 不能直接侦听data.count1
        let stop1 = watch(data, () =>
            console.log("watch1", data.count1, data.count2)
        );
		
		// 如果需要监听data.count1 则需要函数返回值的形式
		stop1 = watch(() => data.count1, (value, oldValue, onCleanup) =>
            console.log("watch1", data.count1, data.count2)
        );

		// 监听ref属性且进行配置
        watch(a, (value, oldValue, onCleanup) => {
		  console.log(d.value)
		}, {
		  immediate: true
		})
        
        // 侦听多个数据源
        let stop2 = watch([data], () => {
            console.log("watch2", data.count1, data.count2);
        });
        
		// 同样需要监听多个对象的值时也需要函数返回值
		// 当监听多个数据组成的数组时,在第二个参数的回调函数的参数中对应接收多个数组 每一个对应一个监听的值
		stop2 = watch(() => [data.count1, data.count2], ([newCount1, oldCount1], [newCount2, oldCount2]) => {
            console.log("watch2", data.count1, data.count2);
        });

		setInterval(() => {
            data.count1++;
        }, 1000);
        
        return {
            data,
            stopAll: () => {
                stop1();
                stop2();
            },
        };
    },
};

当需要停止对数据的监听时,调用watch的返回值即可

stop()
stop2()

// 当我们使用watch回调无法停止监听时也可以通过设置flag实现



无论watch还是watchEffect,在回调函数中获取dom元素都是更新前的,如果要获取更新后的dom,需要设置配置项flush: 'post'

watch(source, callback, {
  flush: 'post'
})
watchEffect(callback, {
  flush: 'post'
})

// 或者直接使用vue3新增的另一个hooks
watchPostEffect(() => {
  /* 在 Vue 更新后执行 */
})



computed

与原先的计算属性相同

export default {
    name: "解构响应式对象数据",
    setup() {
        const user = reactive({
            firstName: "",
            lastName: "",
        });
        const fullName = computed(() => {
            return user.firstName + " " + user.lastName
        })
        return {
            ...toRefs(user),
            fullName
        };
    },
};



toRefs

解构响应式对象数据而不丢失响应性

响应式对象数据如果通过es6…运算符解构就会消除响应性

把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应

export default {
    name: "解构响应式对象数据",
    setup() {
        const user = reactive({
            username: "张三",
            age: 10000,
        });
        return {
            ...toRefs(user)
        };
    },
};



readonly

传入一个对象(响应式或普通)或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 };
  },
};



defineProps

defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入。

在vue3的非语法糖setup和在vue2中的写法是 props , 父组件通过将值传递给子组件,子组件通过defineProps进行接收。

注意:defineProps() 宏中的参数不可以访问 <script setup>中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。

const props = defineProps<{
   foo: String,
   bar?: Number
}>()



defineEmits

defineEmitsdefineProps 一样也是仅用于 <script setup> ,并且不需要导入。

在vue3的非语法糖setup中的写法是 emits ,用于子组件触发父组件的的事件,并且进行传值。

defineEmits 的不同点在于,组件要触发的事件可以显式地通过 defineEmits() 宏来声明。

注意:如果一个原生事件的名字 (例如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不会再响应原生的 click 事件。

const emit = defineEmits<{
	(e: 'submit', num: number): void
}>()



defineExpose

在使用 <script setup> 的时候,组件的实例是默认关闭的不能够通过模板引用或者$parent 进行被访问的。

通过 defineExpose 可宏来显式指定在 <script setup> 组件中要暴露出去的属性。

const isShow = ref<boolean>(false)
defineExpose({ // 宏来显示指定组件中属性暴露出去
  isShow,
});



生命周期hooks

选项式 APIHook inside setup
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。

换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('Component is mounted!')
    })
  }
}



Provider Inject

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>
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Raccom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值