1. 计算属性(computed)
computed 的返回值是一个响应式的 ref 对象
import { ref, computed } from 'vue'const count = ref(10)// 写法一:默认返回getter函数 返回值为一个只读的ref对象const doubleCount = computed(() => count.value * 2)// 因为返回值为ref对象 取值须要拿它的valueconsole.log(doubleCount.value) // 20// 留神:这里控制台会报正告,因为咱们只设置了getter函数doubleCount.value = 20 // warn:readonly(只读)属性 不可批改// 写法二:带有getter和setter函数const doubleCount = computed({ get: () => count.value * 2, set: (val) => count.value = val - 2})console.log(doubleCount.value) // 20// 不会报正告,因为咱们手动设置了setter函数doubleCount.value = 10 // 批改doubleCount的值 触发set函数// 所以count的值为 10 - 2 = 8console.log(count.value) // 8
2. 响应式侦听(watch)
①. 监听根本数据类型:
import { ref, watch, reactive } from 'vue'setup() { // 写法一:监听一个一般的值 const data = reactive({ count: 100 }) // 第一个参数写成函数 watch(() => data.count, (newVal, oldVal) => { console.log(newVal, 'newVal') console.log(oldVal, 'oldVal') }) // 写法二:监听ref对象 const count = ref(100) // 第一个参数写成函数 watch(() => count, (newVal, oldVal) => { console.log(newVal, 'newVal') console.log(oldVal, 'oldVal') }) // 写法三:同时监听多个值 const num = ref(200) // 第一个参数写成数组模式,代表要监听的值 watch([count, num], (newVals, oldVals) => { // 这里打印的是一个数组 值别离对应:[count, num] console.log(newVals, 'newVals') console.log(oldVals, 'oldVals') }) count.value = 300 // newVals: [300, 200] oldVals: [100, 200] num.value = 400 // newVals: [300, 400] oldVals: [300, 200]}
②. 监听援用数据类型:
import { watch, reactive } from 'vue'setup() { const list = reactive([1, 2, 3, 4, 5]) // 这里须要对援用数据类型进行拷贝 watch(() => [...list], (newVal, oldVal) => { console.log(newVal, 'newVal') console.log(oldVal, 'oldVal') }) list.push(6) // newVal: [1, 2, 3, 4, 5, 6] oldVal: [1, 2, 3, 4, 5]}
不出意外,vue3 的 watch 同样反对 deep、immediate 属性,只是用法有所扭转
const userInfo = reactive({ username: 'kyrie irving', email: '123@qq.com', like: { isBasketball: true }})// 传入一个对象作为第三个参数 可开启deep/immediatewatch(() => userInfo, (newVal, oldVal) => { console.log(newVal.like.isBasketall) console.log(oldVal.like.isBasketall)}, { deep: true })// 批改状态深层属性的值userInfo.like.isBasketball = false // 打印日志: false, false
看到日志输入两个 false 是不是感觉哪里不对劲?
留神:如果你想要在批改后的状态中拿到上一个状态里的值,须要深拷贝
起因:watch 的返回值是:以后状态(newVal)和上一个状态的援用(oldVal)
// 正确写法(对援用数据类型进行深拷贝)(这里应用vue官网举荐的lodash深拷贝办法,当然你也能够手写(手动狗头)import _ from 'lodash'watch(() => _.cloneDeep(userInfo), (newVal, oldVal) => { console.log(newVal.like.isBasketall) console.log(oldVal.like.isBasketall)})// 批改状态深层属性的值 这里就能够拿到上一个状态的值啦userInfo.like.isBasketball = false // 打印日志: false, true
3. 组合式 API(外围知识点)
假如咱们要开发一个 todoList, 那么在 vue2 的选项式(options)API 中,代码大抵长这样:
export default { components: ['Header', 'List', 'Footer'], props: { item: { type: Object, required: true, default: () => {}, } }, data() { return { value: '', } }, computed: { finallyValue() {} }, methods: { addTodo(data) {} }, mounted() { this.init() }}
很显著能够看到,咱们写的代码被瓜分到(data,computed, methods, mounted...)外面,当组件小的时候还好,但当咱们开发一个大型组件的时候,就会发现咱们的组件变得难以保护。
不晓得大家有没有这种体验:当咱们的组件代码行数太多时,咱们时常须要一直地高低‘跳转’编译器,找到相干的代码块去浏览或编写,这显然开发体验不是很好。
要是有一种货色能够解决这种开发中因为代码太过于碎片化,而且逻辑不好复用的问题,是不是能够进步咱们的开发效率?而这正是 vue3 推出组合式 API呈现的起因。
在Vue3 正确的打开方式(上)里,咱们曾经用到组合式 API 了,定义响应式对象(ref, reactive)...
补充一些对于生命周期和props的常识...
生命周期(vue3将不再须要:beforeCreate、created,起因看下文)
// vue3中为了性能,很多API(办法)都反对Tree Shaking // 所以须要从vue中显式导入,生命周期也不例外import { onBeforeMount, (对应beforeMount) onMounted, (对应mounted) onBeforeUpdate, (对应beforeUpdate) onUpdated, (对应updated) onBeforeUnmount, (对应beforeUnmount) onUnmounted, (对应unmounted) onErrorCaptured, (对应errorCaptured) 根本不必 onRenderTracked, (对应renderTracked) 根本不必 onRenderTriggered (对应renderTriggered) 根本不必} from 'vue'// 第一个参数props,第二个参数为上下文(包含slots,attrs,emit)setup(props, context) { // 用法跟vue相似,不一样的中央在于它承受一个回调函数,在钩子被组件调用时执行 onMounted(() => { console.log('mouted') }), // 其余的钩子相似,这里就不一一列举了 ...}
tips: vue3的setup是围绕beforeCreate和created生命钩子运行的,所以你不须要显式地定义这两个钩子函数,通俗易懂就是说之前在这两个钩子里编写的代码当初你都应该编写在setup函数中。
props(响应式援用)
import { toRefs } from 'vue'// 接管跟vue2一样 可进行类型校验和默认值设置props: { username: { type: String, default: '' }, email: { type: String, default: '' }}// 第一个参数props,第二个参数为上下文(包含slots, attrs, emit)// 所以你能够应用解构的写法,须要留神的是:attrs和slots是有状态的对象// 所以你应该以attrs.x 或 slots.xx 来应用,且它们是非响应式的。setup(props, { slots, attrs, emit }) { // 留神:解构会失落响应式 const { username, email } = props console.log(username, email) // 须要调用toRefs()解决响应式失落问题 const { username, email } = toRefs(props)}
4. Provide / Inject
provide(name, value) name(String类型)
inject(name, 默认值(可选)) inject的name和provide的name要绝对应
Parent.vue 父组件
<template> <div> <h1>App</h1> <p>App的count:{{ count }}</p> <p>App的用户名:{{ userInfo.username }}</p> // 子孙组件 <Child /> </div></template><script>// 先引入provideimport { provide } from 'vue' setup() { // 倡议把provide的状态转换为响应式(ref / reactive) const count = ref(100); const userInfo = reactive({ username: "kyrie", email: "123@qq.com", }); // 如果须要批改provide外面的状态 举荐在父组件注入批改状态的办法 const changeCount = () => { count.value++; }; const changeUsername = () => { userInfo.username = "wen"; }; // 为了防止inject组件批改provide的状态 应用readonly确保数据不被inject组件批改 provide("count", readonly(count)); provide("userInfo", readonly(userInfo)); provide("changeCount", changeCount); provide("changeUsername", changeUsername); }</script>
Child.vue 组件
<template> <h2>Child</h2> <p>inject的count:{{ count }}</p> <p>inject的用户名:{{ userInfo.username }}</p> <p>inject的邮箱:{{ userInfo.email }}</p> // 调用inject的changeCount办法批改count <button @click="changeCount">count++</button> // 调用inject的changeUsername办法批改用户名 <button @click="changeUsername">批改用户名</button> </template><script>import { inject } from "vue";export default { setup(props, context) { // 注入count 并设置count的默认值为0 const count = inject("count", 0); // 注入userInfo 并设置userInfo的默认值为0 const userInfo = inject("userInfo", {}); // 注入批改count的办法 const changeCount = inject("changeCount"); // 注入批改userInfo的办法 const changeUsername = inject("changeUsername"); return { count, userInfo, changeCount, changeUsername, }; },};</script>
以上就是Vue3罕用的也算比拟外围的API了,如果你有什么纳闷或者问题,欢送在下方留言哦...