vue3

vue3

1、主函数 main.ts

1
2
3
4
5
6
7
// 程序的主入口文件,ts文件
// 引入 createApp 函数,创建对应的应用,产生应用的实例对象
import { createApp } from 'vue'
// 引入 App 组件(所有组件的父组件
import App from './App.vue'
// 创建 App 应用返回对应的实例对象,调用 mount 方法进行挂载
createApp(App).mount('#app')

2、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
<template>
<!-- Vue2 中的 html 模板中必须有一对根标签,Vue3组件的模板中可以没有根标签-->
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
</template>

<script lang="ts">
// 这里可以使用 ts 的代码
// defineComponent 函数,目的是定义一个组件,内部可以传入一个配置对象
import { defineComponent } from 'vue';
// 引入一个子级组件
import HelloWorld from './components/HelloWorld.vue';

// 暴露出去一个定义好的组件
export default defineComponent({
// 组件名
name: 'App',
// 注册组件
components: {
// 注册一个子级组件
HelloWorld
}
});
</script>

<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

3、Composition API(常用部分)

3.1、setup

  • 新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
  • 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用

3.2、ref

  • 作用: 定义一个数据的响应式
  • 语法: const xxx = ref(initValue):
    • 创建一个包含响应式数据的引用(reference)对象
    • js中操作数据: xxx.value
    • 模板中操作数据: 不需要.value
  • 一般用来定义一个基本类型的响应式数据
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
<template>
<div>hello,world</div>
<h1>{{number}}</h1>
<button @click="add">递增</button>
</template>

<script lang="ts">
// 这里可以使用 ts 的代码
// defineComponent 函数,目的是定义一个组件,内部可以传入一个配置对象
import {defineComponent,ref} from 'vue';

// 暴露出去一个定义好的组件
export default defineComponent({
// 组件名
name: 'App',
setup(){
// ref 是一个函数,作用:定义一个响应式数据,返回一个ref对象,对象中有一个 value 属性,
// 如果需要对数据进行操作,需要使用 ref 对象调用 value 属性的方式进行数据的操作
// 一般用来定义一个基本类型的响应式数据
const number = ref(0)
console.log('hello,world')
function add(){
number.value++
}
return{
number,
add
}
}
});
</script>

3.3、reactive

  • 作用: 定义多个数据的响应式
  • const proxy = reactive(obj): 接收一个普通对象然后返回该普通对象的响应式代理对象
  • 响应式转换是“深层的”:会影响对象内部所有嵌套的属性
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
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
<template>
<h1>姓名:{{user.name}}</h1>
<h1>年龄:{{user.age}}</h1>
<h1>钱:{{user.assets.money}}</h1>

<div v-for="(item,index) in user.assets.fixedAssets" :key="index">
<h2 >不动产{{index}}:{{ item }}</h2>
</div>

<button @click="updateUser">人物信息</button>
</template>

<script lang="ts">
// 这里可以使用 ts 的代码
// defineComponent 函数,目的是定义一个组件,内部可以传入一个配置对象
import {defineComponent,ref,reactive} from 'vue';

// 暴露出去一个定义好的组件
export default defineComponent({
// 组件名
name: 'App',
setup(){
const obj = {
name:'hqz',
age:22,
assets:{
money:100,
fixedAssets:['computer','car']
}
}

const user = reactive(obj)

function updateUser(){
user.name += '++'
user.age += 1
user.assets.money += 900
user.assets.fixedAssets.push('mouse')
}

return{
user,
updateUser
}
}
});
</script>

3.4、Vue3响应式核心

  • 通过Proxy(代理): 拦截对data任意属性的任意(13种)操作, 包括属性值的读写, 属性的添加, 属性的删除等…
  • 通过 Reflect(反射): 动态对被代理对象的相应属性进行特定的操作
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Proxy 与 Reflect</title>
</head>
<body>
<script>

const user = {
name: "John",
age: 12
};

/*
proxyUser是代理对象, user是被代理对象
后面所有的操作都是通过代理对象来操作被代理对象内部属性
*/
const proxyUser = new Proxy(user, {

get(target, prop) {
console.log('劫持get()', prop)
return Reflect.get(target, prop)
},

set(target, prop, val) {
console.log('劫持set()', prop, val)
return Reflect.set(target, prop, val); // (2)
},

deleteProperty (target, prop) {
console.log('劫持delete属性', prop)
return Reflect.deleteProperty(target, prop)
}
});
// 读取属性值
console.log(proxyUser===user)
console.log(proxyUser.name, proxyUser.age)
// 设置属性值
proxyUser.name = 'bob'
proxyUser.age = 13
console.log(user)
// 添加属性
proxyUser.sex = '男'
console.log(user)
// 删除属性
delete proxyUser.sex
console.log(user)
</script>
</body>
</html>

