共计 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