导读
toRef、toRefs、vue3生命周期、markRaw与toRaw、customRef自定义Ref、provide与inject祖后代组件间通信、组合式API的优势、teleport标签、readonly、vue3中dom获取ref
toRef
- 创建一个ref对象,其value值指向另一个对象中的某个属性。
- 将响应式对象中某个属性单独响应式提供给外部使用
- toRefs与toRef功能一致,但可以批量创建多个ref对象。
return{ //因为person是响应式的,所以这个对象在后期添加属性也是响应式的,也会检测到新添加的新属性 person, }
<template> <h1>子组件</h1> <h2>姓名:{{ name }}</h2> <h2>性别:{{ job.a.sex }}</h2> <button @click="name += '~'">修改名字</button> <button @click="job.a.sex = '女'">修改为女</button> <button @click="job.a.sex = '男'">修改为男</button> </template> <script> import { reactive, toRef, toRefs } from 'vue' export default { name: 'Demo', setup(props, context) { //数据 let person = reactive({ name: 'wxl', age: 18, job: { a: { sex: '男', }, }, }) //因为这是解构取值且setup只执行一次。解构出来的值,以后不会监测到,更不是响应式的, let {name,age}=person //所以这个不是响应式的 let {name,age}=toRefs(person) //2、这种也可以变成ref return { //这种ref虽然也能实现相同效果,但是不推荐,因为这样会造成以后修改的值不是person对象里的值 // name:ref(person.name) //但是这种就可以,因为这是引用(value---->person.namee) // name:toRef(person,'name'), // age:toRef(person,'age'), // sex:toRef(person.job.a,'sex') //1、toRefs把person对象里的所用属性全部变成ref ...toRefs(person),//展开person对象的所有属性 } }, } </script>
生命周期
vue3也提供了组合式(Composition)API的生命周期钩子,也就是放在setup里面,放入之前要在import引入(组合式与上面的不推荐同时使用)
- beforeCreate ——–> setup() 官方认为组合式API没有这个 钩子,setup就是这个钩子
- created——-> setup()
- beforeMount——->onBeforeMount(()=>{})
- mounted——->onMounted
- beforeUpdate——->onBeforeUpdate
- updated——->onUpdated
- beforeUnmount——–>onBeforeUnmount
- unmounted———–>onUnmounted
其他API
readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读。接收一个响应式对象(因为是引用传递,需要考虑是否被修改)的数据。因为是响应式数据,所以在父组件数据发生改变的时候,子组件也会发生改变;但是当子组件想改变这个值的时候,是不允许改变的。如代码所示
let job = reactive({ name:2, time:3, z:0, type:{ name:'wxl' } }) //接收一个响应式对象的数据,然后传给子组件使用 let childUseJob = readonly(job)
- shallowReadonly:让一个响应式数据变为浅只读,即深的还是可以修改的。接收一个响应式数据。
- 应用场景: 不希望子组件修改父组件传的数据
shallowReactive 与 shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
let job = shallowReactive({ name:2, time:3, z:0, type:{ name:'wxl' } }) //也就是只考虑第一层的响应式
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
markRaw与toRaw
- markRaw
- 标记一个对象,让他永远不会再成为响应式对象
- 应用场景
- 有些值不应该背设置为响应式,例如第三方类库
- 当渲染具有不可变数据源的大型列表时,跳过响应式转换可以提高性能
- toRaw
- 将一个由reactive生成的响应式对象转为普通对象
-
let job = reactive({ name:2, time:3, z:0, type:{ name:'wxl' } }) //接收一个响应式的数据 job = toRaw(job)
-
- 用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面上的更新
- 将一个由reactive生成的响应式对象转为普通对象
customRef
创建一个自定义ref
<template> <h1>子组件</h1> <input type="text" v-model="keyWord"> <h2>{{keyWord}}</h2> </template> <script> import { customRef } from 'vue' export default { name: 'Demo', setup(props, context) { // /自定义一个ref :myRef function myRef(value){ let timer return customRef((track,trigger)=>{ return{//return get/set对象 get(){ console.log(`被读取数据${value}`) track()//2再通知vue追踪数据value的变化,才能真正拿到最新的value值 return value }, set(newValue){ console.log(`被改为${newValue}`) clearTimeout(timer) timer = setTimeout(()=>{ value = newValue //1改过之后trigger通知vue去重新解析页面模版,即重新调用get(),此时value已经被修改了 trigger() },1000) } } }) } let keyWord = myRef('zzl') return { keyWord } }, } </script>
provide与inject祖后代组件间通信
- 父组件有一个provide选项来提供数据,组件有一个inject选项来开始使用这些数据
祖组件
setup(props, context) { let person = reactive({name:'wxl',age:'23'}) provide('personData',readonly(person)) }
后代组件
setup(props, context) { const person = inject('personData') }
组合式API的优势
- 使用传统的配置式API,新增或者修改一个需求,就需要再data、methods、computed等里面修改,
- 但是组合式API利用hooks函数,可以让相关功能的代码有序的组织在一起。
/hooks/userPoint.js
import { reactive,onMounted,onBeforeUnmount } from "vue"; export default function(){ //实现鼠标打点的相关数据 let point = reactive({ x:0, y:0 }) //实现鼠标打点的相关方法 function savePoint(event){ point.x=event.pageX point.y=event.pageY } //实现鼠标打点的相关生命周期钩子 onMounted(()=>{ // window.addEventListener('click',function(){}) window.addEventListener('click',savePoint) }) onBeforeUnmount(() => { window.removeEventListener('click',savePoint) }) //返回数据 return point }
App.vue
<template> <h1>父组件</h1> <h2>x:{{point.x}}----y:{{point.y}}</h2> </template> <script> import userPoint from './hooks/userPoint.js' export default { name: 'App', setup() { let point = userPoint() return {point} } } </script>
teleport标签
<template> <h1>后代组件</h1> <teleport to='#tcBox'> <div class="box"> 我会被传送给ID为tcBOx下,以便不占用我所处后代层级的大小,当然也可以放在body等下 </div> </teleport> </template>
异步组件
defineAsyncComponent异步引入
异步引用非路由使用的组件,因为在路由里可以用另一种方法异步加载,但是它们的目的都是为了分包,都是为了加载速度快。
<Suspense> <template v-slot:default> <child></child> </template> <template v-slot:fallback> <h3>加载中...</h3> </template> </Suspense> </template> <script> import {defineAsyncComponent} from 'vue' ////异步组件,defineAsyncComponent接收一个peomise,而import返回的就是promise。 const child = defineAsyncComponent(()=>import{'./components/Demo.vue'}) export default { components:{child},//此时这个组件都是异步加载的了 name: 'App', }
Suspense
它是一个内置的全局组件,有两个插槽。
- default:如果default可以显示,就显示default的内容
- fallback:如果default无法显示,那么就会显示fallback插槽的内容
<suspense> <template #default> <p></p> </template> <template #fallback> <p></p> </template> </suspense>
vue3中获取ref
<template> <!-- 3 --> <h1 ref="title">标题</h1> </template> <script> import { defineComponent, onMounted } from 'vue' export default defineComponent({ setup(props, context) { //1 const title = ref(null) onMounted(()=>{ //vue3中ref只有在挂载之后才能拿到值 // 4 console.log(title) }) return { // 2 title, } }, }) </script> <style></style>