vue3其他Composition api

shallowReactive 与 shallowRef

  • shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
  • shallowRef: 只处理了 value 的响应式, 不进行对象的 reactive 处理

    什么时候用浅响应式呢?

    1. 一般情况下使用 ref 和 reactive 即可
    2. 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
    3. 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <template>
    <h2>shallowReactive 与 shallowRef</h2>
    <h3>m1:{{ m1 }}</h3>
    <h3>m2:{{ m2 }}</h3>
    <h3>m3:{{ m3 }}</h3>
    <h3>m4:{{ m4 }}</h3>
    <hr />
    <button @click="handleUpdata">更新数据</button>
    </template>

    <script lang="ts">
    import {
    defineComponent,
    reactive,
    ref,
    shallowReactive,
    shallowRef,
    } from "vue";
    export default defineComponent({
    name: "",
    setup() {
    // m1 reactive 深度劫持,深度响应式
    const m1 = reactive({ a: 1, b: { c: 2 } })
    const m2 = shallowReactive({ a: 1, b: { c: 2 } })

    const m3 = ref({ a: 1, b: { c: 2 } })
    const m4 = shallowRef({ x: 1, y: { c: '2' } })

    const handleUpdata = () => {
    // m1.b.c += 1
    // m2.b.c += 1
    // m3.value.a += 1
    // m3.value.b.c += 1
    // m4.value.y.c += 1
    console.log(m3);
    console.log(m4);

    // 为什么reactive或ref在使用时,shallowRef和shallowReactive的数据会变成响应式?
    // 数据处于同一环境,都被同一个Fragment包着,Fragment是一种VNode的类型,m1.b.c触发改变
    // 对应的模板领域会进行更新,所以会发生改变
    }
    return {
    m1,
    m2,
    m3,
    m4,
    handleUpdata,
    };
    },
    });
    </script>

readonly 与 shallowReadonly

  • readonly 深度只读数据 shallowReadonly: 浅只读数据

    应用场景:
    在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <template>
    <h2>readonly 与 shallowReadonly</h2>
    <h3>state:{{ state2 }}</h3>
    <hr />
    <button @click="handleUpdata">更新数据</button>
    </template>

    <script lang="ts">
    import { defineComponent, reactive, readonly, ref, shallowReadonly } from "vue";
    export default defineComponent({
    name: "",
    setup(props, { attrs, emit, slots }) {
    const state = reactive({
    name:'he',
    age:20,
    car:{
    name:'红旗'
    }
    })
    // 只读数据---深度只读 对象的属性不能修改,对象的对象的属性也不能修改
    // const state2 = readonly(state)
    // 只读数据---浅度只读 对象的属性不能修改,对象的对象的属性可以修改
    const state2 = JSON.parse(JSON.stringify(shallowReadonly(state)))

    // 问题:state对象里的属性改变,也会影响state2对象里的属性(浅拷贝)
    // 解决:JSON.parse(JSON.stringify(shallowReadonly(state))) 进行深拷贝
    const handleUpdata = ()=>{
    // state2.name += '=='
    state2.car.name += '--'
    console.log(state.car,state2.car);
    }
    return {
    state2,
    state,
    handleUpdata
    };
    },
    });
    </script>

toRaw 与 markRaw

  • toRaw:把代理对象变成普通对象,数据变化,界面不变化
  • toRaw:返回由 reactive 或 readonly 方法转换成响应式代理的普通对象
  • markRaw:markRaw标记的对象,不能成为代理对象,数据不能在页面更新

markRaw应用场景:
有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。
当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<template>
<h2>toRaw 与 markRaw</h2>
<h3>state:{{ state }}</h3>
<h3>user1:{{ user1 }}</h3>
<h3>user2:{{ user2 }}</h3>
<hr />
<button @click="handleUpdata">toRaw</button>
<button @click="handleUpdata1">markRaw</button>
</template>

<script lang="ts">
import { defineComponent, markRaw, reactive, toRaw } from "vue";
interface userInfo {
name: string;
age: number;
car: any;
likes?: string[];
}
export default defineComponent({
name: "",
setup() {
const state = reactive<userInfo>({
name: "he",
age: 20,
car: {
name: "红旗",
},
});
// const user1:any = reactive({})
// const user2:any = reactive({})
const handleUpdata = () => {
// 把代理对象变成普通对象,数据变化,界面不变化
const user1 = toRaw(state);
user1.name += "==";
console.log(state, user1);
};
const handleUpdata1 = () => {
// markRaw标记的对象,不能成为代理对象,数据不能在页面更新
const likes = ["1"];
state.likes = markRaw(likes);
setInterval(() => {
if (state.likes) {
state.likes[0] += "0";
}
}, 1000);
console.log(state);

// const user1 = JSON.parse(JSON.stringify(markRaw(state)));
// user1.name += '123'
// console.log(state);
// console.log(user1);

};
return {
state,
handleUpdata,
handleUpdata1,
};
},
});
</script>

