Vue3.x组合式编程
# 数据绑定
在 Vue 中,状态都是默认深层响应式的。这意味着即使在更改深层次的对象或数组,你的改动也能被检测到。
reactive():创建一个响应式对象或数组(仅对对象类型有效)ref():创建可以使用任何值类型的响应式
区别
- ref 定义:基本类型数据,
- reactive定义:引用类型(对象、数组等)
- ref也可以定义引用类型,内部会转译为reactive然后使用proxy进行代理挂到value上。
- ref和reactive本质我们可以简单的理解为ref是对reactive的二次包装。
# 基本使用
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
import { ref } from 'vue'
const count = ref(0)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# ts中使用
import { ref } from 'vue'
import type { Ref } from 'vue'
const year: Ref<string | number> = ref('2020')
year.value = 2020 // 成功!
import { reactive } from 'vue'
interface Book {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue 3 指引' })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 原理Proxy
用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
# DOM更新nextTick
import { nextTick } from 'vue'
nextTick(() => {
// 访问更新后的 DOM
})
1
2
3
4
2
3
4
# 计算属性
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
return author.books.length > 0 ? 'Yes' : 'No'
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed<number>(() => {
// 若返回值不是 number 类型则会报错
})
const result = double.value.split('')
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 生命周期函数
import { onMounted } from 'vue'
onMounted(() => {
console.log(`the component is now mounted.`)
})
1
2
3
4
5
2
3
4
5
# watch
- 使用
import { ref, watch } from 'vue'
const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
// 可以直接侦听一个 ref
watch(question, (newQuestion, oldQuestion) => {
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 监测多个数据源
const x = ref(0)
const y = ref(0)
// 单个 ref
watch(x, (newX) => {
console.log(`x is ${newX}`)
})
// getter 函数
watch(
() => x.value + y.value,
(sum) => {
console.log(`sum of x + y is: ${sum}`)
}
)
// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
console.log(`x is ${newX} and y is ${newY}`)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- 深度对象某个值
// 提供一个 getter 函数
watch(
() => obj.count,
(count) => {
console.log(`count is: ${count}`)
}
)
1
2
3
4
5
6
7
2
3
4
5
6
7
- 深度监测
watch(
() => state.someObject,
(newValue, oldValue) => {
// 注意:`newValue` 此处和 `oldValue` 是相等的
// *除非* state.someObject 被整个替换了
},
{ deep: true }
)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- 即时监测
watch(source, (newValue, oldValue) => {
// 立即执行,且当 `source` 改变时再次执行
}, { immediate: true })
1
2
3
2
3
- 停止监测
侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它
import { watchEffect } from 'vue'
// 它会自动停止
watchEffect(() => {})
// ...这个则不会!
setTimeout(() => {
watchEffect(() => {})
}, 100)
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 组件
# 注册
import { createApp } from 'vue'
const app = createApp({})
app.component(
// 注册的名字
'MyComponent',
// 组件的实现
{
/* ... */
}
)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
import ComponentA from './ComponentA.vue'
1
# props
- 基本使用
import { defineProps } from 'vue'
const props = defineProps(['title'])
console.log(props.title)
defineProps({
title: String,
likes: Number,
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
})
const props = defineProps({
foo: { type: String, required: true },
bar: Number
})
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
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
- ts中使用
const props = defineProps<{
foo: string
bar?: number
}>()
1
2
3
4
5
2
3
4
5
- ts接口声明
interface Props {
foo: string
bar?: number
}
const props = defineProps<Props>()
1
2
3
4
5
6
2
3
4
5
6
# 事件监听
- 基础使用
<button @click="$emit('enlarge-text')">Enlarge text</button>
<MyComponent @some-event="callback" />
1
2
2
const emit = defineEmits(['enlarge-text'])
emit('enlarge-text')
1
2
3
2
3
- 校验
const emit = defineEmits({
// 没有校验
click: null,
// 校验 submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
})
function submitForm(email, password) {
emit('submit', { email, password })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- ts中使用
// 基于类型
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
1
2
3
4
5
2
3
4
5
# 双向绑定v-model
- 基础使用
<MyComponent v-model:title="bookTitle" />
1
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 自定义修饰符
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 自定义指令
// 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
}
app.directive('color', (el, binding) => {
// 这会在 `mounted` 和 `updated` 时都调用
el.style.color = binding.value
})
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
上次更新: 2024/01/18, 10:44:15