Vue3 —— 其他 Composition API(shallowReactive、shallowRef、readonly、shallowReadonly、toRaw、markRaw....)

系列文章目录



shallowReactive

作用

只处理对象最外层属性的响应式(浅响应式)

什么时候用?

如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ==> shallowReactive

实例

由于 salary 不是最外层的属性,所以当点击增加薪资时,薪资不会发生响应式变化

	<template>
	  <div>
	    <h4>{
    
    {
    
    person}}</h4>
	    <h3>姓名:{
    
    {
    
    name}}</h3>
	    <h3>年龄:{
    
    {
    
    age}}</h3>
	    <h3>薪资:{
    
    {
    
    job.j1.salary}}K</h3>
	    <button @click="name+='~'">修改姓名</button>
	    <button @click="age++">增长年龄</button>
	    <button @click="job.j1.salary++">增加薪资</button>
	  </div>
	</template>
	
	<script>
	import {
    
     toRefs, shallowReactive } from "vue";
	export default {
    
    
	  name: "Demo",
	  setup() {
    
    
	    // 数据
	    let person = shallowReactive({
    
    
	      name: '张三',
	      age: 18,
	      job: {
    
    
	        j1:{
    
    
	          salary: 20
	        }
	      }
	    })

	    // 返回一个对象
	    return {
    
    
	      person,
	      ...toRefs(person)
	    };
	  },
	};
	</script>

点击姓名和年龄按钮都会响应式变化,点击薪资按钮不变化:

在这里插入图片描述

shallowRef

作用

只处理基本数据类型的响应式,不进行对象的响应式处理

什么时候用?

如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ==> shallowRef

实例

对于对象数据,不会修改该对象中的属性,所以点击按钮值不变

	<template>
	  <div>
	    <h4>当前的x值是:{
    
    {
    
    x.y}}</h4>
	    <button @click="x.y++">点我x+1</button>
	    <hr>
	  </div>
	</template>
	
	<script>
	import {
    
     shallowRef } from "vue";
	export default {
    
    
	  name: "Demo",
	  setup() {
    
    
	    // 数据
	    let x = shallowRef({
    
    
	      y: 0
	    })
	    
	    //可以发现是object类型不是proxy,所以不能响应式变化
	    console.log(x); 
	    
	    // 返回一个对象
	    return {
    
    
	      x,
	    };
	  },
	};
	</script>

点击按钮,值不变:

在这里插入图片描述

控制台输出的 x(对象类型非Proxy类型,所以不能响应式):

在这里插入图片描述

若修改 button 里面的内容,生新的对象,此时改变的是 x,则会响应式变化

	<button @click="x = {y:200}">点我更新x</button>

更新前:
在这里插入图片描述
更新后:
在这里插入图片描述

readonly

作用

让一个响应式数据变为只读的(深只读)

应用场景:不希望数据被修改时

实例

  1. 把 person 通过 readonly 变为只读的
  2. 点击按钮时不会发生任何响应式变化
	<template>
	  <div>
	    <h3>姓名:{
    
    {
    
    name}}</h3>
	    <h3>年龄:{
    
    {
    
    age}}</h3>
	    <h3>薪资:{
    
    {
    
    job.j1.salary}}K</h3>
	    <button @click="name+='~'">修改姓名</button>
	    <button @click="age++">增长年龄</button>
	    <button @click="job.j1.salary++">增加薪资</button>
	  </div>
	</template>
	
	<script>
	import {
    
     reactive, toRefs, readonly } from "vue";
	export default {
    
    
	  name: "Demo",
	  setup() {
    
    
	    // 数据
	
	    let person = reactive({
    
    
	      name: '张三',
	      age: 18,
	      job: {
    
    
	        j1:{
    
    
	          salary: 20
	        }
	      }
	    })
	
	    person = readonly(person)
	    
	    // 返回一个对象
	    return {
    
    
	      ...toRefs(person)
	    };
	  },
	};
	</script>

三个属性均为只读:

在这里插入图片描述

shallowReadonly

作用

让一个响应式数据变为只读的(浅只读)

应用场景:不希望数据被修改时

