1. 基础
还能使用vue2的语法(data,methods等),但是不建议
<tamplate>
// 可以不加外层div
<h2>{{ count }}</h2>
<button @click="update">更新</button>
</tamplate>
// 使用ts代码
<script lang="ts"></script>
2. setup(父子组件的交互)
在beforeCreate之前执行
父组件:
<child :msg="msg" attr="attr" @fn="fn"/>
import child from './child.vue'
components: {
child
}
子组件:
<h3>msg: {{ msg }}</h3>
<h3>msg2: {{ $attrs.msg2 }}</h3>
// setup (props, context) {
setup (props, {attrs, emit, slots}) {
/*
props: 父组件传递给子组件的数据
attr: 父组件写在子组件上的属性
emit: 父组件传递给子组件的方法
slots: 插槽
*/
console.log(props.msg, attrs.msg2, slots, emit)
function update () {
emit('fn', '++')
}
}
3. 计算属性
const user = reactive({
firstName: 'A',
lastName: 'B'
})
// 只有getter的计算属性
const fullName1 = computed(() => {
return user.firstName + '-' + user.lastName
})
// 有getter与setter的计算属性
const fullName2 = computed({
get () {
// 返回给fullName2
return user.firstName + '-' + user.lastName
},
set (value: string) { // value: fullName2
const [firstName, lastName] = value.split('-')
user.firstName = firstName
user.lastName = lastName
}
})
4. 监听属性
watch(user, (newVal, oldVal) => {
fullName3.value = newVal.firstName + '-' + newVal.lastName
}, {
immediate: true, // 是否初始化立即执行一次, 默认是false
deep: true, // 是否是深度监视, 默认是false
})
/* 监听多个数据:
使用数组来指定
如果是ref对象, 直接指定
如果是reactive对象中的属性,必须通过函数来指定
*/
// watch([user, fullName3], (values) => {
watch([() => user.firstName, () => user.lastName, fullName3], (values) => {
console.log('监视多个数据', values)
})
// 监听watchEffect内部的所有变量,初始化会立即执行一次
watchEffect(() => {
fullName3.value = user.firstName + '-' + user.lastName
})
5. 生命周期
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
6. hook
hooks/useMousePosition.ts:
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}
}
index.ts:
/* 在组件中引入并使用自定义hook
作用: 类似于vue2中的mixin技术
优势: 代码复用
*/
import useMousePosition from './hooks/useMousePosition'
export default {
setup() {
const {x, y} = useMousePosition()
return {
x,
y,
}
}
}
7. Composition API
7.1 ref获取元素
<input type="text" ref="inputRef">
import { onMounted, ref } from 'vue'
export default {
setup() {
const inputRef = ref<HTMLElement | null>(null)
onMounted(() => {
// 让输入框自动获取焦点
inputRef.value && inputRef.value.focus()
})
return {
inputRef
}
},
}
7.2 ref
<template>
// html中不需要加value
<h2>{{ count }}</h2>
<button @click="update">更新</button>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
// 非响应式,html没法检测js中count值的变化
const count = 1
// ref: 定义响应式数据(适用于基本数据类型,也可以放对象,但是要写多个value,没必要)
const count = ref(1)
function update () {
// js中的使用,需要加value
count.value = count.value + 1
}
// 暴露出去,给html用
return {
count,
update
}
}
}
</script>
7.3 reactive
<template>
<h2>name: {{ state.name }}</h2>
<button @click="update">更新</button>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
// reactive: 定义响应式数据(适用于对象),深层响应(内部数组的变化都可以被响应到)
// const state = reactive<any>({ // any类型就可以添加新属性
const state = reactive({ // 默认是不能添加新属性的
name: 'tom',
wife: {
name: 'marry',
arr: [1, 2, 3]
},
})
const update = () => {
state.name += '--'
state.wife.arr[4] = 4
}
return {
state,
update,
}
}
}
</script>
7.4 toRef
const state = reactive({
foo: 'a',
bar: 'b',
})
// foo1 和 state.foo 相互影响
const foo1 = toRef(state, 'foo')
// foo2 和 state.foo 不相互影响
const foo2 = ref(state.foo)
7.5 toRefs
export default {
setup () {
const state = reactive({
foo: 'a',
bar: 'b',
})
/* 相当于
const foo = ref('a');
const bar = ref('b');
*/
const stateAsRefs = toRefs(state)
const { foo2, bar2 } = useReatureX()
return {
...stateAsRefs,
foo2,
bar2
}
},
}
// 应用: 当从合成函数返回响应式对象时,toRefs 非常有用
function useReatureX() {
const state = reactive({
foo2: 'a',
bar2: 'b',
})
return toRefs(state)
}
7.6 toRaw 与 markRaw
const state = reactive<any>({
name: 'tom',
age: 25,
})
const testToRaw = () => {
const user = toRaw(state)
// state不会跟着变
user.age++
}
const testMarkRaw = () => {
const likes = ['a', 'b']
// likes不再是响应式,其他属性还是响应式
state.likes = markRaw(likes)
}
7.7 readonly 与 shallowReadonly
// 深度只读数据
const rState1 = readonly(state)
// 浅只读数据: 外层无法修改,内层可以修改
const rState2 = shallowReadonly(state)
7.8 shallowReactive 与 shallowRef(比较少用)
// 深层响应
const m1 = reactive({a: 1, b: {c: 2}})
// 浅响应,外层有响应,内层没响应
const m2 = shallowReactive({a: 1, b: {c: 2}})
// 深层响应
const m3 = ref({a: 1, b: {c: 2}})
// 浅响应: 只处理了value的响应式,不处理对象的响应式
const m4 = shallowRef({a: 1, b: {c: 2}})
7.9 customRef: 创建一个自定义的 ref
setup () {
// 参数一: 初始值
const keyword = useDebouncedRef('xxx', 500)
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)
}
}
})
}
7.10 响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
- isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
- isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
8.新组件
8.1 Teleport
<template>
<button @click="modalOpen = true">Open</button>
// 让组件的html在父组件界面外的特定标签(很可能是body)下插入显示
<teleport to="body">
<div v-if="modalOpen" class="modal">
<div>
<button @click="modalOpen = false">Close</button>
</div>
</div>
</teleport>
</template>
8.2 Suspense
应用程序在等待异步组件时渲染一些后备内容
父组件:
<template>
<Suspense>
<template v-slot:default>
<AsyncAddress/>
</template>
<template v-slot:fallback>
<h1>LOADING...</h1>
</template>
</Suspense>
</template>
子组件: AsyncAddress.vue
async setup() {
const result = await axios.get('/data/address.json')
return {
data: result.data
}
}
9. vue3和vue2的比较
- 新增脚手架vite(不支持vue2)
- 支持ts
- 速度更快: 虚拟DOM的优化,生命周期的优化等
- 打包出来的体积更小
- 新增 Composition API,更有利于实现代码的复用
- 重构响应式系统,使用 Proxy 替换 Object.defineProperty (重要)
- 可直接监听数组类型的数据变化
- 直接实现对象属性的新增/删除
- 监听的目标为对象本身,不需要像 Object.defineProperty 一样遍历每个属性,有一定的性能提升
- 可拦截apply、ownKeys、has等13种方法,而 Object.defineProperty 不行