Vue3、setup的使用

1、setup的使用

1.1、简介

setup选项是一个接受props和context的函数,也就是说它的基本写法应该是这样的:

export default {
    
    
  name: 'test',
  setup(props, context){
    
    
    return {
    
    } // 这里返回的任何内容都可以用于组件的其余部分
  }
}

接受一个props和context函数并且将setup内的内容通过return暴露给组件的其余部分。

1.2、setup注意点

由于在执行setup函数的时候,还没执行created生命周期方法,所以在setup函数中,无法使用data和methods的变量和方法。
由于我们不能在setup函数中使用data和methods,所以Vue为了避免我们错误的使用,直接将setup函数中的this修改成了undefined。

1.3、定义响应式数据

refreactive

  • ref我们用来将基本数据类型定义为响应式数据,其本质是基于Object.defineProperty()重新定义属性的方式来实现(ref更适合定义基本数据类型)
  • reactive用来将引用类型定义为响应式数据,其本质是基于Proxy实现对象代理。
  • 基本数据类型(单类型):除Object。String、Number、boolean、null、undefined。
  • 引用数据类型:object。里面包含的function、Array、Date。
  • 定义:
<script>
import {
    
     ref, reactive } from "vue";
export default {
    
    
  name: "test",
  setup(){
    
    
    // 基本类型
    const nub = ref(0)
    const str = ref('inline')
    const boo = ref(false)
    // 引用类型
    const obj = reactive({
    
    
      name: 'inline',
      age: '18'
    })
    const arr = reactive(['0', '1', '2'])
    
    return {
    
    
      nub,
      str,
      boo,
      obj,
      arr
    }
  }
}
</script>
  • 使用:
<template>
  <div>
    <h1>基本类型</h1>
    <p>nub:{
    
    {
    
     nub }}</p>
    <p>str:{
    
    {
    
     str }}</p>
    <p>boo:{
    
    {
    
     boo }}</p>
    <h1>引用类型</h1>
    <p>obj:{
    
    {
    
     obj.name }}</p>
    <p>arr:{
    
    {
    
     arr[1] }}</p>
  </div>
</template>
  • 结果:
    在这里插入图片描述

1.4、toRefs

如果我们用reactive的形式来定义响应式变量

setup(){
    
    
  const obj = reactive({
    
    
    name: 'inline',
    gender: '男',
    age: '18'
  })
  return {
    
    
    obj
  }
}

使用:

<div>
  <p>姓名:{
    
    {
    
     obj.name }}</p>
  <p>性别:{
    
    {
    
     obj.gender }}</p>
  <p>年龄:{
    
    {
    
     obj.age }}</p>
</div>

这样我们是不是发现在模版内使用参数很麻烦,那我们想直接用{ { name }}的方式访问行不行,答案是不行的
这里我们使用es6的扩展运算符。

setup(){
    
    
  const obj = reactive({
    
    
    name: 'inline',
    gender: '男‘,
    age: '18',
  })
  return {
    
    
    ...obj
  }
}

使用:

