vue3新特性

总结一下vue3.0带来的部分新特性

  1. setup()
  2. ref()
  3. reactive()
  4. isRef()
  5. toRefs()
  6. computed()
  7. watch()
  8. LifeCycle Hooks(新的生命周期)
  9. Template refs
  10. globalProperties
  11. Suspense

vue2与vue3的对比

  • 对TypeScript支持不友好(所有属性都放在了this对象上,难以推到组件的数据类型)
  • 大量的Api挂载在Vue对象的原型上,难以实现TreeShaking
  • 架构层面对跨平台dom渲染开发支持不友好
  • Composition Api 受ReactHock启发
  • 更方便的支持了jsx
  • vue3的Template支持多个根标签, vue2不支持
  • 对虚拟DOM进行了重写,对模板的编译进行了优化操作

一、setup函数

setup()函数是vue3中,专门为为组件提供的新属性。它为我们使用vue3的Composition Api新特性提供了统一的入口,setup函数会在beforecreate之后、create之前执行,vue3也是取消了这两个钩子,统一用setup代替,该函数相当于一个生命周期函数,vue中过去的data,methods,watch等全部都用对应的新增api写在setup()函数中

1
2
3
4
5
6
7
8
9
10
11
12
setup(props, context) {
context.attrs
context.slots
context.parent
context.root
comtext.emit
comtext.refs

return {

}
}
  • props: 用来接收props数据
  • context用来定义上下文,上下文对象中包含了一些有用的属性,这些属性在vue2.x中需要通过this才能访问到,在setup函数中无法访问到this,是个undefinded
  • 返回值:return{}, 返回响应式数据,模板中需要使用的函数

二、reactive函数

reactive()函数接收一个普通对象,返回一个响应式的数据对象,想要使用创建的响应式数据也很简单,创建出来之后,在setup()中return出去,直接在template中调用即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
{{ name }}
</template>
<script lang="tx">
import { defineComponent, reactive, ref, toRefs } from 'vue';
export default defineComponent({
setup(props, context) {
let state = reactive({
name: 'test'
});
return state
}
})
</script>

三、ref()函数

ref()函数用来根据给定的值创建一个响应式的数据对象,ref()函数调用的返回值是一个对象,这个对象只包含了一个value属性,只在setup()函数内部访问ref函数需要加.value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template class="main">
<div>
{{ count }} //10
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const count = ref<number>(10);
// 在js中获取ref中定义的值,需要通过value属性
console.log(count.value);
return {
count
}
}
})
</script>

在reactive对象中访问ref创建的创建的响应式数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div class="main">
{{ count }} -{{ t }} //10 -100
</div>
</template>
<script lang="ts">
import {defineComponent, reactive, ref, toRefs} from 'vue';
export default defineComponent({
setup() {
const count = ref<number>(10);
const obj = reactive({
t: 100,
count
})
// 通过reactive来获取ref的值时,不需要使用.valte属性
console.log(obj.count);
return {
...toRefs(obj)
}
}
})
</script>

四、isRef()函数

isRef()用来判断某个值是否为ref()创建出来的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script lang="ts">
import { defineComponent, isRef, ref } from 'vue';
export default defineComponent({
setup(props, context) {
let state = reactive({
name: 'test'
});
const age = ref(18);
return {
...toRefs(state),
age
}
}
})
</script>

五、toRefs()函数

toRefs()函数可以将reactive创建出来的响应式对象,转换为普通对象,只不过这个对象上的每一个节点,都是ref()类型的响应式数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div class="main">
{{ name }} // test
{{ age }} // 18
</div>
</template>
<script>
import { defineComponent, reactive, ref, toRefs } from 'vue';
export default defineComponent({
setup(props, context) {
let state = reactive({
name: 'test'
});
const age = ref(18);
return {
...toRefs(states),
age
}
}
})
</script>

六、computed()

该函数用来创造计算属性,和过去一样,它的返回值是一个ref对象。里面可以传方法,或者一个对象,对象中包含set()、get()方法

6.1 创建只读的计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<scripr lang="ts">
import {computed, defineComponent, ref} from 'vue';
expoet default defineComponent({
setup(props, context) {
const age = ref(8)

// 根据age的值,创建一个响应式的计算属性readOnlyAge,它会根据依赖的ref自动计算并返回一个新的ref

const readOnlyAge = computed(() => age.value++) // 19

return {
age,
readOnlyAge
}
}
})
</scripr>

