搜索文档
英文:Composition API
setup
知识点
setup 是 Vue3 中新的配置项,值为一个函数。特别重要!!!
setup 是所有组合式 API
表演的舞台。setup 在 beforeCreate 之前执行一次,在 setup 方法中 this 的指向是 undefined。
setup 不能用 async 修饰。
组件中所用到的数据、方法、计算属性等,均要配置在 setup 中。
jsexport default { name: 'Vue3App', setup() { let name = '张三'; let age = 20; let nameClick = () => { console.log('this is name click function'); } return { name, age, nameClick }; } };
注意
- Vue3 配置中可以写 Vue2 的 data、methods 等,但不推荐。
- 若混合写,Vue3 不能访问到 Vue2 配置中的数据、方法等。Vue2 可以访问 Vue3 中的数据和方法。
- 若混合写,属性或方法名重复时,以 Vue3 为主。
setup 返回值
- 返回对象:对象中的数据、方法在模板中可以直接使用。
- 返回函数:略。
setup 参数
- props:值为对象。是由组件外部传递且在组件内部声明接收的属性。
- context:上下文对象。
- attrs:值为对象。是由组件外部传递且在组件内部 没有 声明接收的属性。
- slots:插槽内容。
- emit:分发自定义事件。
vue
<template>
<Demo name="iGma" :age="20" @info="emitInfo" />
</template>
<script>
import { defineAsyncComponent } from 'vue'; // 异步加载组件
export default {
name: 'Vue3App',
components: {
Demo: defineAsyncComponent(() => import('./components/Demo'))
},
setup() {
const emitInfo = (e) => {
console.log(e);
}
return { emitInfo }
}
};
</script>vue
<template>
<div @click="infoClick">
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
</div>
</template>
<script>
export default {
name: 'Vue3Demo',
props: ['name', 'age'],
setup(props, context) {
const infoClick = () => {
context.emit('info', props);
}
return { infoClick }
}
};
</script>
ref
- 作用:定义一个响应式数据。通过 ref 函数可以将一个数据加工成
引用对象,即 ref 函数的返回值是一个引入对象 —— RefImpl。 - ref 接受的数据可以是基本类型,也可以是对象类型。
- 基本类型数据响应式依然使用
Object.defineProperty()的get和set方法完成的。 - 对象类型的数据是借助 Vue3 内部的
reactive函数完成的。
- 基本类型数据响应式依然使用
- 语法
vue
<script>
import { ref } from 'vue'
export default {
name: 'Vue3App',
setup() {
// 定义
let name = ref('张三');
let age = ref(20);
let school = ref({
name: '内蒙古农业大学',
address: '内蒙古'
})
// 改变数据
let changeInfo = () => {
name.value = '李四';
age.value = 21;
school.value.name = '包头师范大学';
school.value.address = '包头'
}
return { name, age, school, changeInfo }
}
};
</script>reactive
- 作用:定义一个对象类型数据的响应式(基本数据类型不可以)。
- 语法:
const 代理对象 = reactive(源对象) - reactive 定义的响应式数据是深层次的,第 n 层数据发生变化时 Vue 也能监听到。
- 内部基于 ES6 的 Proxy 实现的,通过代理对象操作源对象内部数据。
vue
<script>
import { reactive } from 'vue'
export default {
name: 'Vue3App',
setup() {
// 定义
let data = reactive({
name: '张三',
age: 20,
school: {
name: '内蒙古农业大学',
address: '呼和浩特'
}
})
// 使用
let changeInfo = () => {
data.name = '李四';
data.age = 18;
data.school.name = '包头师范大学';
data.school.address = '包头';
}
return { data, changeInfo }
}
};
</script>响应式原理
Vue2 的响应式
实现原理:
- 对象类型通过
Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。 - 数组类型通过重写更新数组的一系列方法来实现拦截。
- 对象类型通过
$
jsthis.$set(对象, key, value); // 新增 or 修改对象属性 this.$delete(对象, key); // 删除对象属性存在的问题:
- 新增属性、删除属性,页面不更新。
- 直接通过下标改数组,页面也不更新。
Vue3 的响应式
通过 Proxy 代理对象拦截对象中任意属性的变化,包括读写、添加、删除等。
通过 Reflect 反射对象对源对象的属性进行操作。
jslet obj = { name: '张三', age: 20, } let p = new Proxy(obj, { get(obj, key) { console.log('读取数据'); return Reflect.get(obj, key); }, set(obj, key, value) { console.log('修改 or 新增数据'); Reflect.set(obj, key, value); }, deleteProperty(obj, key) { console.log('删除数据'); return Reflect.deleteProperty(obj, key); } });
计算属性
vue
<template>
单价:<input type="number" v-model="data.money" /><br>
数量:<input type="number" v-model="data.number" /><br>
<p>简单写法总计:{{ data.total }}</p>
<p>完整写法总计:{{ data.total_com }}</p>
</template>
<script>
import { computed, reactive } from 'vue';
export default {
name: 'Vue3Demo',
setup() {
const data = reactive({
money: 1,
number: 1
});
// 简写
data.total = computed(() => data.money * data.number);
// 完整写法
data.total_com = computed({
get: () => data.money * data.number,
set(value) {
}
});
return { data }
}
};
</script>监听属性
知识点
watch 方法有三个参数:
- 参数一:要监听的属性,可以是单个属性,也可以是多个属性,多个属性用数组。类型(ref 对象,reactive 对象)
- 参数二:回调函数。
- 参数三:配置对象。
案例
监听单个 ref 数据
vue
<template>
<h3 @click="sum++">sum: {{ sum }}</h3>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'Vue3Demo',
setup() {
let sum = ref(0);
// 监听单个 ref 数据
watch(sum, (n,o) => {
console.log(`sum 新值为 ${n},旧值为 ${o}`);
});
return { sum }
}
};
</script>监听多个 ref 数据
vue
<template>
<h3 @click="sum++">sum: {{ sum }}</h3>
<h3 @click="msg += '!'">msg: {{ msg }}</h3>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'Vue3Demo',
setup() {
let sum = ref(0);
let msg = ref('Hello');
// 监听多个 ref 数据
watch([sum, msg], (n, o) => {
console.log(`数据发生变化`, n, o);
})
return { sum, msg }
}
};
</script>监听 reactive 数据的全部属性
- 坑 1:此处无法正确获取 oldValue。
- 坑 2:强制开启了深度监听,deep 配置无效。
vue
<template>
<h3>姓名:{{ data.name }}</h3>
<h3>年龄:{{ data.age }}</h3>
<h3>Job:{{ data.job.a }}</h3>
<button @click="data.age++">age++</button>
<button @click="data.job.a++">job.a++</button>
</template>
<script>
import { reactive, watch } from 'vue';
export default {
name: 'Vue3App',
setup() {
const data = reactive({
name: 'iGma',
age: 20,
job: {
a: 10
}
});
watch(data, (nv, ov) => {
console.log(nv, ov);
})
return { data };
},
};
</script>监听 reactive 数据的某个属性
watch 方法的第一个参数是一个带有返回值的方法,切返回值是要监听的数据。
vue
<script>
import { reactive, watch } from 'vue'
export default {
name: 'Vue3Demo',
setup() {
let info = reactive({
name: '张三',
age: 10,
});
watch(() => info.age,(n, o) => {
console.log(n, o);
})
return { info }
}
};
</script>监听 reactive 数据的某些属性
vue
<script>
import { reactive, watch } from 'vue'
export default {
name: 'Vue3Demo',
setup() {
let info = reactive({
name: '张三',
age: 10,
school: {
uc: {
name: '内蒙古农业大学'
}
}
});
watch([() => info.age, () => info.name],(n, o) => {
console.log(n, o);
})
return { info }
}
};
</script>监听 reactive 数据的某个对象类型的属性(deep 有效,oldValue 无效 )
vue
<script>
import { reactive, watch } from 'vue'
export default {
name: 'Vue3Demo',
setup() {
let info = reactive({
name: '张三',
age: 10,
school: {
uc: {
name: '内蒙古农业大学'
}
}
});
watch(() => info.school,(n, o) => {
console.log(n, o);
}, { deep: true })
return { info }
}
};
</script>智能监听 watchEffect
- watchEffect 方法中用到的属性即是要监听的属性。
- watchEffect 方法和 computed 有点像,不同的是 computed 注重的是计算出来的值,而 watchEffect 更注重的是计算的过程。
vue
<script>
import { reactive, watchEffect } from 'vue'
export default {
name: 'Vue3Demo',
setup() {
let info = reactive({
name: '张三',
age: 10,
school: {
uc: {
name: '内蒙古农业大学'
}
}
});
watchEffect(() => {
let { age } = info;
let { name } = info.school.uc;
console.log('监听 info.age 和 info.school.uc.name 属性的变化');
})
return { info }
}
};
</script>生命周期

