vue3
使用TypeScript
重写了代码,增加了Composition API
基于函数的API
,Vue3
中使用Proxy
重写了响应式原理.Vue3常用的API如:ref、toRef、toRefs、reactive、setup、watchEffect
等,Vue3中某些API的变化如watch、computed、v-model
等
setup入口
1 | <!-- 父组件 --> |
Vue3中的ref响应式API
ref返回「RefTml」对象、在模板视图中自动渲染value值,在js中使用时需要使用value属性去获取和设置值、基于ES5「Object.defineProperty」的set/get实现数据劫持、通常只作用与单个基本类型值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<template>
<p>支持人数:{{ supNum }}人</p>
</template>
<script>
import { ref } from 'vue'
export default {
// 基于 ref 响应式 API
// + 返回 RefImpl 对象
// + 在模板视图中自动渲染value值, 在js中使用时 需要使用value属性去获取和设置值
// + value属性是基于【Object.defineProperty】的set/get实现数据劫持
// + 通常只作用与单个基本类型值
setup () {
const supNum = ref(10) // 使用ref声明响应式单个数据
console.log(supNum) // RefImpl {_shallow: false,...,value: 10}
console.log(supNum.value) // 10
return { supNum }
}
}
</script>
Vue3中的reactive响应式API
reactive是基于ES6「Proxy」实现数据劫持、reactive返回「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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66<template>
<header class="vote-head">
<span>{{ state.oppNum + state.supNum }}人</span>
</header>
<main class="vote-main">
<p>支持人数:{{ state.supNum }}人</p>
<p>反对人数:{{ state.oppNum }}人</p>
<p>支持比率:{{ ratio }}</p>
</main>
<footer>
<button @click='change("sup")'>支持</button>
<button @click='change("opp")'>反对</button>
</footer>
</template>
<script>
import { computed, reactive } from 'vue'
export default {
setup () {
const state = reactive({ // 使用reactive声明响应式对象
supNum: 10,
oppNum: 5
})
// 计算属性
const ratio = computed(() => {
const total = state.supNum + state.oppNum
return total === 0 ? '--' : ((state.supNum / total) * 100).toFixed(2) + '%'
})
console.log(state) // Proxy {supNum: 10, oppNum: 5}
// 普通方法
const change = (type) => {
type === 'sup' ? state.supNum++ : state.oppNum++
}
return {
state, ratio, change
}
}
}
</script>
<!-- 模板中如果要省略 state.xxx 直接使像 ref 声明数据时 直接使用 xxx
需要使用 toRefs 将reactive声明对象的每一项都转换为单独的 RefImpl 对象-->
<template>
<main class="vote-main">
<p>支持人数:{{ supNum }}人</p>
<p>反对人数:{{ oppNum }}人</p>
</main>
</div>
</template>
<script>
import { computed, reactive, toRefs, toRef } from 'vue'
export default {
name: 'Vote',
props: ['title'],
setup () {
const state = reactive({ // 使用reactive声明响应式对象
supNum: 10,
oppNum: 5
})
// toRefs() 转换多个 toRef() 转化单个
console.log(toRef(state.supNum)) // RefImpl {_shallow: false,..., value: 10}
console.log(toRefs(state)) // {supNum: ObjectRefImpl, oppNum: ObjectRefImpl}
return {
...toRefs(state)
}
}
}
</script>
Vue3中的computed计算属性
computed([getter函数]),返回ComputedRefImpl对象,获取的计算属性值是只读的,若修改会报警告:computed value is 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<template>
<p>支持比率:{{ ratio }}</p>
<footer>
<button @click='change("sup")'>支持</button>
<button @click='change("opp")'>反对</button>
</footer>
</template>
<script>
import { ref, computed } from 'vue'
export default {
setup (props) {
const supNum = ref(10)
const oppNum = ref(5)
// 计算属性依赖的内容发生变更时, 才会重新执行
const ratio = computed(() => {
const total = supNum.value + oppNum.value
return total === 0 ? '--' : ((supNum.value / total) * 100).toFixed(2) + '%'
})
console.log(ratio) // ComputedRefImpl {dep: undefined, _dirty: true, …}
// ratio.value = '100%' // 无法修改 并警告 Write operation failed: computed value is readonly
// 普通方法
const change = (type) => {
type === 'sup' ? supNum.value++ : oppNum.value++
}
return {
ratio, change
}
}
}
</script>
Vue3中的 watch 监听器 和 watchEffect
watch 监听器 默认第一次加载组件不执行 当监听的数据改变时会触发执行
watchEffect(callback) 第一次加载组件和函数中所依赖数据发生改变时都会触发callback
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>
<p>支持人数:{{ supNum }}人</p>
<footer>
<button @click='change("sup")'>支持</button>
</footer>
</template>
<script>
import { ref, reactive, toRefs, watch, watchEffect } from 'vue'
export default {
props: ['title'],
setup () {
const state = reactive({ // 使用reactive声明响应式对象
supNum: 10
})
const num = ref(0)
// 普通方法
const change = (type) => {
type === 'sup' ? state.supNum++ : state.oppNum++
num.value++
}
// 1. 监听reactive创建的响应式对象的property属性时,第一个参数必须使用getter函数 watch(()=>state.supNum,()=>{})
watch(
// 第一个参数不能直接写成 state.supNum
() => state.supNum,
(next, prev) => { // next:新值 prev:旧值
console.log('watch: ', next, prev)
}
)
// 2. 监听单个ref或计算属性
watch(num, (next, prev) => { // next:新值 prev:旧值
console.log('watch: ', next, prev)
})
// 3. 直接给watch传入响应式对象, 会隐式创建深层侦听,嵌套属性值变更时都会被触发
watch(state, (next, prev) => { // 因为监听的是同一个对象 next 和 prev是相同的
console.log('watch: ', next, prev)
})
// 4. watch是懒执行的, 希望创建侦听器时,立即执行一次,可使用第三个参数 { immediate: true }
watch(
state,
(next, prev) => { // next:新值 prev:旧值
console.log('watch: ', next, prev)
},
{ immediate: true }
)
// watchEffect(callback) 第一次加载组件和函数中所依赖数据发生改变时都会触发callback 立即执行 不需要传递侦听的内容,会自动感知代码依赖的变化
watchEffect(() => {
console.log('watchEffect: ', state.supNum)
})
// 侦听器回调默认都会在vue组件更新之前被调用,若要获取响应式状态变化后组件更新之后的DOM
// 需要指明 flush: 'post' 选项
watch(state, (next, prev) => { // 因为监听的是同一个对象 next 和 prev是相同的
// 获取响应式状态更改组件更新后的DOM
}, { flush: 'post' })
return {
...toRefs(state), change, num
}
}
}
</script>
vue3中使用v-model实现组件通信
<Switch :value="bool" @update:value="bool = $event" />
简写为<Switch v-model:value="bool" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!-- 可以使用 v-model:x v-model:xx 绑定多个值 -->
<template>
<button :class="{'checked':value}" @click="toggle"><span></span></button>
</template>
<script lang="ts">
export default {
props: {
value: Boolean
},
setup(props, context){
const toggle = () => {
context.emit('update:value', !props.value)
}
return {toggle}
}
}
Vue3属性绑定
- 默认所有属性都绑定到根元素
- 使用inheritAttrs: false 可以取消默认绑定
- 使用 $attrs 或 context.attrs 获取所有属性
使用 v-bind=”$attrs” 批量绑定属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<!-- 父组件 -->
<Button @click="test" @focus="test" @mouseover="test">
<!-- 子组件 -->
<div :size="size">
<button v-bind="rest"></button>
</div>
<script lang="ts">
export default {
inheritAttrs: false, // 不继承父组件传递过来的属性
setup(props, context){
const {size, ...rest} = context.attrs
return {size, rest}
}
}
</script>props和attrs区别
- props要先声明才能取值, attrs不用先声明
- props不包含事件, attrs包含
- props 没有声明的属性 会在attrs里
- props支持string以外的类型 attrs只有string类型
Vue3其他变更
v-if优先级高于v-for
.native事件修饰符废除