6.2 通过set()、get()方法创建一个可读可写的计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
import {computed, defineComponent, ref} from 'vue';
export default defineComponent({
setup(props, context) {
const age = ref<number>(18)
const computedAge = computed({
get: () => age.value + 1,
set: () => age.value + value
})
// 为计算属性赋值的操作,会触发set函数,触发set函数后,age的值会被更新
age.value = 100;
return {
age,
computedAge
}
}
})
</script>

七、watch()函数

whatch函数用来监听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说再侦听的数据源变更时才执行回调。

7.1监听用reactive声明的数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
import { computed, definComponent, reactive, toRefs, watch } from 'vue';
export default defineComponent({
setup(props, context) {
const state = reactive<Person>({ name: 'vue', age: 10 });

watch(
() => state.age,
(age, preAge) => {
console.log(age); // 100
console.log(preAge); // 10
}
)
// 修改age时会触发watch的回调,打印变更前后的值
state.age = 10
return {
...toRefs(state)
}
}
})
</script>

7.2监听用ref声明的数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script lang="ts">
import { defineComponent, ref, watch } from 'vue';
interface Person {
name: string,
age: number
}
export default defineComponent({
setup(props, context) {
const age = ref<number>(10);

watch(age, () => console.log(age.value)); // 100

// 修改age时会触发watch的回调,打印变更后的值
age.value = 100
return {
age
}
}
})
</script>

7.3同时监听多个值

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
<script lang="ts">
import { watch, computed, defineComponent, reactive, toRefs } from 'vue';
interface Person {
name: string,
age: number
}
export default defineCompontent({
setup(props, context) {
const state = reactive<Person>({ name: 'vue', age: 10 });

watch(
[() => state.age, () => state.name],
([newName, newAge], [oldName, oldAge]) => {
console.log(newName);
console.log(newAge);

console.log(oldName);
console.log(oldAge);
}
)
// 修改age时会触发watch的回调,打印变更前后的值,此时需要注意,更改其中一个值,都会执行watch的回调
state.age = 100
state.name = 'vue3'
return {
...toRefs(state)
}
}
})
</script>

7.4 stop停止监听

在setup()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确的停止某个监视,可以调用watch()函数的返回值即可,语法如下:

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
<script lang="ts">
import { set } 'lodash';
import { computed, defineComponent, reactive, toRefs, watch } from 'vue';
interface Person {
name: string,
age: number
}
export default defineComponent({
setup(props, context) {
const state = reactive<Person>({ name: 'vue', age: 10 });

const stop = watch(
[() => stage.age, () => state.name],
([newName, newAge],[oldName, oldAge]) => {
console.log(newName);
console.log(newAge);

console.log(oldName);
console.log(oldAge);
}
)
// 修改age时会触发watch的回调, 打印变更前后的值,此时需要注意,更改其中一个值,都会执行watch的回调
state.age = 100;
state.name = 'vue3'

setTimeOut(() => {
stop()
// 此时修改时,不会触发watch回调
state.age = 1000
state.name = 'vue3-'
}, 1000)// 1秒后取消watch的监听

return {
...toRefs(state)
}
}
})
</script>

八、lifeCycle Hooks(新的生命后期)

新版的生命周期函数,可以按需导入到组件中,且只能在setup()函数中使用,但是也可以在srtup外定义,在setup中使用

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
<script lang="ts">
import { set } from 'lodash';
import { defineComponent, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onErrorCaptured, onMounted, onUnMounted, onUpdate } from 'vue'
export default defineComponent({
setup(props, context) {
onBeforeMount(()=> {
console.log('beformounted!')
})
onMounted(() => {
console.log('mounted!')
})

onBeforeUpdate(()=> {
console.log('beforupdated!')
})
onUpdated(() => {
console.log('updated!')
})

onBeforeUnmount(()=> {
console.log('beforunmounted!')
})
onUnmounted(() => {
console.log('unmounted!')
})

onErrorCaptured(()=> {
console.log('errorCaptured!')
})

return {}
}
})
</script>

九、Template refs

通过refs来获取真实的dom元素,这个和react的用法一样,为了获得对模板内元素或组件实例的引用,我们可以像往常一样在setup()中声明一个ref并返回它

  1. 还是跟往常一样,在html中写入ref的名称
  2. 在setup中定义一个ref
  3. setup中返回ref的实例
  4. onMounted中可以得到ref的RefImpl的对象,通过.value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<!-- 第一步:还是跟往常一样,在html中写入ref的名称 -->
