Vue3常用API学习

vue3使用TypeScript重写了代码,增加了Composition API基于函数的API,Vue3中使用Proxy重写了响应式原理.Vue3常用的API如:ref、toRef、toRefs、reactive、setup、watchEffect等,Vue3中某些API的变化如watch、computed、v-model

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
<!-- 父组件 -->
<Vote title="Hello Vue3!"></Vote>
<!-- 子组件 -->
<template>
<h2>{{ title }}</h2>
<span>{{ num }}人</span>
</template>
<script>
import { ref } from 'vue'
export default {
props: ['title'],
// + setup通过形参可以拿到props接收的值 context 上下文
// + setup发生在初始化props和beforeCreate之间,在created 实例被完全初始化之前,没有this无法拿到vue实例
// + 会将data/methods/computed/watch/filters等options API聚合在setup中
// + setup只在第一次加载执行一次,重新渲染不会再执行它[销毁后再加载属于第一次]
// + setup中return返回的内容可以直接在视图中渲染
setup (props, context){
console.log(props) // Proxy {title: 'Hello Vue3!'}
console.log(this) // undefined
return { num: ref(10) }
}
}
</script>
<!-- 渲染后 <h2>Hello Vue3!</h2><span>10人</span> -->

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区别

  1. props要先声明才能取值, attrs不用先声明
  2. props不包含事件, attrs包含
  3. props 没有声明的属性 会在attrs里
  4. props支持string以外的类型 attrs只有string类型

Vue3其他变更

  • v-if优先级高于v-for
  • .native事件修饰符废除
-------------本文结束感谢您的阅读-------------
0%