基本结构
组件定义在setup
方法中完成,setup
在beforeCreate
之前执行一次,setup
中this
是undefined
一般情况下setup
函数不能是async
,否则组件无法正常渲染
<template>
<h2>{{name}}</h2>
<h2>{{age}}</h2>
<button @click="sayHello">sayHello</button>
</template>
<script>
export default {
name: 'App',
setup() {
// 取代Vue2中的data部分
let name = 'zhangsan'
let age = 18
// 取代Vue2中的methods部分
function sayHello() {
alert('hello')
}
// 将data和methods返回,共模板部分使用
return {
name,
age,
sayHello
}
}
}
</script>
setup
函数的参数
setup(props, context) {
}
props
组件已声明接收的props
属性
context.attrs
组件未声明接收的props
属性
context.slots
组件接收的插槽内容
context.emit
可触发自定义事件,相当于Vue2的this.$emit
响应式定义
ref响应式
底层原理是Object.defineProperty
,对对象、数组处理时调用reactive
<script>
import { ref } from 'vue'
export default {
name: 'App',
setup() {
let name = ref('zhangsan')
let age = ref(18)
let job = ref({
name: 'UI',
salary: 3000
})
function update() {
name.value = 'lisi'
age.value = 20
job.value.name = 'Java'
job.value.salary = 8000
}
return {
name,
age,
job,
update
}
}
}
</script>
reactive响应式
无法定义基本数据类型
底层原理是Proxy
和Reflect
<script>
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
let person = reactive({
name: 'zhangsan',
age: 18,
job: 'UI',
salary: 8000,
hobby: ['dogs', 'cats']
})
function update() {
person.name = 'lisi'
person.age = 20
person.job = 'Java'
person.salary = 9000
person.hobby[0] = 'ducks'
}
return {
person,
update
}
}
}
</script>
计算函数
<template>
<input type="text" v-model="person.firstName">
<input type="text" v-model="person.lastName">
<input type="text" v-model="person.fullName">
</template>
import {reactive, computed} from 'vue'
setup() {
let person = reactive({
firstName: '张',
lastName: '三'
})
// 简单写法
person.fullName = computed(() => {
return person.firstName + '-' + person.lastNaame
})
// 详细写法
person.fullName = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(value) {
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
}
监视函数
watch
import {ref, reactive, watch} from 'vue'
setup() {
let sum = ref(0)
let msg = ref('你好')
let person = reactive({
name: 'zhangsan'
age: 18
})
// 监视一个基础类型ref
watch(sum, (newValue, oldVaule) => {
}, {immediate: true})
// 监视多个基础类型ref
watch([sum, msg], (newValue, oldValue) => {
})
// 监视一个对象ref
watch(person, (newValue, oldValue) => {
}, {deep: true})
// 监视一个对象ref,转换为监视reactive
watch(person.value, (newValue, oldValue) => {
})
// 监视一个reactive,无法正确获取oldValue且强制开启深度监视
watch(person, (newValue, oldValue) => {
})
// 监视一个reactive的某一属性
watch(() => person.name, (newValue, oldValue) => {
})
// 监视一个reactive的多个属性
watch([() => person.name, () => person.age], (newValue, oldValue) => {
})
// 监视一个reactive的某一对象属性
watch(() => person.job, (newValue, oldValue) => {
}, {deep: true})
}
watchEffect
类似computed,但不关注返回值,只关注相关数据改变时执行某些操作
import {ref, reactive, watchEffect} from 'vue'
setup() {
let sum = ref(0)
let msg = ref('hello')
let person = reactive({
name: 'zhangsan',
age: 18,
job: {
salary: 20
}
})
watchEffect(() => {
// 访问到的变量变化,函数会被调用
const x1 = sum.value
const x2 = person.job.salary
})
}
生命周期钩子
create
创建模型对象mount
挂载页面DOMupdate
更新页面DOMunmount
卸载页面DOMactivated
或deactivated
组件是否显示
unmount
取代Vue2的destroy
组合API统一在原钩子函数名前面加on
,没有onBeforeCreate
和onCreated
,可在setup
中直接实现
import {onBeforeMount} from 'vue'
setup() {
onBeforeMount(() => {
})
}
hook模块化
拆分模块使用
/src/hooks/usePoint.js
import {reactive, onMounted, onBeforeUnmount} from 'vue'
export default function() {
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
onMounted(() => {
window.addEventListener('click', savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
引用hook
import usePoint from '@/src/hooks/usePoint'
export default {
name: 'Test',
setup() {
const point = usePoint()
return {
point
}
}
}
一些组合式API
toRef
将响应式对象中的某个属性单独提供给外部时使用
import {reactive, toRef, toRefs} from 'vue'
setup() {
let person = reactive({
name: 'zhangsan',
age: 18,
job: {
salary: 20
}
})
return {
personName: toRef(person, 'name')
...toRefs(person)
}
}
customRef
自定义ref
响应式,显式控制模板与数据的绑定
<template>
<input type="text" v-model="keyword">
<h3>{{keyWord}}</h3>
</template>
import {customRef} from 'vue'
export default {
name: 'App',
setup() {
function myRef(value, delay) {
let timer
return customRef((track, trigger) => {
return {
get() {
// 通知Vue追踪双向绑定的数据变化
track()
return value
},
set(newValue) {
// 增加防抖功能
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
// 通知Vue重新解析模板
trigger()
}, delay)
}
}
})
}
let keyword = myRef('hello', 500)
return {keyword}
}
}
provide与inject
let car = reactive({name: 'a', price: 40})
provide('car', car)
后代组件获取数据
const car = inject('car')
其它
shallowReactive
只处理对象最外层属性的响应式shallowRef
只处理基本数据类型的响应式,对象替换也可以处理readonly
让一个响应式的数据变为只读shallowReadonly
让一个响应式的数据变为浅只读toRaw
将一个reactive
生成的响应式对象转为普通对象markRaw
标记一个对象不处理响应式isRef
检查是否是ref
方法生成的对象isReactive
检查是否是reactive
方法生成的对象isReadonly
检查是否是readonly
方法生成的对象isProxy
检查是否是reactive
方法或readonly
方法生成的对象
组件
Fragment组件
template
中无需只有一个根标签,当有多个根标签时,会将多个根标签包含在一个虚拟元素中
Teleport组件
将页面结构传送到其它地方,可使用CSS选择器指定
<template>
<div>
<button @click="isShow = true">click</button>
<teleport to="body">
<div></div>
</teleport>
</div>
</template>
Suspense组件
动态引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(() => import('./components/Child'))
export default {
name: 'App',
components: {Child}
}
使用Suspense组件
<template>
<div>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>稍等</h3>
</template>
</Suspense>
</div>
</template>
此时,Child
组件的setup
方法可以await
一个promise
,setup
方法可以标记为async
,组件可以在异步调用完成后渲染出来
一些变化
- 全局API转移,从
Vue
转移到app
,Vue.prototype
转移到app.config.globalProperties
- 子组件中
emits
属性声明自定义事件,没声明的为原生事件,原生事件不需要再加native
PREVIOUSVue响应式原理
NEXTJava运行时泛型信息使用