toRef

  • 为源响应式对象上的某个属性创建一个 ref 对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
  • const age = toRef(state, "age")
  • 区别ref : 拷贝了一份新的数据值单独操作, 更新时相互不影响
  • 应用: 当要将 某个 prop 的 ref 传递给复合函数时,toRef 很有用

    App.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <template>
    <h2>toRef</h2>
    <h3>state:{{ state }}</h3>
    <h3>age:{{ age }}</h3>
    <h3>name:{{ name }}</h3>
    <hr />
    <button @click="handleUpdata">toRaw</button>
    <Child :age='age'></Child>
    </template>

    <script lang="ts">
    import { defineComponent, reactive, toRef, ref } from "vue";
    import Child from './components/Child.vue'
    export default defineComponent({
    name: "",
    components:{
    Child
    },
    setup() {
    const state = reactive({
    name: "he",
    age: 20,
    car: {
    name: "红旗",
    },
    });
    // 把响应式数据state的属性age变成ref对象
    const age = toRef(state, "age");
    // 把响应式数据state的属性name,使用ref进行包装,变成ref对象
    const name = ref(state.name);
    console.log(age,name);

    const handleUpdata = () => {
    state.age += 2
    // name.value += 3
    };
    return {
    state,
    age,
    name,
    handleUpdata,
    };
    },
    });
    </script>

    Child.vue

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <template>
    <h2>Child组件,子组件</h2>
    <h3>age:{{ age }}</h3>
    <h3>length:{{ length }}</h3>
    </template>

    <script lang="ts">
    import { computed, defineComponent, Ref, ref, toRef } from "vue";
    // 参数必须是一个Ref类型的数据
    function useGetLength(age:Ref) {
    return computed(() => {
    return age.value.toString().length;
    });
    }
    export default defineComponent({
    name: "",
    // props: {
    // age: {
    // type: Number,
    // required: true,
    // },
    // },
    props: ["age"],
    setup(props, { attrs, emit, slots }) {
    //把一个对象里的属性,变成ref对象
    const length = useGetLength(toRef(props, "age"));
    console.log(length);
    console.log(props);
    return {
    length,
    };
    },
    });
    </script>

customRef

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

需求: 使用 customRef 实现 debounce 的示例
get: 告诉vue追踪数据:track() set: 告诉Vue更新页面:trigger()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<template>
<h2>customRef 的使用:自定义hook防抖的函数</h2>
<input type="text" v-model="keyword" />
<p>{{ keyword }}</p>
</template>

<script lang="ts">
import { customRef, defineComponent, ref } from "vue";
// 自定义hook防抖的函数
// value传入的数据,将来数据的类型不确定,所以用泛型
// num是防抖的间隔时间
function UsecustomRef<T>(value: T, num=200) {
// 准备一个存储定时器的id的变量
let timeoutId: number;
return customRef((track, trigger) => {
return {
// 返回数据
get() {
// 告诉vue追踪数据
track();
return value;
},
// 设置数据
set(newValue: T) {
// 清理定时器
clearTimeout(timeoutId);
// 开启定时器
timeoutId = setTimeout(() => {
value = newValue;
// 告诉Vue更新页面
trigger();
}, num);
},
};
});
}
export default defineComponent({
name: "",
setup(props, { attrs, emit, slots }) {
// const keyword = ref('abc')
const keyword = UsecustomRef("abc", 500);
return {
keyword,
};
},
});
</script>

provide 与 inject:父组件与孙子组件之间的通信

provide和inject提供依赖注入,功能类似 2.x 的provide/inject
实现跨层级组件(祖孙)间通信

App.vue(父组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<h2>provide 与 inject</h2>
<p>color:{{color}}</p>
<button @click="color = 'yellow'">黄</button>
<button @click="color = 'green'">绿</button>
<button @click="color = 'blue'">蓝</button>
<hr>
<Son></Son>
</template>

<script lang="ts">
// 实现夸子级组件,与孙子组件通信
import { defineComponent, ref,provide } from "vue";
import Son from "./components/Child.vue";

export default defineComponent({
name: "",
components:{
Son
},
setup(props, { attrs, emit, slots }) {
// 响应式数据
const color = ref('red')
// provide
provide('color',color)
return {
color
};
},
});
</script>

Child.vue(子组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<h2>子级组件</h2>
<hr />
<Grandson></Grandson>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import Grandson from "./Grandson.vue";
export default defineComponent({
name: "Child",
components: {
Grandson,
},
setup(props, { attrs, emit, slots }) {
return {};
},
});
</script>

Grandson.vue(孙组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<h2 :style="{color}">孙子组件</h2>
</template>
<script lang="ts">
import { defineComponent, inject } from "vue";
export default defineComponent({
name: "Grandson",
setup(props, { attrs, emit, slots }) {
const color = inject('color')
return {
color
};
},
});
</script>

响应式数据的判断

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template>
<h2>响应式数据的判断</h2>
</template>

<script lang="ts">
import {
defineComponent,
ref,
isRef,
isReactive,
reactive,
isReadonly,
readonly,
isProxy,
} from "vue";
export default defineComponent({
name: "",
// isRef isReactive isReadonly isProxy
setup(props, { attrs, emit, slots }) {
// isRef: 检查一个值是否为一个 ref 对象,返回true or false
console.log(isRef(ref(0))); // true
// isReactive: 检查一个对象是否是由 reactive 创建的响应式代理,返回true or false
console.log(isReactive(reactive({}))); // true
// isReadonly: 检查一个对象是否是由 readonly 创建的只读代理,返回true or false
const str = ref('abc')
const str1 = readonly(str)
console.log('str:',isReadonly(str1)); // true
// isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
console.log("isProxy1:",isProxy(readonly({})));
console.log("isProxy2:",isProxy(reactive({})));
return {};
},
});
</script>

-------------本文结束感谢您的阅读-------------

我的传送门:个人网站GithubGitee