本篇文章基于该视频: 尚硅谷教学Vue2与Vue3
常用composition API
setup函数
初始
新的配置项 值为一个函数 组件中的所有数据、方法等,均要配置在setup中
setup函数有两个返回值: ①若返回一个对象,则对象中的属性、方法,在模板中均可使用。(重点) ②若返回一个渲染函数,则可以自定义渲染内容。(了解)
? vue2中可读取setup中的数据,若重名,则setup优先。(切记不可混用) 不能是一个async函数,因为async函数返回值是promise,在模板中看不到return对象中的属性
<template>
<div>
<h1>{{ msg }}</h1>
<h1>{{uname}}</h1>
<button @click="sayUname">alert</button>
</div>
</template>
<script>
import {h} from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let uname = 'lyz'
function sayUname() {
alert(`我是${uname}`)
}
return {uname,sayUname}
}
}
</script>
注意点
执行时机: 在beforeCreate(通过配置项)之前执行 此时this为undefined
参数: props:值为对象 包含组件外部传递过来的且组件内部声明接收了的属性(父子传值) context:上下文对象 ① attrs:值为对象 包括:组件外部传递过来但未在props配置中声明的属性 相当于this.$attrs ② slots:收到插槽内容 相当于this.$slots ③ emit:分发自定义事件的函数 相当于this.$emit
<template>
<Test msg="Welcome to My App" @toTest="toTestOne">
<template v-slot:qwe>
<span>qwe</span>
</template>
</Test>
</template>
<script>
import Test from './components/Test.vue'
export default {
name: 'App',
components: {
Test
},
setup() {
function toTestOne(el) {
alert(`收到参数${el}`)
}
return {toTestOne}
}
}
</script>
<template>
<div>
<button @click="toTest">test</button>
</div>
</template>
<script>
export default {
name: 'Test',
props: {
msg: String
},
emits:['toTest'],
setup(props, context) {
console.log(context.slots)
function toTest() {
console.log(context)
context.emit('toTest', 'lyz')
}
return {toTest}
}
}
</script>
<style scoped>
</style>
响应式
ref函数(基本数据类型)
Vue2中所有data都会被捕捉,加入setter、getter 带来性能开销 ref()可以选择哪些数据被捕捉 哪些无需监听
基本数据类型:get set 引用数据类型:Proxy (ref()—>reactive())
<template>
<div>
<h1>{{ msg }}</h1>
<h1>{{uname}}</h1>
<h2>{{uage}}</h2>
<h3>{{job.type}}</h3>
<h3>{{job.salary}}</h3>
<button @click="editInfo">修改</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let uname = ref('lyz')
let uage = ref('22')
let job = ref({
type: '前端工程师',
salary: '30k'
})
function editInfo() {
console.log(job);
uname.value = 'my'
uage.value = 23
job.value.type = '后端工程师',
job.value.salary = '50k'
}
return {uname, uage, job,editInfo}
}
}
</script>
reactive函数(引用数据类型)
引用类型的响应式数据 let 代理对象 = reacvtive(源对象) 内部基于Proxy实现,通过代理对象操作源对象内部数据进行操作
<template>
<div>
<h1>{{ msg }}</h1>
<h1>{{person.uname}}</h1>
<h2>{{person.age}}</h2>
<h3>{{person.type}}</h3>
<button @click="editInfo">修改</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let person = reactive({
uname: 'lyz',
age: 22,
type: '前端工程师'
})
function editInfo() {
person.uname = 'my'
person.age = 23
person.type = '后端工程师'
}
return {person, editInfo}
}
}
</script>
返回一个data对象 类似于vue2
<template>
<div>
<h1>{{data.person.uname}}</h1>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let data = reactive({
person: {
uname: 'lyz'
}
})
return {data}
}
}
</script>
<style scoped>
</style>
响应式原理
Vue2.0X响应式原理
对象:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。 数组:通过重写数组的一系列方法来完成拦截。(对数组的变更方法进行了包裹) [...].splice() vue.set() this.$set() vue.delete() this.$delete()
存在问题? ① 新增 删除界面不会更新; ② 直接通过下标修改数组,界面不会自动更新。
let p = new Proxy(person, {
get(target, propName) {
console.log('get')
return target[propName]
},
set(target, propName, value) {
console.log('set')
target[propName] = value
},
deleteProperty(target, propName) {
console.log('delete')
return delete target[propName]
}
})
Vue3.0X响应式原理
通过Proxy(代理)拦截对象中任意属性的变化:读 写 添加 删除等 通过Reflect(反射) 对被代理对象(源对象)的操作
let p = new Proxy(person, {
get(target, propName) {
console.log('get')
return Reflect.get(target, propName)
},
set(target, propName, value) {
console.log('set')
target[propName] = value
Reflect.set(target, propName, value)
},
deleteProperty(target, propName) {
console.log('delete')
return Reflect.deleteProperty(target, propName)
}
})
computed
与vue2配置功能一致 写在setup中
<template>
<div>
<input v-model="person.firstName"/><br>
<input v-model="person.lastName"/><br>
<input v-model="person.fullName"/>
</div>
</template>
<script>
import {reactive,computed} from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let person = reactive({
firstName: 'lyz',
lastName: 'my'
})
person.fullName = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(value) {
let fullName = value.split('-')
person.firstName = fullName[0]
person.lastName = fullName[1]
}
})
return {person}
}
}
</script>
watch
与vue2配置功能一致 写在setup中
不同情况
情况一 监视ref所定义的一个响应式数据
情况二 监视ref所定义的多个响应式数据
情况三 监视reactive所定义的一个响应式数据 ▲此处无法获取正确的oldValue 强制开启了深度监听(deep无效)
情况四 监视reactive所定义的一个响应式数据的某个属性 相当于监视的基本数据类型
情况五 监视reactive所定义的一个响应式数据的某些属性
特殊情况 监视的是reactive定义的对象中的对象属性 则deep有效 此处无法获取正确的oldValue
<template>
<div>
<h1>{{sum}}</h1>
<button @click="sum++">sum+1</button>
<hr>
<h1>{{hello}}</h1>
<button @click="hello+='!'">hello+!</button>
<hr>
<h1>{{person.name}}</h1>
<h1>{{person.age}}</h1>
<h1>{{person.job.j1.salary}}</h1>
<button @click="person.name+='!'">姓名+!</button>
<button @click="person.age++">年龄+1</button>
<button @click="person.job.j1.salary++">工资+1000</button>
</div>
</template>
<script>
import {watch, ref, reactive} from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let sum = ref(0),
hello = ref('hello')
let person = reactive({
name: 'lyz',
age: 21,
job: {
j1: {
salary: 10000
}
}
})
watch(() =>person.job, (newValue, oldValue) =>{
console.log('person的job被监听了', newValue, oldValue)
}, {deep: true})
return {sum, hello, person}
}
}
</script>
监听ref定义的数据(基本或引用数据类型)
基本数据类型:监听的是RefImpl对象 引用数据类型:监听的是Proxy对象 实际为reactive定义的数据
<template>
<div>
<h1>{{sum}}</h1>
<button @click="sum++">sum+1</button>
<hr>
<h1>{{hello}}</h1>
<button @click="hello+='!'">hello+!</button>
<hr>
<h1>{{person.name}}</h1>
<h1>{{person.age}}</h1>
<h1>{{person.job.j1.salary}}</h1>
<button @click="person.name+='!'">姓名+!</button>
<button @click="person.age++">年龄+1</button>
<button @click="person.job.j1.salary++">工资+1000</button>
</div>
</template>
<script>
import {watch, ref} from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let sum = ref(0),
hello = ref('hello'),
person = ref({
name: 'lyz',
age: 21,
job: {
j1: {
salary: 10000
}
}
})
watch(sum, (newValue, oldValue) =>{
console.log('sum值变化', newValue, oldValue)
})
watch(person, (newValue, oldValue) =>{
console.log('person变化', newValue, oldValue)
}, {deep: true})
return {sum, hello, person}
}
}
</script>
watchEffect函数
与watch区别
watch:既要指名监听的属性。也要指名监听回调函数 watchEffect:无需指名监听那个属性,只监听回调中用哪个属性就监视哪个属性
与computed区别
有点类似,但是 computed:注重计算出的结果,回调函数有返回值 watchEffect:更注重过程,回调函数的而函数体
eg:报销流程、ajax请求
<template>
<div>
<h1>{{sum}}</h1>
<button @click="sum++">sum+1</button>
<hr>
<h1>{{hello}}</h1>
<button @click="hello+='!'">hello+!</button>
<hr>
<h1>{{person.name}}</h1>
<h1>{{person.age}}</h1>
<h1>{{person.job.j1.salary}}</h1>
<button @click="person.name+='!'">姓名+!</button>
<button @click="person.age++">年龄+1</button>
<button @click="person.job.j1.salary++">工资+1000</button>
</div>
</template>
<script>
import {ref, reactive, watchEffect} from 'vue'
export default {
name: 'Test',
props: {
msg: String
},
setup() {
let sum = ref(0),
hello = ref('hello'),
person = reactive({
name: 'lyz',
age: 21,
job: {
j1: {
salary: 10000
}
}
})
watchEffect(() =>{
let sum_w = sum.value
let salary_w = person.job.j1.salary
console.log('watchEffect监听了', sum_w, salary_w)
})
return {sum, hello, person}
}
}
</script>
生命周期
与vue2相比,修改了最后两个钩子函数,改为beforeUnmount、unmounted
组合式api先于配置项出现,最好用一种形式 beforeCreate/created对应 组合式api setup函数
配置项
<template>
<div>
<h1>{{sum}}</h1>
<button @click="sum++">sum+1</button>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'Test',
setup() {
let sum = ref(0)
return {sum}
},
beforeCreate() {
console.log('-----beforeCreate-----')
},
created() {
console.log('-----created-----')
},
beforeMount() {
console.log('-----beforeMount-----')
},
mounted() {
console.log('-----mounted-----')
},
beforeUpdate() {
console.log('-----beforeUpdate-----')
},
updated() {
console.log('-----updated-----')
},
beforeUnmount(){
console.log('-----beforeUnmount-----')
},
unmounted() {
console.log('-----unmounted-----')
}
}
</script>
组合式api
<template>
<div>
<h1>{{sum}}</h1>
<button @click="sum++">sum+1</button>
</div>
</template>
<script>
import {ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted} from 'vue'
export default {
name: 'Test',
setup() {
let sum = ref(0)
onBeforeMount(()=>{
console.log('-----onBeforeMount-----')
})
onMounted(()=>{
console.log('-----onMounted-----')
})
onBeforeUpdate(()=>{
console.log('-----onBeforeUpdate-----')
})
onUpdated(()=>{
console.log('-----onUpdated-----')
})
onBeforeUnmount(()=>{
console.log('-----onBeforeUnmount-----')
})
onUnmounted(()=>{
console.log('-----onUnmounted-----')
})
return {sum}
}
}
</script>
hook函数
本质上是一个函数,把setup函数中使用的Composition API进行封装,类似于Vue2.X中的mixin 自定义hook函数的优势:复用代码,让setup中的逻辑清晰易懂
import { reactive, onBeforeMount, onBeforeUnmount} from "vue"
export default function() {
let point = reactive({
x: 0,
y: 0
})
function savePiont(e) {
point.x = e.pageX
point.y = e.pageY
console.log(e.pageX, e.pageY)
}
onBeforeMount(() =>{
window.addEventListener('click', savePiont)
})
onBeforeUnmount(() =>{
window.removeEventListener('click', savePiont)
})
return point
}
<template>
<div>
<h1>{{sum}}</h1>
<button @click="sum++">sum+1</button>
<hr>
<h2>X:{{point.x}} Y:{{point.y}}</h2>
</div>
</template>
<script>
import {ref} from 'vue'
import usePoint from '../hook/usePoint.js'
export default {
name: 'Test',
setup() {
let sum = ref(0)
let point = usePoint()
return {sum, point}
}
}
</script>
toRef函数
作用:创建一个ref对象 其value指向另一个对象中的某个属性 console.log(toRef(p, 'uname')) 应用:要将响应式对象中的某个属性单独提供给外部使用 扩展:toRefs于其功能一致,但可批量创建多个ref对象 console.log(toRefs(p, 'uname'))
|