setup是Vue3.0中一个新的配置项,值的类型为一个函数。setup是所有Composition API(组合API)的“表演舞台 ”。
setup函数在beforeCreate之前执行一次,this是undefined;也就是说在setup中不能使用this!
export default {
setup () {
console.log('this', this) // undefined
}
}
在vue2.x中数据需要定义在data中,方法需要定义在methods中。
在vue3.x中数据和方法直接定义在setup中即可。
那么哪些数据可以在模版中直接使用呢?
setup函数返回值若是为一个
对象,则该对象内的所有属性均可在模版中直接使用<template> <div class="test">{{ name }}</div> </template> <script> export default { setup () { const name = 'chaochao' return { name } } } </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
setup函数返回值若是一个渲染函数,则该页面的内容为渲染函数的值而非模版中的内容了。
<template> <div class="test">11111</div> </template> <script> export default { setup () { return ()=>{ return h('div', 'chaochao') } } } </script>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
tips: 模版中的内容被覆盖了!
注意:setup不能是一个async函数,因为async函数的返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
vue3.0中可以使用vue2.x的data、methods配置项吗?
在vue3.0中可以兼容之前的配置项,但是尽量不要与Vue2.x配置混用。若是混用:
setup (){
let 变量名 = 值
return {
变量名
}
}
直接通过上述语法声明变量,变量虽然可以在模版中使用,但是数据并不是响应式的,当数据更改时,vue并不会重新渲染数据;
若是想要数据是响应式的需要借助 ref函数或 reactive函数 进行双向数据绑定。
ref函数的作用是 定义一个响应式的数据;推荐在定义简单数据类型时使用;
let 变量名 = ref(initValue)
变量名.value{{变量名}}即可(内部做了封装,实际读取得是变量名.value)<template>
<div class="hello">
<h1>姓名:{{name}}h1>
div>
template>
<script>
import { ref } from 'vue'
export default {
setup () {
let name = ref('chaochao')
console.log('@@@', name) // reference对象,value属性值为真实的值
return {
name
}
}
}
</script>

Object.defineProperty()的get与set完成的。->实现原理传送门reactive函数转为代理对象reactive函数的作用是 定义一个引用类型的响应式数据(基本类型使用ref函数)
const 代理对象= reactive(源对象)
在Vue3中如果对一个reactive对象重新赋值,会导致失去响应式。
<template>
<div class="test">
{{ info.name }}
<br>
{{ info.age }}
<br>
<button @click="editinfo">修改</button>
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
setup () {
let info = reactive({
name:'chaochao',
age:18
})
function editinfo () {
info = reactive({
name:'niuniu',
age:20
})
console.log('info', info)
}
return {
info,
editinfo
}
}
}
</script>
上述示例中点击按钮页面没有任何变化(虽然打印的info数据发生了变化)
这是因为reactive是使用了Proxy来实现响应式,而Proxy只能监听对象的属性访问和修改,而不能监听对象本身的赋值操作!
若是想要修改对象的值应该怎么做呢? 可以修改对象单个的属性
// 此时点击按钮时页面内容随之改变
function editinfo () {
info.name = 'niuniu'
info.age = 20
console.log('info', info)
}
但是很多情况下我们修改对象都是后端直接返回来的数据,字段很多,这样修改比较麻烦,可以使用ref来定义对象。
export default {
setup () {
let info = ref({
name:'chaochao',
age:18
})
function editinfo () {
info.value = {
name: 'niuniu',
age: 20
}
}
return {
info,
editinfo
}
}
}
因为修改ref对象修改的是value属性值,可以直接修改(数据还是响应式的)!
reactive转为代理对象。Object.defineProperty()的get与set来实现响应式(数据劫持)。Proxy来实现响应式(数据劫持)。.value,读取数据时模板中直接读取不需要.value。.value。tips: 个人感觉在3.x中2.x中的生命周期函数毫无用处->获取不到setup中的数据根本进行不了任何操作!
在vue3中侦听器被抽取为一个组合API,使用之前需要先引入
<script>
// [1]在vue3中 watch是一个组合式API,使用之前需要先引入
import { watch } from 'vue';
export default{
setup(){
// [2]开启watch侦听
/*
参数1:变量(监听变量),
参数2:Function(逻辑代码)
参数3:Object:配置项
newVal: 该变量更改前的值
oldVal:该变量更改后的值
*/
watch(监听变量,(newVal,oldVal)=>{
// 逻辑代码
}, {
// 配置项
})
}
}
</script>
年龄:age
按钮(点击年龄➕1)
注:监听打印年龄
<template>
<h4>年龄:{{age}}</h4>
<button @click="age++">点我年龄增加</button>
</template>
<script>
// [1]在vue3中 watch是一个组合式API,使用之前需要先引入
import { ref, watch } from 'vue';
export default{
setup(){
let age = ref(18)
watch(age,(newVal,oldVal)=>{
console.log('@@@', newVal, oldVal)
},{
immediate:true
})
return {
age
}
}
}
</script>