3.5、setup 执行时机

  • setup执行的时机
    • 在beforeCreate之前执行(一次), 此时组件对象还没有创建
    • this是undefined, 不能通过this来访问data/computed/methods / props
    • 其实所有的composition API相关回调函数中也都不可以
  • setup的返回值
    • 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
    • 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
    • 返回对象中的方法会与methods中的方法合并成功组件对象的方法
    • 如果有重名, setup优先
    • 注意:
    • 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods
    • setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据
  • setup的参数
    • setup(props, context) / setup(props, {attrs, slots, emit})
    • props: 包含props配置声明且传入了的所有属性的对象
    • attrs: 包含没有在props配置中声明的属性的对象, 相当于 this.$attrs
    • slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
    • emit: 用来分发自定义事件的函数, 相当于 this.$emit

3.6、reactive与ref-细节

  • 是Vue3的 composition API中2个最重要的响应式API
  • ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
  • 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
  • ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
  • reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
  • ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)

3.7、计算属性与监视

  • computed函数:
    • 与computed配置功能一致
    • 只有getter(函数中传入一个回调函数)
    • 有getter和setter(函数中传入一个对象,重写 set 和 get 方法)
  • watch函数
    • 与watch配置功能一致
    • 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
    • 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
    • 通过配置deep为true, 来指定深度监视
  • watchEffect函数
    • 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
    • 默认初始时就会执行第一次, 从而可以收集需要监视的数据
    • 监视数据发生变化时回调
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
<template>
<h2>App</h2>
fistName: <input v-model="user.firstName"/><br>
lastName: <input v-model="user.lastName"/><br>
fullName1: <input v-model="fullName1"/><br>
fullName2: <input v-model="fullName2"><br>
fullName3: <input v-model="fullName3"><br>

</template>

<script lang="ts">

import {
reactive,
ref,
computed,
watch,
watchEffect
} from 'vue'

export default {

setup () {
const user = reactive({
firstName: 'A',
lastName: 'B'
})

// 只有getter的计算属性
const fullName1 = computed(() => {
console.log('fullName1')
return user.firstName + '-' + user.lastName
})

// 有getter与setter的计算属性
const fullName2 = computed({
get () {
console.log('fullName2 get')
return user.firstName + '-' + user.lastName
},

set (value: string) {
console.log('fullName2 set')
const names = value.split('-')
user.firstName = names[0]
user.lastName = names[1]
}
})

const fullName3 = ref('')

/*
watchEffect: 监视所有回调中使用的数据
*/
/*
watchEffect(() => {
console.log('watchEffect')
fullName3.value = user.firstName + '-' + user.lastName
})
*/

/*
使用watch的2个特性:
深度监视
初始化立即执行
*/
watch(user, () => {
fullName3.value = user.firstName + '-' + user.lastName
}, {
immediate: true, // 是否初始化立即执行一次, 默认是false
deep: true, // 是否是深度监视, 默认是false
})

/*
watch一个数据
默认在数据发生改变时执行回调
*/
watch(fullName3, (value) => {
console.log('watch')
const names = value.split('-')
user.firstName = names[0]
user.lastName = names[1]
})

/*
watch多个数据:
使用数组来指定
如果是ref对象, 直接指定
如果是reactive对象中的属性, 必须通过函数来指定
*/
watch([() => user.firstName, () => user.lastName, fullName3], (values) => {
console.log('监视多个数据', values)
})

return {
user,
fullName1,
fullName2,
fullName3
}
}
}
</script>

3.8、生命周期

与 2.x 版本生命周期相对应的组合式 API

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

新增的钩子函数

组合式 API 还提供了以下调试钩子函数:

  • onRenderTracked
  • onRenderTriggered

3.9、自定义hook函数

  • 使用Vue3的组合API封装的可复用的功能函数

  • 自定义hook的作用类似于vue2中的mixin技术

  • 自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂

  • 需求1: 收集用户鼠标点击的页面坐标

    hooks/useMousePosition.ts

    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
    import { ref, onMounted, onUnmounted } from 'vue'
    /*
    收集用户鼠标点击的页面坐标
    */
    export default function useMousePosition () {
    // 初始化坐标数据
    const x = ref(-1)
    const y = ref(-1)

    // 用于收集点击事件坐标的函数
    const updatePosition = (e: MouseEvent) => {
    x.value = e.pageX
    y.value = e.pageY
    }

    // 挂载后绑定点击监听
    onMounted(() => {
    document.addEventListener('click', updatePosition)
    })

    // 卸载前解绑点击监听
    onUnmounted(() => {
    document.removeEventListener('click', updatePosition)
    })

    return {x, y}
    }
    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
    <template>
    <div>
    <h2>x: {{x}}, y: {{y}}</h2>
    </div>
    </template>

    <script>

    import {
    ref
    } from "vue"
    /*
    在组件中引入并使用自定义hook
    自定义hook的作用类似于vue2中的mixin技术
    自定义Hook的优势: 很清楚复用功能代码的来源, 更清楚易懂
    */
    import useMousePosition from './hooks/useMousePosition'

    export default {
    setup() {

    const {x, y} = useMousePosition()

    return {
    x,
    y,
    }
    }
    }
    </script>
  • 利用TS泛型强化类型检查

  • 需求2: 封装发ajax请求的hook函数

    hooks/useRequest.ts

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
import { ref } from 'vue'
import axios from 'axios'

