Composition API(组合式)
setup()函数
setup函数的参数:1、第一个参数:props,2、第二个参数:context。
第一个参数:props
子组件接收的从父组件传递过来的属性,会被放到props对象中,在setup函数中使用,可以直接通过props参数获取使用即可。
第二个参数:context
context也称之为是一个SetupContext,它里面包含三个属性:
1、attrs:为一个对象,存储着所有的从父组件传递过来的非props的attribute;
2、slots:父组件传递过来的插槽;
3、emit:组件内部需要发出事件时用emit;
setup函数的返回值
setup的返回值可以在模板template中被使用,也就是说可以通过setup的返回值来替代data选项。
注:对于定义的变量来说,默认情况下,Vue并不会跟踪它的变化,来引起界面的响应式操作。
注:setup中避免使用this,因为它不会找到组件实例,setup的调用发生在data property、computed property或methods被解析之前,所以它们无法在setup中被获取。
参考:Vue3,仍然支持Options API用法,更推荐Composition API,代码案例
ref()
ref,生成值类型(即基本数据类型) 的响应式数据,用于模板和reactive,通过.value来修改值,注意一定要加上.value,不仅可以用于响应式,还可以用于模板的DOM元素。
ref()函数可以根据给定的值来创建一个响应式的数据对象,返回值是一个对象,且只包含一个 .value属性。
为什么要用ref?
值类型(即基本数据类型)无处不在,如果不用ref而直接返回值类型,会丢失响应式,如在setup、computed、合成函数等各种场景中,都有可能返回值类型,Vue如果不定义 ref ,用户将自己制造 ref ,这样反而会更加混乱。
reactive()
Vue3中,reactive提供实现响应式数据的方法。在Vue2中响应式数据是通过defineProperty来实现的,而在Vue3中响应式数据是通过ES6的Proxy来实现的。
reactive()函数接收一个普通的对象,返回出一个响应式对象。
reactive参数必须是对象(json/arry),默认情况下修改对象,界面不会自动更新,若想更新,可通过重新赋值的方式。
toRefs()
对一个普通对象来说,要实现响应式,就用reactive,用了reactive后,它就是一个响应式对象。在一个响应式对象里面,如果其中有一个属性要拿出来单独做响应式的话,就可以用toRef。
toRef,可以响应对象Object ,其针对的是某一个响应式对象(reactive封装)的属性prop。toRef和对象Object两者保持引用关系,即一个改完另外一个也跟着改。toRef,如果用于普通对象(非响应式对象),产出的结果不具备响应式。
toRef(Object, prop) 的形式来传对象名和具体的属性名,达到某个属性数据响应式的效果。
export default和export default defineComponent区别
defineComponent
Vue3中,新增了defineComponent,它并没有实现任何的逻辑,只是把接收的Object直接返回,它的存在是完全让传入的整个对象获得对应的类型,它的存在就是完全为了服务TypeScript 而存在的。
Vue,单文件组件开发时,会有下面两种写法:JavaScript的写法、TypeScript的写法。
JavaScript的写法
<script >
export default({
// js写法
})
</script>
TypeScript的写法
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
// ts写法
})
</script>
Composition API(组合式)的代码案例
代码案例一
效果
demo01Manage.vue
<template>
<div id="demo01">
<h2>
姓名:<span style="color: red">{{ userName }}</span>
</h2>
<h2>
年龄:
<button type="button" @click="changeAge(-1)">+</button>
{{ userAge }}
<button type="button" @click="changeAge(1)">+</button>
</h2>
<h2>出生年份: (插值表达式实现) {{ new Date().getFullYear() - userAge }}</h2>
<h2>
出生年份:(计算属性实现)
<button type="button" @click="changeYear(-1)">-</button>
{{ birthYear }}
<button type="button" @click="changeYear(1)">+</button>
</h2>
</div>
</template>
<script>
import { ref, computed } from "vue";
export default {
name: "demo01",
setup() {
// ref(),接受一个参数值并返回一个响应式且可改变的ref对象
const userName = ref("张三");
// 年龄
const userAge = ref(20);
// 出生年份
const birthYear = computed({
// 设置 getter 和 setter
get: () => {
return new Date().getFullYear() - userAge.value;
},
set: (val) => {
userAge.value = new Date().getFullYear() - val;
},
});
// 计算属性
function changeAge(val) {
// userAge是一个响应式对象,不可以userAge++
userAge.value += val;
}
// 计算属性
const changeYear = (val) => {
birthYear.value = birthYear.value + val;
};
return { userName, userAge, birthYear, changeAge, changeYear };
},
};
</script>
<style>
#demo01{
background-color: rgb(171, 179, 223);
}
</style>
代码案例二
效果
demo02Manage.vue
<template>
<div id="demo02">
<h2>
姓名:<span style="color: red">{{ userName }}</span>
</h2>
<h2>
年龄:
<button type="button" @click="changeAge(-1)">+</button>
{{ userAge }}
<button type="button" @click="changeAge(1)">+</button>
</h2>
<h2>
出生年份: (插值表达式实现) {{ new Date().getFullYear() - userAge }}
</h2>
<h2>
出生年份:(计算属性实现)
<button type="button" @click="changeYear(-1)">-</button>
{{ birthYear }}
<button type="button" @click="changeYear(1)">+</button>
</h2>
</div>
</template>
<script>
import { reactive, computed, toRefs } from "vue";
export default {
name: "demo02",
setup() {
const data = reactive({
userName: "张三",
userAge: 20,
birthYear: computed({
// 设置 getter 和 setter
get: () => {
return new Date().getFullYear() - data.userAge;
},
set: (val) => {
data.userAge = new Date().getFullYear() - val;
},
}),
});
// 计算属性
function changeAge(val) {
data.userAge += val;
}
// 计算属性
const changeYear = (val) => {
data.birthYear = data.birthYear + val;
};
// 直接返回data是一个响应式的数据,页面需要使用data.xxx得到属性
// 页面直接使用属性,使用 ..toRefs()方法将一个整体的响应式对象变成普通对象
// 展开(解包)得到单独的响应式数据
return { ...toRefs(data), changeAge, changeYear };
},
};
</script>
<style>
#demo02 {
background-color: rgb(223, 171, 219);
}
</style>
代码案例三
效果
demo03Manage.vue
<template>
<div id="demo03">
<h1>{{name}}</h1>
<Child
id="child"
class="demoClass"
message="发送消息"
:title="title"
@receiveData="receiveData">
</Child>
</div>
</template>
<script>
import { ref } from 'vue'
import Child from './childComponent.vue'
export default {
name:'demo03',
components:{
Child
},
setup() {
const name = ref('我是父组件');
const title = ref('定义标题' + new Date().getTime());
// 接收子页面传输的元素
const receiveData = (data) => {
console.log("parent.监听到了change事件", data);
};
return {
name,
title,
receiveData
}
},
}
</script>
<style>
#demo02 {
background-color: rgb(171, 217, 223);
}
.demoClass{
color:rgb(4, 0, 255);
}
</style>
childComponent.vue
<template>
<div id="demo03">
<div>
<h3>子组件</h3>
<h3>{{ title }}</h3>
<h3>{{ message }}</h3>
<h3>title: {{ $props.title }} ----- message: {{ $props.message }}</h3>
<h3>id: {{ $attrs.id }} ----- class: {{ $attrs.class }}</h3>
<slot></slot>
</div>
<hr/>
<h2>姓名:<span style="color: red">{{ userName }}</span></h2>
<h2>
年龄:
<button type="button" @click="changeAge(-1)">+</button>
{{ userAge }}
<button type="button" @click="changeAge(1)">+</button>
</h2>
<h2>
出生年份: (插值表达式实现) {{ new Date().getFullYear() - userAge }}
</h2>
<h2>
出生年份:(计算属性实现)
<button type="button" @click="changeYear(-1)">-</button>
{{ birthYear }}
<button type="button" @click="changeYear(1)">+</button>
</h2>
</div>
</template>
<script>
import { reactive, computed, toRefs, watch } from "vue";
export default {
name: "demo03",
// setup 参数
props: {
title: {
type: String,
default: "",
},
message: {
type: String,
required: true,
},
},
emits: ["receiveData"],
setup(props, context) {
// =======================================================//
const { attrs, slots, emit } = context;
//在setup中获取props中属性,通过setup函数的第一个参数获取
console.log("props", props, props.message, props.title);
//父组件传递给子组件的非props属性在setup中使用,这些属性都存储在context.attrs对象中
console.log("attrs", attrs, attrs.id, attrs.class);
//父组件插入子组件插槽中的内容
console.log("slots", slots);
// =======================================================//
//emit用来向父组件发送事件
emit("receiveData", "子组件发送事件");
// =======================================================//
const data = reactive({
userName: "张三",
userAge: 20,
birthYear: computed({
// 设置 getter 和 setter
get: () => {
return new Date().getFullYear() - data.userAge;
},
set: (val) => {
data.userAge = new Date().getFullYear() - val;
},
}),
});
// 使用监听器
const watchTitle = watch(() => props.title, function(newTitle, oldTitle){
console.log("......" + oldTitle + "->" + newTitle);
emit("receiveData", oldTitle + "->" + newTitle);
}
);
const watchAge = watch(() => data.userAge, (newAge, oldAge) => {
console.log("receiveData", newAge + "->" + oldAge);
emit("receiveData", newAge + "->" + oldAge);
}
);
// 计算属性
function changeAge(val) {
data.userAge += val;
props.title = "年龄" + data.userAge;
}
// 计算属性
const changeYear = (val) => {
// 响应式对象
data.birthYear = data.birthYear + val;
};
// 直接返回data是一个响应式的数据,页面需要使用data.xxx得到属性
// 页面直接使用属性,使用 ..toRefs()方法将一个整体的响应式对象变成普通对象
// 展开(解包)得到单独的响应式数据
return {
...toRefs(data),
watchTitle,
watchAge,
changeAge,
changeYear,
};
},
};
</script>
<style>
#demo03 {
background-color: rgb(171, 223, 197);
}
</style>