配置项写法
vue
<template>
<button @click="show = !show">显示 or 隐藏 Demo 组件</button>
<Demo v-if="show" />
</template>
<script>
import { defineAsyncComponent, ref } from 'vue';
export default {
name: 'Vue3Demo',
components: {
Demo: defineAsyncComponent(() => import('./components/Demo'))
},
setup() {
let show = ref(true);
return { show };
},
};
</script>vue
<template>
<h2 @click="sum++">sum: {{ sum }}</h2>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'Vue3Demo',
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 写法
- 组合式 API 的执行早于配置项写法
- 对应关系
| 配置项 | 组合式 API |
|---|---|
| beforeCreate | setup() |
| created | setup() |
| beforeMount | onBeforeMount() |
| mounted | onMounted() |
| beforeUpdate | onBeforeUpdate() |
| updated | onUpdated() |
| beforeUnmount | onBeforeUnmount() |
| unmounted | onUnmounted() |
vue
<template>
<h2 @click="sum++">sum: {{ sum }}</h2>
</template>
<script>
import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
export default {
name: 'Vue3Demo',
setup() {
let sum = ref(0);
console.log('---setup---');
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
- Hook 本质是一个函数,它可以把 setup 中使用到的组合式 API 进行封装,方便其他组件复用。
- 类似于 Vue2 中的 mixin。
- 创建
/src/hooks目录,用来存放自定义 Hook 文件。
js
import { ref } from "vue"
export default () => {
let page = ref(0);
let pagesize = ref(10);
// 获取列表
const getList = () => {
console.log('发送网络请求');
};
// 点击分页
const changePage = (e) => {
console.log(`changePage 接收到参数 e = ${e}`);
page.value = e;
getList();
};
return { page, pagesize, getList, changePage }
}vue
<template>
<h3>当前页码:{{ page }}</h3>
<h3>单页数据量:{{ pagesize }}</h3>
<button @click="changePage(2)">改变页码</button>
<button @click="getList">发送网络请求</button>
</template>
<script>
import usePages from "../hooks/usePage";
export default {
name: 'Vue3Demo',
setup() {
return { ...usePages() }
},
};
</script>toRef 与 toRefs
- 作用:创建一个 ref 对象,其 value 值指向另一个对象中的某个属性。
- 语法:
const name = toRef(object, key) - 应用:要将响应式对象中的某个属性单独提供给外部使用,其保留响应式。
vue
<template>
<h3>姓名:{{ name }}</h3>
<h3>年龄:{{ age }}</h3>
<h3>工资:{{ k }}</h3>
<button @click="name += '~'">改变 name</button>
<button @click="age++">改变 age</button>
<button @click="k++">改变 k</button>
</template>
<script>
import {onUpdated, reactive, toRef, toRefs} from 'vue';
export default {
name: 'Vue3Demo',
setup() {
const data = reactive({
name: 'iGma',
age: 20,
job: {
k: 10
}
});
onUpdated(() => {
console.log(data);
})
return {
k: toRef(data.job, 'k'),
...toRefs(data)
}
},
};
</script>