/*
使用axios发送异步ajax请求
*/
export default function useUrlLoader<T>(url: string) {
// 加上泛型
const result = ref<T | null>(null)
const loading = ref(true)
const errorMsg = ref(null)

axios.get(url)
.then(response => {
loading.value = false
result.value = response.data
})
.catch(e => {
loading.value = false
errorMsg.value = e.message || '未知错误'
})

return {
loading,
result,
errorMsg,
}
}
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
<template>
<div class="about">
<h2 v-if="loading">LOADING...</h2>
<h2 v-else-if="errorMsg">{{errorMsg}}</h2>
<!-- <ul v-else>
<li>id: {{result.id}}</li>
<li>name: {{result.name}}</li>
<li>distance: {{result.distance}}</li>
</ul> -->

<ul v-for="p in result" :key="p.id">
<li>id: {{p.id}}</li>
<li>title: {{p.title}}</li>
<li>price: {{p.price}}</li>
</ul>
<!-- <img v-if="result" :src="result[0].url" alt=""> -->
</div>
</template>

<script lang="ts">
import {
watch
} from "vue"
import useRequest from './hooks/useRequest'

// 地址数据接口
interface AddressResult {
id: number;
name: string;
distance: string;
}

// 产品数据接口
interface ProductResult {
id: string;
title: string;
price: number;
}

export default {
setup() {

// const {loading, result, errorMsg} = useRequest<AddressResult>('/data/address.json')
const {loading, result, errorMsg} = useRequest<ProductResult[]>('/data/products.json')

watch(result, () => {
if (result.value) {
console.log(result.value.length) // 有提示
}
})

return {
loading,
result,
errorMsg
}
}
}
</script>

3.10、toRefs

把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref

应用: 当从合成函数返回响应式对象时,toRefs 非常有用,这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用

问题: reactive 对象取出的所有属性值都是非响应式的

解决: 利用 toRefs 可以将一个响应式 reactive 对象的所有原始属性转换为响应式的 ref 属性

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
<template>
<h3>foo: {{foo}}</h3>
<h3>bar: {{bar}}</h3>
</template>

<script lang="ts">
import { reactive, toRefs } from 'vue'
/*
toRefs:
将响应式对象中所有属性包装为ref对象, 并返回包含这些ref对象的普通对象
应用: 当从合成函数返回响应式对象时,toRefs 非常有用,
这样消费组件就可以在不丢失响应式的情况下对返回的对象进行分解使用
*/
export default {

setup () {

const state = reactive({
foo: 'a',
bar: 'b',
})

const stateAsRefs = toRefs(state)

setTimeout(() => {
state.foo += '++'
state.bar += '++'
}, 2000);

return {
...stateAsRefs,
}
},
}
</script>

3.11、ref获取元素

利用ref函数获取组件中的标签元素

功能需求: 让输入框自动获取焦点

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
<template>
<h2>App</h2>
<input type="text">---
<input type="text" ref="inputRef">
</template>

<script lang="ts">
import { onMounted, ref } from 'vue'
/*
ref获取元素: 利用ref函数获取组件中的标签元素
功能需求: 让输入框自动获取焦点
*/
export default {
setup() {
const inputRef = ref<HTMLElement|null>(null)

onMounted(() => {
inputRef.value && inputRef.value.focus()
})

return {
inputRef
}
},
}
</script>

4、 Composition API(其它部分)

4.1、 shallowReactive 与 shallowRef

  • shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
  • shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
  • 什么时候用浅响应式呢?
    • 一般情况下使用ref和reactive即可
    • 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
    • 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef

4.2、 readonly 与 shallowReadonly

  • readonly:
    • 深度只读数据
    • 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理。
    • 只读代理是深层的:访问的任何嵌套 property 也是只读的。
  • shallowReadonly
    • 浅只读数据
    • 创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
  • 应用场景:
    • 在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除