实例

  1. 浅只读,只是第一层限制只读
  2. 对深层的数据不影响其响应式
	<template>
	  <div>
	    <h3>姓名:{
    
    {
    
    name}}</h3>
	    <h3>年龄:{
    
    {
    
    age}}</h3>
	    <h3>薪资:{
    
    {
    
    job.j1.salary}}K</h3>
	    <button @click="name+='~'">修改姓名</button>
	    <button @click="age++">增长年龄</button>
	    <button @click="job.j1.salary++">增加薪资</button>
	  </div>
	</template>
	 
	<script>
	import {
    
     reactive, toRefs, shallowReadonly } from "vue";
	export default {
    
    
	  name: "Demo",
	  setup() {
    
    
	    // 数据
	
	    let person = reactive({
    
    
	      name: '张三',
	      age: 18,
	      job: {
    
    
	        j1:{
    
    
	          salary: 20
	        }
	      }
	    })
	
	    person = shallowReadonly(person)
	    
	    // 返回一个对象
	    return {
    
    
	      ...toRefs(person)
	    };
	  },
	};
	</script>

name 和 age 为只读属性,薪资可以响应式变化:

在这里插入图片描述

toRaw

作用

将一个由 reactive 生成的响应式对象转为普通对象 (对 ref 无效)

使用场景

用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新

实例

通过 toRaw(xxx),可以将响应式对象转为普通对象

	<template>
	  <div>
	    <h3>姓名:{
    
    {
    
    name}}</h3>
	    <h3>年龄:{
    
    {
    
    age}}</h3>
	    <h3>薪资:{
    
    {
    
    job.j1.salary}}K</h3>
	    <button @click="showRawPerson">输出最原始的person</button>
	  </div>
	</template>
	
	<script>
	import {
    
     reactive, toRefs, toRaw } from "vue";
	export default {
    
    
	  name: "Demo",
	  setup() {
    
    
	    // 数据
	    let person = reactive({
    
    
	      name: '张三',
	      age: 18,
	      job: {
    
    
	        j1:{
    
    
	          salary: 20
	        }
	      }
	    })
	
	    function showRawPerson(){
    
    
	      const p = toRaw(person)
	      console.log(p);
	    }
	    
	    // 返回一个对象
	    return {
    
    
	      ...toRefs(person),
	      showRawPerson,
	    };
	  },
	};
	</script>

控制台输出原始数据:

在这里插入图片描述

markRaw

作用

标记一个对象,使其永远不会再成为响应式对象

应用场景

  1. 有些值不应被设置为响应式,例如复杂的第三方类库等
  2. 当渲染具有不可变数据的大列表时,跳过响应式转换可以提高性能

实例

使用 markRaw(xxx) 标记,则 xxx 不再是响应式对象

	<template>
	  <div>
	    <h3>姓名:{
    
    {
    
    name}}</h3>
	    <h4 v-show="person.car">汽车信息:{
    
    {
    
    person.car}}</h4>
	    <button @click="addCar">给人添加一台车</button>
	    <button @click="person.car.name+='!'">换车名</button>
	    <button @click="person.car.price++">换价格</button>
	  </div>
	</template>
	
	<script>
	import {
    
     reactive, toRefs, markRaw  } from "vue";
	export default {
    
    
	  name: "Demo",
	  setup() {
    
    
	    // 数据
	    let person = reactive({
    
    
	      name: '张三',
	    })
	
	
	    function addCar(){
    
    
	      let car = {
    
    name: '奔驰', price: '40'}
	      person.car = markRaw(car)
	    }
	    
	    // 返回一个对象
	    return {
    
    
	      person,
	      ...toRefs(person),
	      addCar
	    };
	  },
	};
	</script>

页面初始状态:

在这里插入图片描述

添加车后,点击换车名和换价格无变化:

在这里插入图片描述

customRef

作用

创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制

