共计 5284 个字符,预计需要花费 14 分钟才能阅读完成。
1. 计算属性(computed)
computed 的返回值是一个响应式的 ref 对象
import {ref, computed} from 'vue' | |
const count = ref(10) | |
// 写法一:默认返回 getter 函数 返回值为一个只读的 ref 对象 | |
const doubleCount = computed(() => count.value * 2) | |
// 因为返回值为 ref 对象 取值须要拿它的 value | |
console.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 = 8 | |
console.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/immediate | |
watch(() => 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> | |
// 先引入 provide | |
import {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 了,如果你有什么纳闷或者问题,欢送在下方留言哦 …
正文完
发表至: javascript
2021-03-18