4.3、 toRaw 与 markRaw

  • toRaw
    • 返回由 reactivereadonly 方法转换成响应式代理的普通对象。
    • 这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新。
  • markRaw
    • 标记一个对象,使其永远不会转换为代理。返回对象本身
    • 应用场景:
      • 有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象。
      • 当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能。

4.4、toRef

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

4.5、customRef

  • 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
  • 需求: 使用 customRef 实现 debounce 的示例
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
<template>
<h2>App</h2>
<input v-model="keyword" placeholder="搜索关键字"/>
<p>{{keyword}}</p>
</template>

<script lang="ts">

import {
ref,
customRef
} from 'vue'

export default {

setup () {
const keyword = useDebouncedRef('', 500)
console.log(keyword)
return {
keyword
}
},
}

/*
实现函数防抖的自定义ref
*/
function useDebouncedRef<T>(value: T, delay = 200) {
let timeout: number
// 自定义响应时间
return customRef((track, trigger) => {
return {
get() {
// 告诉Vue追踪数据
track()
return value
},
set(newValue: T) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
// 告诉Vue去触发界面更新
trigger()
}, delay)
}
}
})
}
</script>

4.6、provide 与 inject

  • provideinject提供依赖注入,功能类似 2.x 的provide/inject
  • 实现跨层级组件(祖孙)间通信
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
<template>
<h1>父组件</h1>
<p>当前颜色: {{color}}</p>
<button @click="color='red'">红</button>
<button @click="color='yellow'">黄</button>
<button @click="color='blue'">蓝</button>

<hr>
<Son />
</template>

<script lang="ts">
import { provide, ref } from 'vue'
/*
- provide` 和 `inject` 提供依赖注入,功能类似 2.x 的 `provide/inject
- 实现跨层级组件(祖孙)间通信
*/

import Son from './Son.vue'
export default {
name: 'ProvideInject',
components: {
Son
},
setup() {

const color = ref('red')

provide('color', color)

return {
color
}
}
}
</script>

<template>
<div>
<h2>子组件</h2>
<hr>
<GrandSon />
</div>
</template>

<script lang="ts">
import GrandSon from './GrandSon.vue'
export default {
components: {
GrandSon
},
}
</script>

<template>
<h3 :style="{color}">孙子组件: {{color}}</h3>

</template>

<script lang="ts">
import { inject } from 'vue'
export default {
setup() {
const color = inject('color')

return {
color
}
}
}
</script>

4.7、响应式数据的判断

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

5、其它新组件和api

5.1、Fragment(片断)

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用
1
2
3
4
<template>
<h2>aaaa</h2>
<h2>aaaa</h2>
</template>
  • Teleport 提供了一种干净的方法, 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示

ModalButton.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
<template>
<button @click="modalOpen = true">
Open full screen modal! (With teleport!)
</button>

<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
I'm a teleported modal!
(My parent is "body")
<button @click="modalOpen = false">
Close
</button>
</div>
</div>
</teleport>
</template>

<script>
import { ref } from 'vue'
export default {
name: 'modal-button',
setup () {
const modalOpen = ref(false)
return {
modalOpen
}
}
}
</script>


<style>
.modal {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
background-color: rgba(0,0,0,.5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.modal div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: white;
width: 300px;
height: 300px;
padding: 5px;
}
</style>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<h2>App</h2>
<modal-button></modal-button>
</template>

<script lang="ts">
import ModalButton from './ModalButton.vue'

export default {
setup() {
return {
}
},

components: {
ModalButton
}
}
</script>

6、Suspense(不确定的)

  • 它们允许我们的应用程序在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验
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>
<Suspense>
<template v-slot:default>
<AsyncComp/>
<!-- <AsyncAddress/> -->
</template>

<template v-slot:fallback>
<h1>LOADING...</h1>
</template>
</Suspense>
</template>

<script lang="ts">
/*
异步组件 + Suspense组件
*/
// import AsyncComp from './AsyncComp.vue'
import AsyncAddress from './AsyncAddress.vue'
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./AsyncComp.vue'))
export default {
setup() {
return {

}
},

components: {
AsyncComp,
AsyncAddress
}
}
</script>
  • AsyncComp.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<h2>AsyncComp22</h2>
<p>{{msg}}</p>
</template>

<script lang="ts">

export default {
name: 'AsyncComp',
setup () {
// return new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve({
// msg: 'abc'
// })
// }, 2000)
// })
return {
msg: 'abc'
}
}
}
</script>
  • AsyncAddress.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<h2>{{data}}</h2>
</template>

<script lang="ts">
import axios from 'axios'
export default {
async setup() {
const result = await axios.get('/data/address.json')
return {
data: result.data
}
}
}
</script>
  • Copyrights © 2022-2023 hqz

请我喝杯咖啡吧~

支付宝
微信