实例

  1. 首先要导入 customRef
  2. 定义自定义函数 — myRef
  3. 第一个 return 是把自己写的逻辑交出去,第二个 return 是语法规范要返回一个对象
  4. myRef() 里的 value 是初始值, set() 里的 newValue 是新值
  5. set() 里面的 trigger() 的作用是通知 Vue 去重新解析模板
  6. get() 里面的 track() 的作用是通知 Vue 追踪 value 的变化(提前和 get 商量一下,让它认为这个 value 是有用的)
  7. 声明一个 timer,在开启下一个定时器之前先清除定时器,是为了防抖。(防止快速输入时,输入框抖动的情况)
	<template>
	  <div>
	    <input type="text" v-model="keyWord" />
	    <h3>{
    
    {
    
     keyWord }}</h3>
	  </div>
	</template>
	
	<script>
	import {
    
     customRef } from "vue";
	
	export default {
    
    
	  name: "App",
	  setup() {
    
    
	    // 自定义ref —— 名为:myRef
	    function myRef(value, delay) {
    
    
	      let timer;
	      return customRef((track, trigger) => {
    
    
	        return {
    
    
	          get() {
    
    
	            console.log(`有人从myRef容器中读取数据了,我把${
      
      value}给它了`);
	            track(); //通知Vue追踪value的变化(提前和get商量一下,让它认为这个value是有用的)
	            return value;
	          },
	          set(newValue) {
    
    
	            console.log(`有人把myRef容器中数据改了:${
      
      newValue}`);
	            clearTimeout(timer)
	            timer = setTimeout(() => {
    
    
	              value = newValue;
	              trigger(); //通知Vue去重新解析模板
	            }, delay);
	          },
	        };
	      });
	    }
	
	    let keyWord = myRef("hello", 500);
	    return {
    
     keyWord };
	  },
	};
	</script>

效果如下:

customRef

provide 与 inject

作用

实现祖与后代组件间通信

原理

父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据

在这里插入图片描述

实例

App.vue(祖组件)

	<template>
	  <div class="app">
	    <h3>我是App组件(祖),{
    
    {
    
     name }}——{
    
    {
    
     price }}</h3>
	    <Child />
	  </div>
	</template>
	
	<script>
	import {
    
     reactive, toRefs, provide } from "vue";
	import Child from "./components/Child.vue";
	export default {
    
    
	  name: "App",
	  components: {
    
     Child },
	  setup() {
    
    
	    let car = reactive({
    
     name: "奔驰", price: "40W" });
	    provide("car", car); //给自己的后代组件传递数据
	    return {
    
     ...toRefs(car) };
	  },
	};
	</script>
	
	<style scoped>
	.app {
    
    
	  background-color: gray;
	  padding: 10px;
	}
	</style>

Child.vue(子组件)

	<template>
	  <div class="child">
	    <h3>我是Child组件(子)</h3>
	    <Son/>
	  </div>
	</template>
	
	<script>
	import Son from './Son.vue';
	export default {
    
    
	    name: "Child",
	    components: {
    
     Son }
	};
	</script>
	
	<style scoped>
	.child {
    
    
	  background-color: skyblue;
	  padding: 10px;
	}
	</style>

Son.vue(孙组件)

	<template>
	  <div class="son">
	    <h3>我是Son组件(孙),{
    
    {
    
    car.name}} —— {
    
    {
    
    car.price}}</h3>
	  </div>
	</template>
	
	<script>
	import {
    
    inject} from 'vue'
	export default {
    
    
	  name: "Son",
	  setup(){
    
    
	    let car = inject('car')
	    return {
    
    car}
	  }
	};
	</script>
	
	<style scoped>
	.son {
    
    
	  background-color: orange;
	  padding: 10px;
	}
	</style>

祖组件向孙组件传递数据:

在这里插入图片描述

响应式数据的判断

  1. isRef:检查一个值是否为一个ref对象
  2. isReactive:检查一个对象是否是由 reactive 创建的响应式代理
  3. isReadonly:检查一个对象是否是由 readonly 创建的只读代理
  4. isProxy:检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

实例

对以上四种判断做测试:

	<template>
	  <div class="app">
	    <h3>我是App组件</h3>
	  </div>
	</template>
	
	<script>
	import {
    
     ref, reactive, readonly, toRefs, isRef, isReactive, isReadonly, isProxy } from "vue";
	export default {
    
    
	  name: "App",
	  setup() {
    
    
	    let car = reactive({
    
     name: "奔驰", price: "40W" });
	    let sum = ref(0)
	    let car2 = readonly(car)
	    console.log(isRef(sum));
	    console.log(isReactive(car));
	    console.log(isReadonly(car2));
	    console.log(isProxy(car));
	    return {
    
     sum, ...toRefs(car), car2 };
	  },
	};
	</script>

控制台输出结果:

在这里插入图片描述

不积跬步无以至千里 不积小流无以成江海

点个关注不迷路,持续更新中…

猜你喜欢

转载自blog.csdn.net/qq_45902692/article/details/125934479