<div class="main" ref="elmRefs">
<span>1111</span>
</div>
</template>
<script lang="ts">
import { set } from 'lodash';
import { defineComponent, onMounted, ref } from 'vue';
export default defineComponent({
setup(props, context) {
// 获取真实dom
const elmRefs = ref<null | HTMLElement>(null);
onMounted(() => {
console.log(elmRefs.value); // 得到一个 RefImpl 的对象,通过.value访问到数据
})
return {
elmRefs
}
}
})
</script>

十、vue的全局配置

通过vue实例上config来配置,包含Vue应用程序全局配置的对象。您可以在挂载应用之前修改下面列出的属性

1
2
3
const app = Vue.createApp({})

app.config = {...}

为组件渲染功能和观察程序期间的未捕获错误分配处理程序。错误和应用程序实例将调用处理程序

1
app.config.errorHandle = (err, vm, info) => {}

可以在应用程序内的任何组件实例中访问的全局属性,组件的属性将具有优先权。这可以代替Vue2.X Vue.prototype扩展:

1
2
const app = Vue.createApp({})
app.config.globalProperties.$http = 'xxxxxxxxxs'

可以在组件中通过getCurrentInstance()来获取全局globalProperties中配置的信息,getCurrentInstance方法获取当前组件的实例,然后通过ctx属性获得当前上下文,这样我们就能在setup中使用router和vuex,通过这个属性我们就可以操作变量、全局属性、组件属性等等

1
2
3
4
setup() {
const { ctx } = getCurrentInstance();
ctx.$http
}

十一、Suspense组件

在介绍Vue的Suspense组件之前,我们又必要先了解下React的Suspense组件,因为他们的功能类似。

React.lazy接受一个函数,这个函数需要动态调用import()。它必须返回一个Promise,该Promise需要resolve一个default export的React组件

1
2
3
4
5
6
7
8
9
10
11
import React, { Suspense } from 'react';
const myComponent = React.lazy(() => import('./Component'));
function myComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<myComponent />
</Suspense>
</div>
);
}

Vue3也新增了类似功能的defineAsyncComponent函数,处理动态引入(的组件)。defineAsyncComponent可以接受返回承诺的工厂函数。当您从服务器检索到组件定义时,应该调用Promise的解析回调。您还可以调用reject(reason)来指示负载已经失败

1
2
3
4
5
6
7
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
import('./components/AsyncComponent.vue')
)

app.component('async-component', AsyncComp)

Vue3也新增了Suspense组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>

</template>
<script lang="ts">
import { defineComponent, defineAsyncComponent } from "vue";
const MyComponent = defineAsyncComponent(() => import('./Component'));

export default defineComponent({
components: {
MyComponent
},
setup() {
return {}
}
})
</script>

十二、vue3.x完整组件模板结构

一个完整的vue3.x完整组件模板结构包含了:组件名称、props、components、setup(hooks、computed、watch、methods等)

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
<template>
<div class="mine" ref="elmRefs">
<span>{{ name }}</span>
<br>
<span>{{ count }}</span>
<div>
<button @click="handleClick">测试按钮</button>
</div>

<ul>
<li></li>
</ul>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, getCurrentInstance, onMounted, PropType, reactive, ref, toRefs } from "vue" ;

interface IState {
count: 0,
name: String,
list: Array<object>
}

export default defineComponent({
name: 'demo',
// 父组件传子组件参数
props: {
name: {
type: String as PropType<null | ''>,
default: 'vue3.x'
},
list: {
type: Array as PropType<object[]>,
default: () => []
}
},
components: {
// TODO 组件注册
},
emits:["emits-name"], // 为了提示作用
setup (props, context) {
console.log(props.name)
console.log(props.list)

const state = reactive<IState>({
name: 'vue 3.0组件',
count: 0,
list: [
{
name: 'vue',
id: 1
},
{
name: 'vuex',
id: 2
}
]
})

const a = computed(() => state.name)

onMounted(() => {

})

function handleClick() {
state.count++
// 调用父组件方法
context.emit('emits-name', state.count)
}

reutrn {
...toRefs(state),
handleClick
}
}
});
</script>

vue3的生态

  • 官网
  • 源码
  • vite 构建器
  • 脚手架
  • vue-route-next
  • vuex4.0

UI组件库

  • vant2.x
  • Ant Design of Vue 2.x
  • element-plus