02-组合API详解

7/24/2022

# 一、组合API详解

为什么要使用setup组合?

  • Vue3 中新增的 setup,目的是为了解决 Vue2 中“数据和业务逻辑不分离”的问题

Vue3中使用 setup 是如何解决这一问题的呢?

  • 第1步: 用setup组合API 替换 vue2 中的data/computed/watch/methods等选项
  • 第2步: 把setup中相关联的功能封装成一个个可独立可维护的hooks

# 1,ref

作用:一般用于定义基本数据类型数据,比如 String / Boolean / Number等
原理:ref 的背后是使用 reactive 来实现的响应式
语法:const x = ref(100)
访问:在 setup 中使用 .value 来访问

// Demo.vue

<template>
    <h1>ref的使用</h1>
    <h1 v-text="num"></h1>
    <button @click="num++">自增</button>
    <button @click="add">自增</button>
</template>


<script setup>
import {ref} from "vue"
// num叫一个ref变量,也叫ref对象
let num = ref(1000);
let add = ()=>{
    num.value++
}
</script>

<style lang="less" scoped>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 2,isRef

作用:判断一个变量是否为一个 ref 对象。
语法:const bol = isRef(x)

// Demo.vue

<template>
  <h1>isRef的使用</h1>
  <h1 v-text="num"></h1>
  <button @click="num++">自增</button>
  <button @click="add">自增</button>
</template>

<script setup>
import {ref,isRef} from "vue"
let num = ref(1000);
let add = ()=>{
    num.value++
}

// num就是一个ref变化
console.log(isRef(num)); // true
console.log(isRef(110)); // false
console.log(isRef(num.value)); // false

</script>

<style lang="less" scoped>
</style>
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

# 3,unref

作用:用于返回一个值,如果访问的是 ref变量,就返回其 .value值;如果不是 ref变量,就直接返回。
语法:const x = unref(y)

// Demo.vue

<template>
  <h1>unref的使用</h1>
  <h1 v-text="num"></h1>
  <button @click="num++">自增</button>
  <button @click="add">自增</button>
</template>

<script setup>
import {ref,isRef,unref} from "vue"
let num = ref(1000);
let add = ()=>{
    num.value++
}

console.log(isRef(num));
// 如果给unref传递一个ref变量,得到这个ref的value值
console.log(unref(num));
// 如果给unref传递的不是一个ref变量,得到这个值的本身
console.log(unref(num.value));
console.log(unref(110));

</script>

<style lang="less" scoped>
</style>
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

# 4,customRef

作用:自定义ref对象,把ref对象改写成get/set,进一步可以为它们添加 track/trigger。

// Demo.vue

<template>
  <h1>customRef的使用</h1>
  <input type="text" v-model="name" />
</template>

<script setup>
// import {ref,isRef,unref} from "vue"
// let name = ref("");

import { customRef,onRenderTracked,onRenderTriggered } from "vue"
let name = customRef((track,trigger)=>{
    let value = "";
    return {
        get(){
            track(); // 如果有人访问name,就执行track()
            return value
        },
        set(val){
            trigger(); // 如果有人修改name,就执行trigger
            value = val;
        }
    }
})
// 仅供在开发环境下,用于ref变量的调试
onRenderTracked((ev) => console.log("name被访问了", ev));
onRenderTriggered((ev) => console.log("name被修改了", ev));
</script>

<style lang="less" scoped>
</style>
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

# 5,toRef

作用:把一个 reactive对象中的某个属性变成 ref 变量。
语法:const x = toRef(reactive(obj), 'key') // x.value

// Demo.vue

<template>
  <h1>toRef的使用</h1>
  <h1 v-text="user.name"></h1>
</template>

<script setup>
import {ref,isRef,unref,reactive,toRef} from "vue"
let user = reactive({name:"malu",age:18});
setTimeout(() => {
    user.name = "码路"
}, 1000);

// 能不能把user中的name变成ref变量呢?
// 答:toRef

console.log(user.name);
console.log(isRef(user.name));
console.log("-------");
let name = toRef(user,"name");
console.log(name.value);
console.log(isRef(name));

</script>

<style lang="less" scoped>
</style>
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

# 6,toRefs

作用:把一个reactive响应式对象变成ref变量。
语法:const obj1 = toRefs(reactive(obj))
应用:在子组件中接收父组件传递过来的 props时,使用 toRefs把它变成响应式的。

// Demo.vue

