目录
一、常用Composition API(1)
1.1 Setup
(1)理解:Vue3.0中的一个新的配置项,值为一个函数
(2)组件中所用到的:数据、方法等等,均要配置在setup中
(3)setup函数的两种返回值:
- 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用(重点)
- 若返回一个渲染函数:则可以自定义渲染内容(了解即可)
(4)使用setup时需注意:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methods、computed...)中可以访问到setup中的属性、方法
- 但在setup中不能访问到Vue2.x配置(data、methods、computed...)
- 如果有重名,setup优先
- setup不能是一个asyn函数,因为返回值不再是return的对象,而是promise,模板获取不到return对象中的属性(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合 )
(5)setup注意点:
- setup执行的时机
- 在beforeCreate之前执行依次,this 是 undefined
- setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
- context:上下文对象
- attrs:值为对象,半酣:组件外部传递过来,但没有在props配置中声明的属性,相当于 this.$attrs
- slot:收到的插槽内容,相当于 this.$slot
- emit:分发自定义事件的函数,相当于 this.$emit
1.1.1 案例练习
在App.vue中简单使用setup配置项
<template>
<h1>游戏信息</h1>
<h2>游戏名:{{ gname }}</h2>
<h2>发售日期:{{ date }}</h2>
<button @click="overDate">获取信息</button>
</template>
<script>
// import h from "vue";
export default {
name: "App",
setup() {
let gname = "Splatoon3";
let date = "2022年9月9日";
function overDate() {
alert(`我个人觉得${gname}是跳票到${date}`);
}
// 返回一个对象(常用)
return {
gname,
date,
overDate,
};
// 返回一个函数(渲染函数)
// return ()=> h('h1','Splatoon3')
},
};
</script>
1.2 ref函数
1.2.1 作用
定义一个响应式的数据
1.2.2 语法
// let xxx = ref(initValue)
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
- JS中操作数据:xxx.value
- 模板中读取数据:不需要.value,直接{{xxx}}
1.2.3 备注
- 接收数据可以是:基本类型、也可以是对象类型
- 基本类型的数据:响应式依然是依靠 object.defineProperty() 中的 get 与 set 完成的
- 对象类型的数据:内部使用了 Vue3.0 中的一个新函数 —— reactive函数
1.2.4 简单案例练习
在App.vue中简单使用ref对象
<template>
<h1>游戏信息</h1>
<h2>游戏名:{{ gname }}</h2>
<h2>发售日期:{{ date }}</h2>
<h2>游戏类型:{{ types.gType }}</h2>
<h2>游戏品类:{{ types.webType }}</h2>
<button @click="update">修改信息</button>
</template>
<script>
// 引入ref函数
import { ref } from "vue";
export default {
name: "App",
setup() {
// 基本类型的数据
let gname = ref("Splatoon3");
let date = ref("2022.9.9");
// 对象类型的数据
const types = ref({
gType: "FPS",
webType: "Online",
});
function update() {
gname.value = "异度之刃3";
date.value = "2022.7.29";
types.value.gType = "JRPG";
types.value.webType = "Single Game";
}
// 返回一个对象(常用)
return {
gname,
date,
types,
update,
};
},
};
</script>
1.3 reactive函数
1.3.1 作用
定义一个对象类型的响应式数据(基本类型用ref函数)
1.3.2 语法
const 代理对象 = reactive(源对象)
接收一个对象(或数组)返回一个代理对象(Proxy实例对象,简称proxy对象)
- reactive定义的响应式数据是"深层次的"
- 内部基于ES6的 Proxy 实现,通过 代理对象 操作 源对象内部数据进行操作
1.3.3 简单案例练习
在App.vue中简单使用reactive函数
<template>
<h1>游戏信息</h1>
<h2>游戏名:{{ game.gname }}</h2>
<h2>发售日期:{{ game.date }}</h2>
<h2>游戏类型:{{ game.types.gType }}</h2>
<h2>游戏品类:{{ game.types.webType }}</h2>
<h2>中文名:{{ game.cName }}</h2>
<button @click="update">修改信息</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
// 数据
let game = reactive({
gname: "Splatoon3",
date: "2022.9.9",
types: {
gType: "FPS",
webType: "Online",
},
cName: ["斯普拉遁3", "喷射战士3"],
});
// 方法
function update() {
game.gname = "Xenoblade3";
game.date = "2022.7.29";
game.types.gType = "JRPG";
game.types.webType = "Single Game";
game.cName[0] = "异度神剑3";
game.cName[1] = "异度之刃3";
}
// 返回一个对象(常用)
return {
game,
update,
};
},
};
</script>
1.4 reactive 对比 ref
1.4.1 定义数据角度对比
- ref用来定义:基本类型数据
- reactive用来定义:对象(或数组)类型对象
- 备注:ref也可以用来定义对象(或数组)类型对象,它内部会自动通过reactive转为代理对象
1.4.2 原理角度对比
- ref通过 Object.defineProperty() 的 get 与 set 来实现响应式(数据劫持)
- reactive 通过使用 Proxy 来实现响应式(数据劫持),并通过 Reflect 操作源对象内部的数据
1.4.3 使用角度对比
- ref定义的数据:操作数据需要 .value,读取数据时模板中直接读取不需要 .value
- reactive定义的数据:操作数据与读取数据:均不需要 .value
二、Vue3.0中的响应式原理
2.1 vue2.x的响应式
2.1.1 实现原理
- 对象类型:通过 Object.defineProperty() 对属性的读取、修改进行拦截(数据劫持)
- 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
Object.defineProperty( 对象 , '属性名',{
get(){},
set(){}
})
2.1.2 存在问题
- 新增属性、删除属性,界面不会刷新
- 直接下标修改数组,界面不会自动属性
问题解决:
- Vue.set(target, propertyName/index, value)
- vm.$set(target, propertyName/index, value)
2.2 Vue3.0的响应式
2.2.1 实现原理
- 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作
MDN文档中描述的Proxy与Reflect:
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
2.3 案例练习
2.3.1 模拟Vue2中实现响应式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 源数据
let person = {
name: "周星星",
age: 20,
};
// 模拟Vue2中实现响应式
let p = {};
Object.defineProperty(p, "name", {
configurable: true,
// 读取name时调用
get() {
return person.name;
},
// 修改name时调用
set(value) {
console.log("Vue2 - 修改了name属性,更新界面");
person.name = value;
},
});
Object.defineProperty(p, "age", {
configurable: true,
// 读取age时调用
get() {
return person.age;
},
// 修改age时调用
set(value) {
console.log("Vue2 - 修改了age属性,更新界面");
person.age = value;
},
});
</script>
</body>
</html>
2.3.2 模拟Vue3中实现响应式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 源数据
let person = {
name: "周星星",
age: 20,
};
// // 模拟Vue3中实现响应式
const p = new Proxy(person, {
// 读取p的某个属性时调用
get(target, propName) {
console.log(`Vue3 - 读取p身上的${propName}属性`);
return Reflect.get(target, propName);
},
// 修改p的某个属性 或 给p追加某个属性
set(target, propName, value) {
console.log(`Vue3 - 修改p身上的${propName}属性,更新界面`);
Reflect.set(target, propName, value);
},
// 删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`Vue3 - 删除p身上的${propName}属性,更新界面`);
return Reflect.deleteProperty(target, propName);
},
});
</script>
</body>
</html>