tips: 实际上监听的并不是18这个数值的变化,而是监听的ref实例对象的变化,只要是任意一个属性变化都会触发watch监听!
当监听多个ref定义的数据时,可以使用[1]中语法使用两个watch函数进行监听,也可以使用下列语法
/*
参数1:Array(监听变量),
参数2:Function(逻辑代码)
参数3:Object:配置项
newVal: [变量1更改前的值,变量2更改前的值]
oldVal:[变量1更改后的值,变量2更改后的值]
*/
watch([变量1,变量2], (newVal,oldVal)=>{
console.log('@@@', newVal, oldVal)
// 逻辑代码
},{
// 配置项
})
<script>
// [1]在vue3中 watch是一个组合式API,使用之前需要先引入
import { watch } from 'vue';
export default{
setup(){
// [2]开启watch侦听
/*
参数1:变量(监听变量),
参数2:Function(逻辑代码)
参数3:Object:配置项
newVal: 该变量更改前的值
oldVal:该变量更改后的值
*/
watch(监听变量,(newVal,oldVal)=>{
// 逻辑代码
}, {
// 配置项
})
}
}
</script>
无法正确获得oldValue!!强制开启了深度监视且无法关闭(deep:false失效)<script>
// [1]在vue3中 watch是一个组合式API,使用之前需要先引入
import { watch } from 'vue';
export default{
setup(){
// [2]开启watch侦听
/*
参数1:Function(监听变量),
参数2:Function(逻辑代码)
参数3:Object:配置项
newVal: 该变量更改前的值
oldVal:该变量更改后的值
*/
watch(()=> 变量.属性,(newVal,oldVal)=>{
// 逻辑代码
}, {
// 配置项
})
}
}
</script>
<script>
// [1]在vue3中 watch是一个组合式API,使用之前需要先引入
import { watch } from 'vue';
export default{
setup(){
// [2]开启watch侦听
/*
参数1:Array(监听变量),
参数2:Function(逻辑代码)
参数3:Object:配置项
newVal: [变量1更改前的值,变量2更改前的值]
oldVal: [变量1更改后的值,变量2更改后的值]
*/
watch([()=> 变量.属性,()=> 变量.属性],(newVal,oldVal)=>{
// 逻辑代码
}, {
// 配置项
})
}
}
</script>
<template>
<h4>年龄:{{info.age}}</h4>
<button @click="info.age++">点我年龄增加</button>
<h4>sonplay:{{info.sonInfo.play.car}}</h4>
<button @click="info.sonInfo.play.car+='~'">点我修改</button>
</template>
<script>
// [1]在vue3中 watch是一个组合式API,使用之前需要先引入
import { reactive, ref, watch } from 'vue';
export default{
setup(){
let info =reactive({
age:18,
say:'hello',
sonInfo:{
age:2,
play:{
car:'汽车真好玩'
}
}
})
watch([()=> info.age,()=> info.sonInfo], (newVal,oldVal)=>{
// 当点击 点我年龄增加 按钮时 会打印, 当点击 点我修改 时不会打印
/*原因:
只有监听reactive定义的对象的全部属性时才会强制开始深度监听
当监听reactive定义的对象的某个属性时并没有开启强制刷新
此时 deep:true还有效
只要在配置项中进行配置 点击上述两个按钮的任意一个都会触发watch监听了!
*/
console.log('@@@', newVal, oldVal)
})
return {
info
}
}
}
</script>
watch的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
在vue3中计算属性被抽取为一个组合API,使用之前需要先引入
import {computed} from 'vue'
export default {
setup(){
// computed的类型为一个函数
// [1]在页面中使用->需要传入一个回调函数
let res = computed(()=>{
return xxx. // return 的值就是计算属性的值
})
// [2]在页面中修改计算属性的值-> 去修改其他的值,就需使用get、set方法
let res = computed({
get(){
return xxx // return 的值就是计算属性的值
},
set(value){
// 对其他值进行操作
}
})
return {
res // 响应式的
}
}
}
下面以一个小例子说明vue3中计算属性的使用
姓:输入框
名:输入框
姓名:当用户输入姓、名时自动在此显示 姓 - 名 (实时改变)
实现:
<template>
姓:<input type="text" v-model="info.firstname">
<br>
名:<input type="text" v-model="info.lastname">
<br>
姓名:{{info.name}}
</template>
<script>
// [1]在vue3中 computed是一个组合式API,使用之前需要先引入
import { reactive, computed } from 'vue';
export default{
setup(){
let info = reactive({
firstname:'',
lastname:''
})
// [2]使用计算属性
info.name = computed(()=>{
return `${info.firstname}-${info.lastname}`
})
return {
info
}
}
}
</script>