<template>
  <h1>toRefs的使用</h1>
  <h2 v-text="name"></h2>
  <h2 v-text="age"></h2>
</template>

<script setup>
import {ref,isRef,unref,reactive,toRefs} from "vue"
let user = reactive({name:"malu",age:18});
let {name,age} = toRefs(user);
console.log(isRef(name));
console.log(isRef(age));
</script>

<style lang="less" scoped>
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 7,shallowRef

作用:对复杂层级的对象,只将其第一层变成 ref 响应。 (性能优化)
语法:const x = shallowRef({a:{b:{c:1}}, d:2}) 如此a、b、c、d变化都不会自动更新,需要借助 triggerRef 来强制更新。

// Demo.vue

<template>
  <h1>shallowRef的使用</h1>
  <h1 v-text="info1.a.b.c"></h1>
  <button @click="add1">自增</button>
  <hr />

  <h1 v-text="info2.a.b.c"></h1>
  <button @click="add2">自增</button>
</template>

<script setup>
import {shallowRef,ref,triggerRef} from "vue"

// let info1 = ref("wc");
// ref是定义基本数据数据的响应式
// reactive是定义引用数据类型的响应式
// 其实ref也可以定义,如果ref中写了一人引用数据类型,它的内部还是会调用reactive
let info1 = ref({a:{b:{c:3}}});
console.log(info1.value.a.b.c);
let add1 = ()=>{
    info1.value.a.b.c++
}
// -------------------

// {a:{b:{c:3}}} 整体替换的话是响应式的,对a,b,c修改并不是响应式的
let info2 = shallowRef({a:{b:{c:3}}});
console.log(info2.value.a.b.c);
let add2 = ()=>{
    // info2.value = {a:{b:{c:10000}}}
    // info2.value.a = {b:{c:10000}}
    // info2.value.a.b = {c:10000}
    // info2.value.a.b.c = 10000
    info2.value.a.b.c++

    triggerRef(info2); // 强制刷新页面
}


</script>
<style lang="less" scoped>
</style>
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

# 8,triggerRef

作用:强制更新一个 shallowRef对象的渲染。
语法:triggerRef(shallowRef对象)

参考上例

# 9,reactive

作用:定义响应式变量,一般用于定义引用数据类型。如果是基本数据类型,建议使用ref来定义。
语法:const info = reactive([] | {})

// Demo.vue

<template>
  <h1>reactive的使用</h1>
  <h1 v-text="user.a"></h1>
  <button @click="add">+1</button>
  <h1 v-text="user.b"></h1>
</template>

<script setup>
import {reactive} from "vue"
let obj = {a:1,b:2,c:3}
let user = reactive(obj);

let add = ()=>{
    user.a++
}

</script>
<style lang="less" scoped>
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 10,readonly

作用:把一个对象,变成只读的。
语法:const rs = readonly(ref对象 | reactive对象 | 普通对象)

// Demo.vue

<template>
  <h1>readonly的使用</h1>
  <h1 v-text="user2.a"></h1>
  <button @click="add">+1</button>
</template>


<script setup>
import {reactive,readonly,isReadonly,ref} from "vue"
let obj = {a:1,b:2}
let user = reactive(obj);

// readonly(ref对象 | reactive对象 | 普通对象)
// readonly它内部是拦截的set
let info = readonly({c:3,d:4}); // info叫readonly对象
let user2 = readonly(user)
let foo = ref(100);
let foo2 = readonly(foo)
let xx = 666;

let add = ()=>{
    // key "a" failed: target is readonly. 
    // a是只读,只能使用,不能修改
    user2.a++; // 不能
}

console.log(isReadonly(info)); // true
console.log(isReadonly(user2)); // true
console.log(isReadonly(foo2)); // true
console.log("---------");
console.log(isReadonly(foo));
console.log(isReadonly(user));
console.log(isReadonly(xx));
</script>
<style lang="less" scoped>
</style>
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

# 11,isReadonly

作用: 判断一个变量是不是只读的。
语法:const bol = isReadonly(变量)

参考上例

# 12,isReactive

作用:判断一变量是不是 reactive的。
注意:被 readonly代理过的 reactive变量,调用 isReactive 也是返回 true的。

// Demo.vue

<template>
  <h1>isReactive的使用</h1>
  <h1 v-text="user2.a"></h1>
  <button @click="add">+1</button>
</template>

<script setup>
import { reactive, readonly, ref, isReactive } from "vue";

