Vue3使用setup、ref和reactive 
1. 初识setup函数 
- 理解: 它是Vue3中新的一个配置项, 值为函数
- setup是所有的Composetion API(组合API)"表演的舞台"
- 组件中所用到: 数据方法等等,均要配置到setup中。
- setup函数的两种返回值: - 若返回一个对象,则对象中的属性、方法,在模版中局可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。(了解)
 
- 注意点: - 尽量不要与Vue2.x配置混用,有下面奇怪的情形出现: - Vue2.x配置(data,methods、computed...)中可以访问到setup中的属性,方法。
- 但在setup中不能访问到Vue2.x配置(data, methods、computed...)。
- 如果有重名,setup优先
 
- setup不能是一个async函数,因为返回值不再是return的对象,而是promise, Vue不会自动解析then中的值,自然模版看不到return对象中的属性。
 提示 例外:用到 <Suspense>和异步组件配合使用时可以return一个Promise实例对象,即此时是异步的
- 尽量不要与Vue2.x配置混用,有下面奇怪的情形出现: 
2. setup函数深入理解 
- setup执行的时机:在beforeCreate之前执行一次,this的值是undefined
- setup的参数: - props: 值为对象,包含组件外部传递过来,且组件内部声明接收了的属性
- context(上下文对象): - attrs: 值为对象,包含组件外部传递过来,但没有在props配置中声明的属性
- slots: 收到的插槽内容,相当于this.$slots
- emit: 分发自定义事件的函数, 相当于this.$emit
 
 
在Vue3中:
js
<script>
  export default {
    name: 'Demo',
    beforeCreate(){
        console.log('------------beforeCreate-------------')
    },
    // Vue3中提供两个参数方便调用,避免没有this使用影响,需要重点关注props和context
    setup(props, context){ 
        console.log('------------setup-------------', this);
    }
  }
</script>执行结果: 
提示
因此在Vue3中不用关心是否能写箭头函数了,因为已经没有了this,箭头函数中现在不用写this避免undefined出现。
- context参数attrs源自Vue2的$attrs, context参数slots源自Vue2中的$slots
2.1 回顾Vue2中的attrs和slots 
$attrs用来保存父组件传入子组件的属性信息,子组件$attrs会自动接收,它和props互斥,在props中定义的会在$attrs移除 在Vue2中About.vue代码如下:
vue
<template>
  <div>
   <About msg="早上好" name="jack">
     <span>在广州打工</span>
     <span>在成都打工</span>
   </About>
  </div>
</template>可以查看控制台打印About.vue的实例对象查看上面的属性: 其中$slots中存放了两个<span>的虚拟节点, default表示为默认插槽。  带名字的插槽传入
 带名字的插槽传入
vue
<template>
  <div>
   <About msg="早上好" name="jack">
     <template v-slot:test1>
       <span>在广州打工</span>
     </template>
     <template v-slot:test2>
       <span>在成都打工</span>
     </template>
   </About>
  </div>
</template> 可见出现两个带有名称的插槽在$slots上面。
 可见出现两个带有名称的插槽在$slots上面。
2.2 分析props对象 
在Vue3中父组件App.vue传入的数据:"你好啊"
vue
<template>
  <Demo hello="你好啊"></Demo>
</template>
<script>
  import { ref, reactive} from 'vue'
  import Demo from './components/Demo.vue'
  export default {
    name: 'App',
    components: {Demo},
    setup(props, context) {
      
    } 
}   
</script>vue
<template>
    <div>{{ hello }}</div>
</template>
<script>
import { ref } from 'vue'
export default {
    name: 'Demo',
    setup(props, context) {
        console.log('------------setup-------------', props);
    }
}
</script>运行会发现警告:提示我们没有使用传值变量hello,分析console打印发现props里面什么也没有,说明Vue3中参数props传入进来并没有自动包含携带父传子数据  这时需要
 这时需要props属性,Vue3中依然有效,修改Demo.vue:
vue
<template>
    <div>{{ hello }}</div>
</template>
<script>
import { ref } from 'vue'
export default {
    name: 'Demo',
    props: ['hello'], 
    setup(props, context) {
        console.log('------------setup-------------', props);
    }
}
</script>运行警告消失, props数据出来了: 
2.2 分析context对象 
打印context对象如下,有四大属性attrs、emit、expose、slots 
vue
<template>
    <div>{{ hello }}</div>
    <button @click="invokeWalk">触发自定义事件</button>
    <slot></slot>
</template>
<script>
import { ref } from 'vue'
export default {
    name: 'Demo',
    props: ['hello'],
    emits: ['walkCode'],  // Vue3需要在子组件中申明使用自定义函数,不然会有警告
    setup(props, context) {
        console.log('------------context-------------', context)
        console.log('attrs', context.attrs); // 由于配置props: ['hello'],attrs中也就没有了hello值
        console.log('slots', context.slots);  // slots不会随着子组件中配置了<slot></slot>,就会消失里面的值
        console.log('emit', context.emit);   
        let invokeWalk = ()=>{
            context.emit('walkCode', 'jack')  
        }
        return {
            invokeWalk
        }    
    }
}
</script>vue
<template>
  <Demo hello="你好啊" @walkCode="goWalkCode">
    <span>我是自定义Html代码</span>
  </Demo>
</template>
<script>
  import { ref, reactive} from 'vue'
  import Demo from './components/Demo.vue'
  export default {
    name: 'App',
    components: {Demo},
    setup(props, context) {
      let goWalkCode=(name)=>{
        alert(`继续搞java, ${name}`)
      }
      return {
        goWalkCode
      }
    } 
}
</script>2.3 setup返回数据误区 
- 不具有响应式,只在第一次页面正常显示,点击按钮页面不变。
js
return {
  name: person.name, 
  age: person.age, 
  salary: person.job.j1.salary, 
}- 虽然具有响应式,但是数据已经没有和person关联,person的数据不再受到变化,响应式的数据只是return产生的新数据,不能在返回数据中加入ref。
js
return {
  name: ref(person.name), 
  age: ref(person.age), 
  salary: ref(person.job.j1.salary) 
}3. ref函数 
不同于Vue2中的ref类似id的效果,获取组件实例对象,Vue3中的ref是一个函数
- 作用: 定义一个响应式的数据
- 语法: const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象)
- JS中操作数据: xxx.value
- 模版中数据读取:不需要.value得到值,直接{{xxx}}即可,Vue3读取模版插值的时候会判断是RefImpl类型自动读取value属性。
- 接收的数据可以是: 基本类型、也可以是对象类型。
- 基本类型的数据: 响应式依然是靠Object.defineProperties()的get与set的实现, 是RefImpl类型数据
- 对象类型的数据: 内部"求助"了Vue3.x中的新函数--reactive函数, 是Proxy类型数据
4. reactive函数 
- 作用: 定义一个对象类型的响应式数据(基本类型不要使用它,要用ref函数)
- 语法: const 代理对象 = reactive(源对象)接收一个对象(或数组), 返回一个代理对象,返回一个代理对象(Proxy的实例对象, 简称proxy对象)
- reactive定义的响应式数据是深层次的
- 内部基于ES6的proxy实现,通过代理对象操作源对象内部数据进行操作。
5. 对比ref和reactive 
- 从定义数据角度对比: - ref用来定义: 基本类型数据。
- reative用来定义: 对象(或数组)类型数据
- 备注: ref也可以定义对象(或数组)类型数据, 它内部会自动reactive转为代理对象。
 
- 从原理角度对比: - ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)
- reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
 
- 从使用角度对比: - ref定义的数据: 操作数据需要.value, 读取数据时模版中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据: 均不需要.value
 
