2022年5月24日 Vue3
cli项目
创建: Vue create vue3_demo
认识默认配置
main.ts
public -> index.html 所有组件会在这个<div>渲染
App.vue
src下的assets文件夹,存放静态资源
vue.config.js
语法检查严格模式,,,建议关了,会因为奇怪的原因停掉项目
Composition API
常用部分
setup
- 新的option(配置项),所有组合API函数都在此使用,只在初始化时执行一次
- 函数如果返回对象,对象中的属性或方法,模板中可以直接使用
ref
- 作用: 定义一个响应式的数据
- 语法: const xxx = ref(initValue)
- 一般用来定义一个基本类型的响应式数据
<template>
<h2>setup和ref的基本使用</h2>
<h3>{{count}}</h3>
<button @click="updateCount">更新数据</button>
</template>
<script lang="ts">
import {defineComponent,ref} from "vue";
export default defineComponent({
name: "App",
// 需求:页面打开后可以直接看到一个数据,点击按钮后数据变化
// vue2方式实现
// data(){
// return {
// count:0 // 属性
// }
// },
// methods:{
// updateCount(){
// this.count++ // 方法
// }
// }
// vue3方法实现
// setup是组合API的入口
setup(){
// 变量
// let count = 0 // 此时的数据不是响应式的数据
// ref是一个函数,作用是定义一个响应式的数据,返回的是一个ref对象,对象中有value属性,如果需要操作数据,用对象调用vlaue属性
// html模板中是不需要使用.value属性的写法
const count = ref(0)
// 方法
function updateCount(){
// count++ // 报错的原因是count是ref的对象,对象是不能进行++操作的
count.value++
}
// 返回的是一个对象
return {
// 属性
count,
// 方法
updateCount
}
}
});
</script>
reactive
- 作用: 定义多个数据的响应式
- const proxy = reactive(obj): 接收一个普通对象,返回一个Proxy类型的代理对象
- 响应式转换是"深层次的": 会影响对象内部所有嵌套的属性
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据都是响应式的
<template>
<h2>reactive的使用</h2>
<h3>名字:{{user.name}}</h3>
<h3>年龄:{{user.age}}</h3>
<h3>存款:{{user.money}}</h3>
<h3>媳妇:{{user.wife}}</h3>
<hr>
<button @click="updateUser">更新数据</button>
</template>
<script lang="ts">
import {defineComponent, reactive} from "vue";
export default defineComponent({
name: "App",
// 需求:显示用户的相关信息,点击按钮,可以更新用户的相关信息数据
setup(){
const obj = { name:'小贾',
age:20,
money:10000000000,
wife:{
name:'小富婆',
age:18,
cars:['奔驰','宝马','奥迪']
}}
const user = reactive(obj)
// 方法
// function updateUser(){} 或者
const updateUser = ()=>{
user.name += '=='
user.money = 0
user.wife.cars[0]
}
return{
user,
updateUser
}
}
});
</script>
如果操作代理对象,目标对象中的数据也会随之变化,页面也会重新渲染
比较Vue2 与 Vue3的响应式
据说是面试常问
Vue2的响应式
- 对象:通过Object.defineProperty实现,对对象的已有属性值的读取和修改进行劫持(监视/拦截) 问题是对象直接添加的属性或删除已有属性,界面不会自动更新
- 数组:通过重写数组更新数组一系列更新元素的方法来实现元素修改的劫持,就是重写更改元素的方法 问题是直接通过下标替换元素或者更新lenfth,界面不会自动更新 arr[1] = {}
Vue3的响应式
- 通过Proxy把基本对象变为代理对象,拦截对data任意属性的操作
const p = new Proxy(target,handler)
target:被Proxy代理虚拟化的对象.就是目标对象
handler:包含捕捉器(trap)的占位符对象,里面有监视的方法,用来监视数据的变化 - Reflect(反射) 是内置对象用来做反射操作,动态对被代理对象的相应属性进行特定操作
<script>
// 目标对象
const user = {
name:'佐助',
age:20,
wife:{
name:'小樱',
age:19
}
}
// 把目标对象变成代理对象
// 参数1:user--->target
// 参数2:handler--->处理器对象,用来监视及操作数据
const proxyUser = new Proxy(user,{
// 获取目标对象的某个属性值
get(target,prop){
console.log('get方法调用了');
return Reflect.get(target,prop)
},
// 修改目标对象的属性值/为目标对象添加新的属性
set(target,prop,val){
console.log('set方法调用了');
1. return Reflect.set(target,prop,val)
},
// 删除目标对象上某个属性
deleteProperty(target,prop){
console.log('delete方法调用了');
return Reflect.defineProperty(target,prop,val)
}
})
// 通过代理对象获取目标对象中的某个属性值
console.log(proxyUser.name)
// 通过代理对象更新目标对象上的某个属性值
proxyUser.name = '鸣人'
console.log(user);
// 通过代理对象向目标对象中添加一个新的属性
proxyUser.gender = '男'
console.log(user);
delete proxyUser.name
console.log(user);
// 更新目标对象中的某个属性对象中的属性值
proxyUser.wife.name = '雏田'
console.log(user);
</script>
</body>
</html>
setup细节
- setup执行时机
- setup是在beforeCreate生命周期回调之前就执行了,而且就执行一次
- setup在执行的时候当前组件还没有创建出来,也就意味着组件实例对象this根本不能用
- this是underfind,说明不能通过this调用data/computed/methods/pops中的相关内容
- 所有composition API相关回调函数也都不行
- setup的返回值
- setup的返回值是一个对象,内部的属性和方法是给html模板使用的
- setup中的对象内部的属性和data函数中的return对象的属性都可以在html模板中使用,且会合并为组件对象的属性
- setup中对象的方法和methods对象中的方法会合并为组件对象的方法
- 如果有重名setup优先 尽量不要混合使用
- setup不能是一个async函数:因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性数据
- 参数
- props参数,是一个对象,里面有父级组件向子级组件传递的数据,并且是在子级组件中使用props接收到的所有的属性
- 包含props配置声明且传入了的所有属性的对象
- context参数,是一个对象,里面有attrs对象(获取当前组件标签上的所有的属性的对象,但是该属性实在props中没有声明接收的属性的对象,emit方法(用来分发事件的),slots对象(插槽)
import {defineComponent} from 'vue'
export default defineComponent({
name:'Child',
props:['msg'],
// 数据初始化的生命周期回调
beforeCreate(){
console.log('beforeCreate执行了');
},
// 界面渲染完毕后调用
mounted(){},
setup(props,context){
// props参数,是一个对象,里面有父级组件向子级组件传递的数据,并且是在子级组件中使用props接收到的所有的属性
// 包含props配置声明且传入了的所有属性的对象
// context参数,是一个对象,里面有attrs对象,emit方法和slots插槽
// console.log(props);
console.log(context);
console.log('setup执行了',this);
const showMsg1 =() =>{
console.log('setup中的showMsg1方法s');
}
return {
showMsg1
// setup中一般都是返回一个对象,对象中的属性和方法都可以在html模板中直接使用
}
},
data () {
return {
count:10
}
},
// 界面渲染后的
mounted(){
console.log(this);
},
methods:{
showMsg2() {
console.log('methods中的showMsg方法');
}
}
})
</script>
reactive和ref细节问题
- ref用来处理基本类型数据,reactive用来处理对象(递归深度响应式)
- 如果是ref对象/数组,内部会自动将对象/数组转换为reactive的代理对象
- ref内部:通过给value属性添加getter/setter来实现对数据的劫持
- reactive内部:通过使用Proxy来实现对对象内部所有数据的劫持,并通过Reflect操做对象内部数据
<h3>m1:{{m1}}</h3>
<h3>m2:{{m2}}</h3>
<h3>m3:{{m3}}</h3>
<hr>
<button @click="update">更新数据</button>
</template>
<script lang="ts">
import {defineComponent, reactive, ref} from "vue";
// 引入子级组件Child
import Child from './components/Child.vue'
export default defineComponent({
name: "App",
// 注册组件
components: {
Child,
},
setup(){
// 通过ref的方法设置的数据
const m1 = ref('abc')
const m2 = reactive({name:'小明',
wife:{
name:'小红'
}
})
// ref也可以传入对象吗
const m3 = ref({
name:'小明',
wife:{
name:'小红'
}
})
// 更新数据
const update = ()=>{
m1.value+="==="
m2.wife.name+="==="
m3.value.wife.name+="==="
}
return {
m1,
m2,
m3,
update
}
}
})
</script>