<div>
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>性别:{
    
    {
    
     gender }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <button @click="name = 'lgg'">改变姓名</button>
  <button @click="gender = '未知'">改变性别</button>
  <button @click="age = '18'">改变年龄</button>
</div>

结果:
这里我们看到,参数都正常的显示在了页面上,但是我们去改变参数时发现视图并没有更新,why?
我们把扩展运算符写成它的等价格式

setup(){
    
    
  const obj = reactive({
    
    
    name: 'inline',
    gender: '男‘,
    age: '18',
  })
  return {
    
    
    ...obj ==> name:obj.name
  }
}

当鼠标浮动上去提示name只是个字符串?
在这里插入图片描述
那我们再看看我们用ref定义值时提示的是什么
在这里插入图片描述
这时我们看到name是一个Ref,是一个响应式字符串。
这时就知道了为什么没有更新视图了,当我们用…扩展运算符时我们得到的只是一个普通类型的数值,并不是一个响应式数据
问了解决这个问题,vue3给我们提供了**toRefs函数,来让我们看看效果如何:

setup(){
    
    
  const obj = reactive({
    
    
    name: 'inline',
    gender: '男'age: '18'
  })
  return {
    
    
    ...toRefs(obj)
  }
}
<div>
  <p>姓名:{
    
    {
    
     name }}</p>
  <p>性别:{
    
    {
    
     gender }}</p>
  <p>年龄:{
    
    {
    
     age }}</p>
  <button @click="name = 'lgg'">改变姓名</button>
  <button @click="gender = '未知'">改变性别</button>
  <button @click="age = '18'">改变年龄</button>
</div>

参数都可以正常改变,成功改头换面。

toRefs总结:
toRefs会将一个响应式的对象转变为一个普通对象,然后将这个普通对象里的每一个属性变为一个响应式的数据

1.5、setup中执行方法

1.5.1、方式一:

以reactive定义响应式数据的方式来定义方法:

<script>
  import {
    
     ref, reactive, toRefs } from 'vue';
  export default {
    
    
    name: 'test',
    setup(){
    
    
      const str = ref('inline')
      const fun = reactive({
    
    
        fun1(data) {
    
    
          console.log(str.value)
          this.fun2(data)
        },
        fun2(data) {
    
    
          console.log(data)
          console.log('我是fun2')
        }
      })
      return {
    
    
        ...toRefs(fun)
      }
    }
  }
</script>

通过点击事件将值传给fun1,fun1接受到后再传给fun2。
这里我们用this.fun2()的方式去调用fun2,为什么这里用this可以正常执行不会报undefind,因为这里的this非彼this,Vue2里的this是实例,这里的this是对象

<button @click="fun1('你还')">点我</button>

结果在这里插入图片描述

1.5.2、方式二:

注意这里调用fun2的方式与方式一不同,直接调用就可以,不用this调用。

export default {
    
    
  name: 'test',
  setup(){
    
    
    const fun1 = (data) => {
    
    
      fun2(data)
    }
    const fun2 = (data) => {
    
    
      console.log(data)
    }
    return {
    
    
      fun1
    }
  }
}

调用

<button @click="fun1('你好 inline')">点我1</button>

结果
在这里插入图片描述

1.5.3、方式三:

这种方式避免了将功能逻辑都堆叠在setup的问题,我们可以将独立的功能写成单独的函数,这里我在setup外写了fun()login()两个功能函数,并在setup内分别调用。

import {
    
     ref, reactive, toRefs } from 'vue';
export default {
    
    
  name: 'test',
  setup(){
    
    
    const test1 = fun() // 如果函数返回的参数过多,可以赋值给变量并用扩展运算符暴露给组件的其余部分。
    const {
    
     test } = login() // 也可单个接受
    return {
    
    
      ...toRefs(test1),
      test
    }
  }
}
// 功能1
function fun() {
    
    
  let str = ref('我是功能1')
  function fun1(data) {
    
    
    console.log(str.value)
    fun2(data)
  }
  function fun2(data) {
    
    
    console.log(data)
  }
  return{
    
    
    fun1,
    fun2
  }
}
// 功能2
function login() {
    
    
  const obj = reactive({
    
    
    msg: '我是功能2'
  })
  function test() {
    
    
    console.log(obj.msg)
  }
  return{
    
    
    test
  }
}

调用:

<button @click="fun1(你好 inline)">点击1</button>
<button @click="test">点击2</button>

1.5.4、方式四:

与方式三一样,只是我们将两个功能函数提取出来放在单独的.js文件中:
在这里插入图片描述
然后引入组件,并在setup内调用

<template>
  <div>
    <button @click="fun1('你好 inline')">点击1</button>
    <button @click="test">点击2</button>
  </div>
</tempalte>
<script>
import {
    
     ref, reactive, toRefs } from 'vue';
import {
    
     fun, login } from './test.js'
export default {
    
    
  name: "test",
  setup(){
    
    
    const test1 = fun()
    const {
    
     test } = login()
  }
  return{
    
    
    ...toRefs(test1),
    test
  }
}
</script>

1.5.5、方式五:

我们还可以这样写,这里我定义一个reactive响应式对象,赋值给login变量,这个响应式对像内有我们登录需要的参数、验证和方法,这里我们全部放在login这个响应式对象内然后用toRefs及其扩展运算符暴露出去。

<script>
import {
    
     ref, reactive, toRefs } from "vue";
export default {
    
    
  name: "test",
  setup(){
    
    
    const login = reactive({
    
    
      param: {
    
    
        username: '123',
        password: '123456',
      },
      rules: {
    
    
        username: [{
    
    
          required: true,
          message: '请输入用户名',
          trigger: 'blur'
        }],
        password: [{
    
    
          required: true,
          message: '请输入密码',
          trigger: 'blur'
        }]
      },
      login(){
    
    
        this.param.username = 'inline'
        this.param.password = '123456'
        console.log('登录成功!‘)
      }
    })
    return{
    
    
      ...toRefs
    }
  }
}
</script>

使用

<input type="text" v-model="param.username">
<input type="password" v-model="param.password">
<button @click="login">登录</button>

结果:
在这里插入图片描述
正常执行,所以我们还可以将一个功能的所有方法和相关参数写在一个reactive对像内

1.6、script setup:

script setup已经在vue3.2的版本上正式发布
用法

<script setup></script>
  • 变量方法无需return
  • 使用<script setup》时,模版被编译成一个内联在setup函数作用域内的渲染函数。这意味着内部声明的任何顶级绑定<script setup》都可以直接在模版中使用
<script setup>
  const msg = 'hello!'
</script>
<template>
  <div>{
    
    {
    
     msg }}</div>
</template>

script setup内定义的变量和方法无需返回,可以直接使用

  • 组件引入:
    导入的组件无需注册,可直接使用
<script setup>
// 导入的组件也可以直接在模版中使用
import Foo from './Foo.vue'
import {
    
     ref } from 'vue' 
// 编写合成API代码,就像在正常设置中一样
// 不需要手动返回所有内容
const count = ref(0)
const inc = () => {
    
    
  count.value++
}
</script>
<template>
  <Foo :count="count" @click="inc">
</template>
  • 发布Props和Emits
<script setup>
  const props = defineProps({
    
     foo: String })
  const emit = defineEmits(['update', 'delete'])
</script>
  • 普通script和script setup:
    script setup 可以和script同时存在
<script>
  export const name = 1
</script>
<script setup>
import {
    
     ref } from 'vue'
const count = ref(0)
</script>
  • script setup 附加选项
    script setup给提供了大多数与options api等效的能力;
    就是说options api能办到的事script setup大部分都能办到。
    那还有哪些是script setup做不到的呢?
  1. name
  2. inheritAttrs
  3. 插件或库所需要的自定义选项
    怎么办?分开写:
<script>
export default {
    
    
  name: 'CustomName', // 自定义名称
  inheritAttrs: false, // 继承属性
  customOptions: {
    
    } // 自定义选项
}
</script>
<script setup></script>

1.7、defineExpose:

script setup定义的变量默认不会暴露出去,因为变量这时候包含在setup的闭包中。这时我们可以使用defineExpose({})来暴露组件内部属性给父组件使用:

<script setup>
const a = 1
const b = ref(2)
defineExpose({
    
    
  a, 
  b
})
</script>

当父组件通过模版引用获取此组件的实例时,检索到的实例将会是这样{ a: number, b: number } (引用会像在普通实例上一样自动展开)

2、ref、reactive、toRef、toRefs的作用及区别

2.1、reactive

reactive用于为对象添加响应式状态。
接受一个js对象作为参数,返回一个具有响应式状态的副本

  • 获取数据值的时候直接获取,不需要加.value
  • 参数只能传入对象类型。
import {
    
     reactive } from 'vue'

// 响应式状态
const state = reactive({
    
    
  count: 0
})
// 打印count的值
console.log(state.count)

2.2、ref

ref用于为数据添加响应式状态。
由于reactive只能传入对象类型的参数,而对于基本数据类型要添加响应式状态就只能用ref了,同样返回一个具有响应式状态的副本

  • setup获取数据值的时候需要加.value,template则会自动带入.value。(对于基本类型,ref是自己的实现方式且性能优于reactive,而对于对象类型,ref可以理解为是通过reactive包装实现的)
  • 参数可以传递任意数据类型,传递对象类型时也能保持深度响应式,所以适用性更广。
  • vue3.0 setup里定义数据时优先使用ref,方便逻辑拆分和业务解耦
import {
    
     ref } from 'vue'

// 为基本数据类型添加响应式状态
const name = ref('lgg')

// 为复杂数据类型添加响应式状态
const state = ref({
    
    
  count: 0
})

// 打印name的值
console.log(name.value)
// 打印count的值
console.log(state.value.count)

2.3、toRef

toRef用于为**源响应式对象上的属性**新建一个ref,从而保持对其源对象属性的响应式连接。
接受两个参数:1、源响应式对象;2、属性名。返回一个ref数据。
例如:使用父组件传递的props数据时,要引用props数据时,要引用props的某个属性且要保持响应式连接时就很有用。

  • 获取数据值的时候需要加.value。
  • toRef后的ref数据如果是复杂类型数据时,不是原始数据的拷贝,而是引用,改变结果数据的值也会同时改变原始数据。
import {
    
     defineComponent, toRef } from 'vue'
export default defineComponent({
    
    
  props: [title],

  setup(props) {
    
    
    // 创建变量myTitle
    const myTitle = toRef(props, 'title')

    console.log(myTitle.value)
  }
})

2.4、toRefs

toRefs用于将响应式对象转换为结果对象,其中结果对象的每个属性都是指向原始对象响应的属性的ref。
常用于es6的解构赋值操作,因为在对一个响应式对象直接解构时解构后的数据将不再有响应式,而使用toRefs可以方便解决这一问题。

  • 获取数据值的时候需要加.value。
  • toRefs后的ref数据如果是复杂类型数据时,不是原始数据的拷贝,而是引用,改变结果数据的值,也会同时改变原始数据
  • 作用其实和toRef类似,只不过toRef是对一个个属性手动赋值,而toRefs是自动解构赋值。
import {
    
     defineComponent, toRefs} from 'vue'
export default defineComponent({
    
    
  props: [title],
  setup(props) {
    
    
    // 使用了解构赋值语法创建了变量myTitle
    const {
    
     myTitle } = toRefs(props)
    
    console.log(myTitle.value)
  }
})

提示:尽量不要混着用,reactive和ref选一种,toRef和toRefs选一种,不然代码会很乱。(推荐ref和toRefs)

猜你喜欢

转载自blog.csdn.net/weixin_44767973/article/details/125146023