姓:输入框
名:输入框
姓名:输入框
注意:当用户输入姓、名时自动在此显示 姓 - 名 (实时改变) ; 当修改姓名时 姓、名输入框也要实时改变
实现
<template>
姓:<input type="text" v-model="info.firstname">
<br>
名:<input type="text" v-model="info.lastname">
<br>
姓名:<input type="text" v-model="info.name">
</template>
<script>
// [1]在vue3中 computed是一个组合式API,使用之前需要先引入
import { reactive, computed } from 'vue';
export default{
setup(){
let info = reactive({
firstname:'',
lastname:''
})
info.name = computed({
get(){
return `${info.firstname}-${info.lastname}`
},
set(value){
const arr = value.split('-')
info.firstname = arr[0]
info.lastname = arr[1]
}
})
return {
info
}
}
}
</script>

函数,把setup函数中使用的Composition API进行了封装当用户点击页面时获取鼠标点击的点的坐标;
<template>
<h1> 当前鼠标点击的坐标为point({{point.x}},{{point.y}})</h1>
</template>
<script>
import { onBeforeUnmount, onMounted, reactive } from 'vue';
export default{
setup(){
// 数据
let point = reactive({
x:0,
y:0
})
// 方法
function getpoint(event){
point.x = event.pageX,
point.y = event.pageY
}
// 生命周期
onMounted(()=>{
window.addEventListener('click', getpoint)
})
// 需要在页面注销时解除事件绑定,不然关闭当前页面之后点击页面还是会执行getpoint方法
onBeforeUnmount(()=>{
window.removeEventListener('click', getpoint)
})
return {
point
}
}
}
</script>