const uu = { a: 1, b: 2 };
const user = reactive(uu);

const info = readonly({ c: 3, d: 4 }); // 是Readonly
const user2 = readonly(user); // 是Readonly
const foo = ref(100);  // foo是一个ref,不是reactive
const foo2 = readonly(foo); // 是Readonly

const xx = 100;

const add = () => {
  user2.a++;
};
// 如果readonly包了一个reactive,这个readonly也是reactive
// 如果readonly包了一个ref,这个readonly不是reactive
console.log(isReactive(user2));
console.log(isReactive(user));
console.log(isReactive(foo2));
console.log(isReactive(foo));
console.log(isReactive(info));
console.log(isReactive(xx));

</script>
<style lang="less" scoped>
</style>
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

# 13,isProxy

作用:判断一个变量是不是 readonly 或 reactive的。

// Demo.vue

<template>
  <h1>isProxy的使用</h1>
  <h1 v-text="user2.a"></h1>
  <button @click="add">+1</button>
</template>

<script setup>
import { reactive, readonly, ref, isReactive,isProxy } from "vue";

const uu = { a: 1, b: 2 };
const user = reactive(uu);

const info = readonly({ c: 3, d: 4 }); // 是Readonly
const user2 = readonly(user); // 是Readonly
const foo = ref(100);  // foo是一个ref,不是reactive
const foo2 = readonly(foo); // 是Readonly

const xx = 100;

const add = () => {
  user2.a++;
};

// isProxy判断一个变量是否是readonly或reactive
console.log(isProxy(uu));
console.log(isProxy(user));
console.log(isProxy(info));
console.log(isProxy(user2));
console.log(isProxy(foo));
console.log(isProxy(foo2));
console.log(isProxy(xx));

</script>
<style lang="less" scoped>
</style>
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

# 14,toRaw

作用:得到返回 reactive变量或 readonly变量的"原始对象"。
语法::const raw = toRaw(reactive变量或readonly变量)
说明:reactive(obj)、readonly(obj) 和 obj 之间是一种代理关系,并且它们之间是一种浅拷贝的关系。obj 变化,会导致reactive(obj) 同步变化,反之一样。

// Demo.vue

<template>
  <h1>toRaw的使用</h1>
</template>

<script setup>
import { reactive, toRaw, readonly } from "vue";

const uu = { a: 1, b: 2 };  // 原始对象
const user = reactive(uu);  // 把uu变成了reactive

const info = readonly(user);  // 把reactive变成readonly

// toRaw(user) 把一个reactive打回原形  变成原始对象
// 它们之间的转化是浅copy
// toRaw(user)  和  uu 始终是同一个对象
console.log(toRaw(user) === uu);  // true
// reactive对象和原始对象比较:false
console.log(user === uu);

console.log("-----------");

// toRaw(info) 把一个readmonly打回原形 还是得原始对象
console.log(toRaw(info) === uu);
</script>
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

# 15,markRaw

作用:把一个普通对象标记成"永久原始",从此将无法再变成proxy了。
语法:const raw = markRaw({a, b})

// Demo.vue

<template>
  <h1>markRaw的使用</h1>
  <h2>{{ yy2.a }}</h2>
  <button @click="add">+1</button>
</template>

<script setup>
import { reactive, markRaw, isReactive } from "vue";

// { a: 1, b: 2 } 就是原始对象
// markRaw({ a: 1, b: 2 }) 把原始对象标记为不可代理
// 不能是raactive或readonly
const user = markRaw({ a: 1, b: 2 });

// yy2不是reactive  因为user被标记为永久原始
const yy2 = reactive(user);
console.log(isReactive(yy2));

// 不是一个reactive,不是响应式的
const add = () => {
  yy2.a++;
};
</script>
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

# 16,shallowReactive

作用:定义一个reactive变量,只对它的第一层进行Proxy, 所以只有第一层变化时视图才更新。
语法:const obj = shallowReactive({a:{b:9}})

// Demo.vue

<template>
  <h1>shallowReactive的使用</h1>
  <h2>{{ aa }}</h2>
  <button @click="change">change</button>
</template>

<script setup>
import { shallowReactive } from "vue";