但是这个功能不只是在该组件被使用,在页面的其他组件中也使用到了;
当然可以把代码复制过去,但是最简单的是将代码写在hook中,哪里使用在哪里引入
hook文件 - usePoint
import { onBeforeUnmount, onMounted, reactive } from 'vue';
export default function savePoint(){
// 数据
let point = reactive({
x:0,
y:0
})
// 方法
function getpoint(event){
point.x = event.pageX,
point.y = event.pageY
}
// 生命周期
onMounted(()=>{
window.addEventListener('click', getpoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click', getpoint)
})
return point
}
组件
<template>
<h1> 当前鼠标点击的坐标为point({{point.x}},{{point.y}})</h1>
</template>
<script>
import usePoint from '@/hooks/usePoint'
export default{
setup(){
let point = usePoint()
return {
point
}
}
}
</script>
在vue3中创建路由实力化对象的方式与vue2相同,但是由于setup中不能使用this,因此在vue3中路由的跳转与访问还是有了一定的变化~



在vue2中会通过以下方式获取dom
<div ref='box'>div>
在vue3的setup中不能使用this,因此指定不能使用上述方式获取dom
onMounted(()=>{
console.log(11111, document.getElementById('box')) // null
})
<div ref='box'>div>
<script setup>
import { ref } from '@vue/reactivity';
const box = ref()
onMounted(()=>{
console.log(1111,box.value) //
})
</script>
tips: 若是没有使用setup语法糖,需要将
box变量return出去,否则将获取不到dom元素!
组件的获取方式与dom元素的获取方式相同, 只要是在setup中 return出来的属性和方法都可以获取到
const name = ref(null) // name与组件绑定的ref属性值一致
name.value.属性值. // 获取指定属性
name.value.方法名() // 调用指定方法
举例说明
<template>
<div>{{ info.name }}div>
template>
<script>
import { reactive } from 'vue'
export default{
setup(){
const info = reactive({
name:'chaochao',
sex:'nv'
})
const name = 'niuniu'
return{
info
}
}
}
script>
<template>
<div>
<test ref="box">test>
虽然说在setup中获取不到this,但是在某些情况下又不得不使用this。
在vue3中, 存在getCurrentInstance方法获取当前组件的实例,等同于this
import { getCurrentInstance } from 'vue'
<script setup>
import { getCurrentInstance } from 'vue'
const {ctx, proxy} = getCurrentInstance()
</script>
其实ctx 与proxy 都是实例化对象,都可以获取到全局变量,但是~
注意:虽然proxy能代表实例化对象,但是不要在setup中将proxy当作this使用(可以说是能不使用就不使用)
tips: getCurrentInstance 只能在 setup 或生命周期钩子中调用
在vue2.0中是将全局属性或方法挂在到Vue实力化对象的原型链上。
比如我们想添加一个全局属性,如下代码:
Vue.prototype.$pagehost = 'http://xxx'
在组件中使用,仅需如下:
this.$pagehost // 'http://xxx'
在vue3中是将数据添加在全局配置app.config.globalProperties中。
比如我们想添加一个全局属性,如下代码:
const app = createApp(App);
app.config.globalProperties.pagehost = 'http://xxx'
在组件中使用:
import { getCurrentInstance } from 'vue'
<script setup>
setup(){
const {ctx, proxy} = getCurrentInstance()
proxy.pagehost // 'http://xxx'
}
</script>
在vue3.2将setup封装为语法糖,将setup写在script标签上即可,语法如下:
<script setup>
...
</script>
这样写之后呢,setup不再是一个函数了,没有return了,因此数据的导出等等都发生了一系列的变化,整理如下:
<template>
<div>
{{ info.name }}
<button @click="editname">点我修改namebutton>
div>
template>
<script setup>
import { reactive } from 'vue'
const info = reactive({
name:'chaochao',
sex:'nv'
})
function editname(){
info.name = 'niuniu'
}
script>
如上代码在运行之后在页面显示文本“chaochao”,在点击按钮“点我修改name”之后页面文本变为"niuniu"
通过模板 ref 或者 $parent 链获取到的是组件的公开实例。
使用 语法糖的组件是默认关闭的,也即通过模板 ref 或者 $parent 链不会获取到任何在