// { a: { b: { c: 1 } } }   利用shallowReactive,只能对第1层代理
// 所谓的第1层就是a这个属性
let aa = shallowReactive({ a: { b: { c: 1 } } });
const change = () => {
    // aa = {d:{e:{f:1000}}}
    // aa.a = { b: { c: 2 } };
    // aa.a.b = {c:110}
    // aa.a.b.c = 110
    aa.a.b.c++
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 17,shallowReadonly

作用:定义一个reactive变量,只有第一层是只读的。
语法:const obj = shallowReadonly({a:{b:9}})

// Demo.vue

<template>
  <h1>shallowReadonly的使用</h1>
  <h2>{{ mm }}</h2>
  <button @click="change">change</button>
</template>

<script setup>
import { shallowReadonly, reactive } from "vue";

// shallowReadonly只有第1层是只读的
// 第一层:a  d
// 第二层:b
// 第三层:c
const mm = shallowReadonly(reactive({ a: { b: { c: 1 } }, d: 2 }));
const change = () => {
    // mm.d++  // 不能改
    mm.a.b.c++; // 可以改
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 18,computed

作用:对响应式变量进行缓存计算。
语法:const c = computed(fn / {get, set})

// Demo.vue

<template>
  <h1>computed的使用</h1>
  <h1 v-text="num"></h1>
  <button @click="num++">自增</button>
  <h2>{{ total }}</h2>
  <input type="text" v-model.number="total" />
</template>

<script setup>
import { ref, computed } from "vue";
let num = ref(100)

// let total = computed({
//   get() {
//     return num.value * 100
//   },
//   set(val) {
//     num.value = val / 100
//   }
// })

let total = computed(() => {
  return 666
})
</script>
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

# 19,watch

作用:用于监听响应式变量的变化,组件初始化时,它不执行。
语法:const stop = watch(x, (new, old)=>{}),调用stop() 可以停止监听。
语法:const stop = watch([x, y], ([newX, newY], [oldX, oldY])=>{}),调用stop()可以停止监听。

// Demo.vue

<template>
  <h1>watch的使用</h1>
  <h1 v-text="num"></h1>
  <button @click="num++">自增</button>
  <h2>{{ total }}</h2>
  <input type="text" v-model.number="total" />
  <button @click="stopAll">停止一切监听</button>
</template>

<script setup>
import { ref, computed, watch } from "vue";
let num = ref(100)
const total = computed({
  get() {
    return num.value * 100;
  },
  set(val) {
    num.value = val / 100;
  },
});

// ------------------------------------
let stop1 = watch(num, (newNum, oldNum) => {
  console.log("num变化了:", newNum, oldNum);
})

// ------------------------------------
let stop2 = watch([num, total], ([newNum, newTotal], [oldNum, oldTotal]) => {
  console.log("num变化了:", newNum, oldNum);
  console.log("total变化了:", newTotal, oldTotal);
})
// ------------------------------------
let stopAll = () => {
  stop1();
  stop2();
}
</script>
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

# 20,watchEffect

作用:相当于是 react中的 useEffect(),用于执行各种副作用。
语法

  • const stop = watchEffect(fn),默认其 flush:'pre',前置执行的副作用。
  • watchPostEffect,等价于 watchEffect(fn, {flush:'post'}),后置执行的副作用。
  • watchSyncEffect,等价于 watchEffect(fn, {flush:'sync'}),同步执行的副作用。
  • 特点:watchEffect 会自动收集其内部响应式依赖,当响应式依赖发变化时,这个watchEffect将再次执行,直到你手动 stop() 掉它。
// Demo.vue

<template>
  <h1>watchEffect的使用</h1>
  <h1 v-text="num"></h1>
  <button @click="num++">自增</button>
  <hr />
  <h1 v-text="foo"></h1>
  <button @click="foo--">自减</button>
  <hr />
  <h1 v-text="bar.a"></h1>
  <button @click="bar.a++">改变</button>
</template>

<script setup>
import { ref, computed, watchEffect, watchPostEffect, reactive } from "vue";
let num = ref(1)
let foo = ref(200)
let bar = reactive({ a: 1, b: 2 })

// watchEffect 处理各种副作用  类似于生命周期函数
// 下面的代码,一开始只会执行一次,后面不会执行
// watchEffect(() => {
//   console.log("执行副作用1");
// })

// 当num数据发生了变化,需要重新执行,你需要在下面的代码使用num
// watchEffect依赖了num数据,num数据变化,就会重新执行
// watchEffect(() => {
//   console.log("执行副作用1",num.value);
// })

// 只能foo数据变化,才会重新执行
// watchEffect(() => {
//   console.log("执行副作用1", foo.value);
// })

// watchEffect(() => {
//   console.log("执行副作用1", foo.value,num.value);
// })

// --------------------------------------------

// watchEffect(() => {  // pre先执行
//   console.log("执行副作用1", foo.value, num.value);
// }, { flush: "pre" })

// watchEffect(() => {  // post表示后执行
//   console.log("执行副作用2", foo.value, num.value);
// }, { flush: "post" })


// --------------------------------------------

// // 这样写等价与  { flush: "pre" })
// watchEffect(() => {  // pre先执行
//   console.log("执行副作用1", foo.value, num.value);
// })

// // 这样写等价与  { flush: "post" })
// watchPostEffect(() => {  // post表示后执行
//   console.log("执行副作用2", foo.value, num.value);
// })


// --------------------------------------------

// 1  3  2
// watchEffect(() => {  // pre先执行
//   console.log("执行副作用1", foo.value, num.value);
// }, { flush: "pre" })

// watchEffect(() => {  // post表示后执行
//   console.log("执行副作用2", foo.value, num.value);
// }, { flush: "post" })

// // sync 同步执行,轮到它的时候就执行
// watchEffect(() => {  // post表示后执行
//   console.log("执行副作用3", foo.value, num.value);
// }, { flush: "sync" });

// --------------------------------------------

//  3  1  2

// sync 同步执行,轮到它的时候就执行
// 等价于:watchSyncEffect
watchEffect(() => {  // post表示后执行
  console.log("执行副作用3", foo.value, num.value);
}, { flush: "sync" });


watchEffect(() => {  // pre先执行
  console.log("执行副作用1", foo.value, num.value);
}, { flush: "pre" })

watchEffect(() => {  // post表示后执行
  console.log("执行副作用2", foo.value, num.value);
}, { flush: "post" })
</script>
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

# 21,生命周期钩子

  • 选项式的 beforeCreate、created,被setup替代了。setup表示组件被创建之前、props被解析之后执行,它是组合式 API 的入口。
  • 选项式的 beforeDestroy、destroyed 被更名为 beforeUnmount、unmounted。
  • 新增了两个选项式的生命周期 renderTracked、renderTriggered,它们只在开发环境有用,常用于调试。
  • 在使用 setup组合时,不建议使用选项式的生命周期,建议使用 on* 系列 hooks生命周期。
// Demo.vue

<template>
  <h1 v-text="num"></h1>
  <button @click="num++">自增</button>
</template>

<script setup>
import {
  ref,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  onRenderTracked,
  onRenderTriggered,
  onActivated,
  onDeactivated,
  onErrorCaptured,
} from "vue";

console.log("---setup");
const num = ref(100);
// 挂载阶段
onBeforeMount(() => console.log("---开始挂载"));
onRenderTracked(() => console.log("---跟踪"));
onMounted(() => console.log("---挂载完成"));

// 更新阶段
onRenderTriggered(() => console.log("---触发"));
onBeforeUpdate(() => console.log("---开始更新"));
onUpdated(() => console.log("---更新完成"));

// 销毁阶段
onBeforeUnmount(() => console.log("---开始销毁"));
onUnmounted(() => console.log("---销毁完成"));

// 与动态组件有关
onActivated(() => console.log("---激活"));
onDeactivated(() => console.log("---休眠"));

// 异常捕获
onErrorCaptured(() => console.log("---错误捕获"));
</script>
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

# 22,provide / inject

作用:在组件树中自上而下地传递数据.
语法:provide('key', value)
语法:const value = inject('key', '默认值')

// App.vue

<template><Demo /></template>

<script setup>
import Demo from "./components/Demo.vue";

import { ref, provide } from "vue";
const msg = ref("Hello World");
// 向组件树中注入数据
provide("msg", msg);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
// Demo.vue

<template>
  <h1>provide / inject的使用</h1>
  <h1 v-text="msg"></h1>
</template>
<script setup>
import { inject } from "vue";
// 消费组件树中的数据,第二参数为默认值
// inject表示注入,消费者
// "Hello Vue" 是默认值
const msg = inject("msg", "Hello Vue");
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 23,getCurrentInstance

作用:用于访问内部组件实例。请不要把它当作在组合式 API 中获取 this 的替代方案来使用。
语法:const app = getCurrentInstance()
场景:常用于访问 app.config.globalProperties 上的全局数据。

// main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

let app = createApp(App)
// Vue2.x
// Vue.prototype.$http = () => { }

// Vue3.x
app.config.globalProperties.$http = () => { }

app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
// Demo.vue

<template>
  <h1>getCurrentInstance的使用</h1>
</template>
<script setup>
import { getCurrentInstance } from "vue";
const app = getCurrentInstance();
// 全局数据,是不具备响应式的。
const global = app.appContext.config.globalProperties;
console.log("app", app);
console.log("全局数据", global);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 24,组合编程最佳范式

最佳实践:

  • 只使用 setup 及组合API,不要再使用vue选项了。
  • 有必要封装 hooks时,建议把功能封装成hooks,以便于代码的可维护性。
  • 能用 vite就尽量使用vite,能用ts 就尽量使用ts。

总结常用API如下

  • ref
  • reactive
  • watch
  • computed
  • watchEffect
  • toRefs
  • onMounted
  • onBeforeUnmount
  • onActivated
  • onDeactivated
  • 其它的组合API都是面试题
  • provide / inject 用setup语法糖组合时是有响应式的,如果用选项式写法是没有响应式的(需要使用computed包裹一层)

# 25,todomvc

参考:https://www.cnblogs.com/OwenLin/p/14062857.html

静态结构如下:

<template>
  <div class="todo-list">
    <h1>todos</h1>
    <div class="todo-list-content">
      <section class="content-input-box">
        <input class="todo-input" type="text" autofocus autocomplete="off" placeholder="What needs to be done?" />
      </section>
      <input type="checkbox" class="toggle-all" :class="{ 'toggle-all-active': true }" />
      <ul class="content">
        <li class="list-item">
          <div class="list-item-box">
            <input class="checkbox" type="checkbox" />
            <div>
              学习vue3
            </div>
            <span v-show="" class="delete-icon">X</span>
          </div>
          <input v-show="false" class="edit-input" type="text" />
        </li>
      </ul>
      <section class="footer">
        <span>1 items left</span>
        <div class="status-buttons">
          <button></button>
        </div>
        <p class="clear-button">
          Clear completed
        </p>
      </section>
    </div>
  </div>
</template>

<style>
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

input {
  outline: none;
}

ul,
li,
ol {
  list-style: none;
}

::-webkit-input-placeholder {
  color: #d5d5d5;
  font-size: 25px;
}

.todo-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #f5f5f5;
  width: 100%;
  height: 500px;
}

h1 {
  margin: 10px;
  font-size: 100px;
  color: rgba(175, 47, 47, 0.15);
}

/* content部分样式 */
.todo-list .todo-list-content {
  position: relative;
  width: 600px;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}

.todo-list-content .content-input-box {
  display: flex;
  align-items: center;
  box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
}

.toggle-all {
  position: absolute;
  left: 42px;
  top: 27px;
  width: 0px;
  height: 0px;
  transform: rotate(90deg);
  cursor: pointer;
}

.toggle-all:before {
  content: "❯";
  font-size: 22px;
  color: #e6e6e6;
}

.toggle-all-active:before {
  color: #737373;
}

.todo-list-content .todo-input {
  font-size: 24px;
  width: 100%;
  padding: 16px 16px 16px 60px;
  border: 1px solid transparent;
  background: rgba(0, 0, 0, 0.003);
}

.content .list-item {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 24px;
  border-bottom: 1px solid #ececec;
}

.list-item .edit-input {
  width: 100%;
  padding: 16px;
  margin-left: 42px;
  font-size: 24px;
  box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
  border: 1px solid #999;
}

.list-item .list-item-box {
  display: flex;
  flex-direction: row;
  align-items: center;
  width: 100%;
  padding: 16px;
}

.list-item .checkbox {
  cursor: pointer;
  width: 20px;
  height: 20px;
}

.list-item .text {
  margin-left: 30px;
  width: 100%;
  text-align: left;
}

.list-item .delete-icon {
  color: red;
  cursor: pointer;
}

.complete {
  color: #d9d9d9;
  text-decoration: line-through;
}

/* footer部分样式 */
.footer {
  padding: 12px 15px;
  display: flex;
  justify-content: space-between;
}

.footer .status-buttons {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

.footer .status-buttons button {
  padding: 2px 8px;
  margin-left: 5px;
}

.footer .clear-button {
  cursor: pointer;
}

.active-status-button {
  background-color: #777;
  outline: -webkit-focus-ring-color auto 1px;
}
</style>
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
Last Updated: 12/25/2022, 10:02:14 PM