【14.0】Vue3之函数

发布时间 2023-08-07 08:33:24作者: Chimengmeng

【一】setup函数

  • setup函数是用来替代Vue 2中的datamethods等选项的。
    • 它是一个特殊的函数,它会在组件实例创建之前被调用,用于初始化组件的状态和行为
  • setup函数可以定义变量和函数,并且可以使用ES6的letconst语法来声明变量。
    • setup函数内部定义的变量和函数都是局部的,不会暴露给模板或其他组件
    • 可以定义函数,可以定义匿名函数
  • 如果想要在模板中使用setup函数内部定义的变量,必须将其通过return语句返回,才可以在模板中使用它们
  • setup函数内部定义的变量默认不具有响应性。如果希望某个变量具有响应性,需要使用ref()函数将其包裹起来。
    • 在示例代码中,age没有使用ref()包裹,因此在addAge函数中对age的修改不会触发组件重新渲染
  • setup函数内部可以使用datamethods等选项中定义的属性和方法
    • 但是setup函数会先于这些选项执行,所以如果在data中定义了与setup中相同的变量,setup中的定义会覆盖它。
    • 同样地,如果在模板中使用某个变量或方法,优先使用setup中的定义
  • setup函数是在组件实例创建之前执行的
    • 因此在setup函数内部无法访问到this,也不能使用this关键字。
    • 如果需要获取组件实例,请使用getCurrentInstance()函数
<script>
export default {
  // Vue2中的写法
  name: "HomeView",
  // Vue2在 data 中定义变量
  data() {
    return {}
  },
  methods: {},
  // Vue3将所有内容写在 setup 函数内
  setup() {
    // var: ES5老语法(不建议使用)  let: ES6新语法  const: 定义常量

    // (1)定义变量和函数 ----(不会触发响应式)
    let name = "dream"
    let age = 19

    const addAge = () => {
      age += 1;
      alert(age)
    }
    // setup 必须将变量 return 出去才能看到定义的 值
    return {name, age, addAge}
  }
}
</script>

【二】ref函数

  • 在Vue 3中,ref函数是用来包裹变量,以实现响应式的效果。
    • 它可以使普通的JavaScript变量变成响应式的数据,并且可以通过.value来访问和修改内部的值
  • 导入ref函数:
    • 在使用ref函数之前,需要从Vue库中导入它。
    • 在示例代码中使用了import {ref} from "vue"来导入ref函数。
  • 使用ref函数包裹变量:
    • 在示例代码中,nameage都被用ref函数包裹起来,变成了响应式的数据。
    • 包裹后的变量在ref函数内部由一个对象进行包装,该对象具有一个.value属性,用于访问和修改包裹的变量值。
  • 访问响应式变量的值:
    • 在模板中,可以直接访问nameage这样的响应式变量。
    • 在示例代码中,可以在模板中直接使用{{ name }}{{ age }}来渲染它们的值。
  • 修改响应式变量的值:
    • 在示例代码中,addAge函数用于增加age的值。
    • 由于age是通过ref函数包裹的,所以在修改它的值时,需要使用age.value来访问和修改其内部的真实值。
  • 注意事项:
    • 在使用ref函数包裹变量时,尽量在组件的setup函数内部进行。
      • 这样可以确保变量的响应式特性在组件的整个生命周期中有效。
    • ref函数只会对变量的值进行响应式处理,并不会将变量的所有属性都变为响应式。
      • 如果需要将对象、数组等复杂类型的属性也变为响应式,可以使用reactive函数。
<script>
// 导入ref函数
import {ref} from "vue";
export default {
  // Vue3将所有内容写在 setup 函数内
  setup() {
    // var: ES5老语法(不建议使用)  let: ES6新语法  const: 定义常量
    // (2)定义变量和函数 ----(会触发响应式)
    // 用 ref 函数将变量值包裹起来
    let name = ref("dream")
    let age = ref(19)

    const addAge = () => {
      // 如果修改变量的值,需要对象.value才能修改
      age.value += 1;
    }
    const clickChange = () => {
      name.value = "baby"
    }
    // setup 必须将变量 return 出去才能看到定义的 值
    return {name, age, addAge,clickChange}
  }
}
</script>

【三】data/methods

setup函数内部可以使用datamethods等选项中定义的属性和方法

  • 但是setup函数会先于这些选项执行,所以如果在data中定义了与setup中相同的变量,setup中的定义会覆盖它。
  • 同样地,如果在模板中使用某个变量或方法,优先使用setup中的定义

【四】reactive函数

  • reactive函数是Vue框架中的一个响应式函数,用于定义对象(或数组)类型的数据。

    • 与ref函数不同,ref函数用于定义基本类型数据。
  • 从定义数据角度对比:

    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
  • 从原理角度对比:

    • ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:

    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
    • reactive定义的数据:操作数据与读取数据:均不需要.value。
<script>

// 导入ref函数
import {ref} from "vue";
// 导入reactive函数
import {reactive} from "vue";

export default {

  name: "HomeView",
  setup() {
    // (1)要想让变量有响应式,要用 ref 包裹,包裹的都是字符串/数字/布尔
    // 使用ref定义基本类型数据
    let count = ref(0);
	
    // (2)如果要想让对象有响应式,需要用 reactive
    // 使用reactive定义对象类型数据
    let userInfo = reactive({"name": "dream", "age": 18, "hobby": "music"});

    const addCount = () => {
      // 在操作基本类型数据时,需要使用.value来获取或修改数据
      count.value += 1;
    }

    const addAge = () => {
      // 在操作reactive定义的对象时,可以直接当做对象使用
      userInfo.age += 1;
    }
    
    return {count, userInfo, addCount, addAge};
  }
}
</script>

【五】计算/监听属性

  • 属性是响应式的变量,可以在组件中被使用和观察变化。
  • Vue 3提供了不同类型的属性,包括普通变量、对象和多个变量

【1】监听属性

  • 监听属性之普通变量
<script>

// 导入ref函数
import {ref, reactive, watch} from "vue";
// 导入reactive函数

export default {

  name: "HomeView",
  setup() {
    // 原来的计算属性和监听属性,通过配置项api配置

    // 现在使用组合式api后都写在 setup 内
    // (1)监听属性之普通变量
    let name = ref("蚩梦")

    const clickChange = () => {
      name.value = "晓萌"
    }

    watch(name, (newValue, oldValue) => {
      // 原来的值
      console.log(oldValue)
      // 修改后的新值
      console.log(newValue)
      console.log('name 真的变了 ... ')
    })
    return {name, clickChange}
  }
}
</script>
  • 监听属性之对象
<script>

// 导入ref函数
import {ref, reactive, watch} from "vue";
// 导入reactive函数

export default {

  name: "HomeView",
  setup() {
    // 原来的计算属性和监听属性,通过配置项api配置

    // 现在使用组合式api后都写在 setup 内
    // (2)监听属性之对象
    let info = reactive({"name": "dream", "age": 18, "hobby": "music"})

    const clickChangeInfo = () => {
      info.name = "萌萌"
    }

    // 要写成函数形式
    watch(() => info.name, (newValue, oldValue) => {
      // 原来的值
      console.log(oldValue)
      // 修改后的新值
      console.log(newValue)
      console.log('info中的name变了')
    })

    return {info,clickChangeInfo}
  }
}
</script>
  • 监听属性之监听多个变量
<script>

// 导入ref函数
import {ref, reactive, watch} from "vue";
// 导入reactive函数

export default {

  name: "HomeView",
  setup() {
    // 原来的计算属性和监听属性,通过配置项api配置

    // 现在使用组合式api后都写在 setup 内

    // (3)监听属性之监听多个变量
    let width = ref(100)
    let height_name = ref('高度')

    const clickChangeWidth = () => {
      width.value = 9999
    }
    const clickChangeHeight_name = () => {
      height_name.value = "新高度"
    }

    watch([width, height_name], (newValue, oldValue) => {
      console.log('旧值',oldValue)
      console.log('新值',newValue)
      console.log('width/height_name发生了变化')
    })

    return {width, height_name, clickChangeWidth, clickChangeHeight_name}
  }
}
  • 页面展示
<template>
  <div>
    <h1>这是一个首页</h1>
    名字是:>>>>{{ name }}
    <br>
    <button @click="clickChange">点我换名字</button>
    <br>
    对象中的名字是:>>>>{{ info.name }}
    <br>
    <button @click="clickChangeInfo">点我换名字</button>
    <br>
    宽度是:>>>>{{ width }}
    高度是:>>>>{{ height_name }}
    <br>
    <button @click="clickChangeWidth">点我换宽度</button>
    <br>
    <button @click="clickChangeHeight_name">点我换高度</button>
    <br>
  </div>
</template>

【2】计算属性

  • 计算属性 使用computed ,只有取值触发或取值赋值触发函数
  • 计算属性可以取值
<script>

// 导入ref函数
import {ref, reactive, watch, computed} from "vue";
// 导入reactive函数

export default {

    name: "HomeView",
    setup() {
        // 原来的计算属性和监听属性,通过配置项api配置

        // 现在使用组合式api后都写在 setup 内

        let name = ref("蚩梦")
        // 计算属性可以取值
        let newName = computed(() => {
            return name.value + "NB"
        })

        return {name, newName}
    }
}
</script>
  • 计算属性可以修改值
<script>

// 导入ref函数
import {ref, reactive, watch, computed} from "vue";
// 导入reactive函数

export default {

  name: "HomeView",
  setup() {
    // 原来的计算属性和监听属性,通过配置项api配置

    // 现在使用组合式api后都写在 setup 内

    let name = ref("蚩梦")
  
    // 计算属性可以修改值
    let newName = computed({
      get() {
        // 只要计算属性 取值就会触发
        return name.value + '_dr'
      },
      set(value) {
        // 只要计算属性 值发生变化就会触发
        // 只要 newName 发生变化 , name 也应该变化
        console.log('旧值:>>>', name.value)
        let result = value.split('_')
        name.value = result[0]
        console.log('新值:>>>', newName.value)
      }

    })

    return {name, newName}
  }
}
</script>
  • 页面展示
<template>
  <div>
    <h1>这是一个首页</h1>
    <br>
    旧值:>>><input type="text" v-model="name">
    <br>
    <h2>输入的值 ----> {{ name }}</h2>
    <br>
    <h2>监听到的新值 ----> {{ newName }}</h2>
    <br>
    新输入的新值:>>><input type="text" v-model="newName">
  </div>
</template>

【六】生命周期钩子

【一】介绍

  • vue3不建议使用 配置项api,把所有代码都写在setup函数中
    • 以后没有:destory这俩了,换成了unmounted
  • 可以写配置项api
beforeCreate
created
beforeMoun
mounted
beforeUpdate
updated 
beforeUnmount 
unmounted 
  • 组合式api
beforeCreate===>setup()
created=======>setup()
beforeMount ===>onBeforeMount
mounted=======>onMounted
beforeUpdate===>onBeforeUpdate
updated =======>onUpdated
beforeUnmount ==>onBeforeUnmount
unmounted =====>onUnmounted
  • beforeCreate -> setup函数:

    • 在Vue 2中,beforeCreate钩子用于在实例被创建之前执行逻辑。
    • 在Vue 3中,可以将beforeCreate钩子中的代码移到setup函数中执行。setup函数会在组件实例创建之前执行,并且返回响应式状态和事件处理函数等。
  • created -> setup函数:

    • 在Vue 2中,created钩子用于在实例创建完成后执行逻辑。
    • 在Vue 3中,可以将created钩子中的代码移到setup函数中执行。
  • beforeMount -> onBeforeMount:

    • 在Vue 2中,beforeMount钩子在挂载之前执行。
    • 在Vue 3中,可以使用onBeforeMount函数来替代beforeMount钩子。该函数会在组件挂载之前执行。
  • mounted -> onMounted:

    • 在Vue 2中,mounted钩子在挂载完成后执行。
      -onMounted函数来替代mounted钩子。该函数会在组件挂载完成后执行。
  • beforeUpdate -> onBeforeUpdate:

    • 在Vue 2中,beforeUpdate钩子在数据更新之前执行。
    • 在Vue 3中,可以使用onBeforeUpdate函数来替代beforeUpdated:
    • 在Vue 2中,updated钩子在数据更新完成后执行。
    • 在Vue 3中,可以使用onUpdated函数来替代updated钩子。该函数会在组件更新完成后执行。
  • beforeUnmount -> onBeforeUnmount:

    • 在Vue 2中,beforeUnmount钩子在组件卸载之前执行。
    • 在Vue 3中,可以使用onBeforeUnmount函数来替代beforeUnmount钩子。该函数会在组件卸载之前执行。
  • unmounted -> onUnmounted:

    • 在Vue 2中,unmounted钩子在组件卸载完成后执行。
    • 在Vue 3中,可以使用onUnmounted函数来替代unmounted钩子。该函数会在组件卸载完成后执行。

【二】示例

  • src\views\HomeView.vue
<script setup>
</script>

<template>
  <div>
    <h1>这是一个首页</h1>
    <br>
    <h2>组件HelloWorld</h2>
    <br>
    <button @click="showFunc">点击显示</button>
    <br>
    <HelloWorld v-if="show"></HelloWorld>
  </div>


</template>

<script>
import HelloWorld from "@/components/HelloWorld.vue";
// 导入ref函数
import {ref, reactive, watch, computed} from "vue";
// 导入reactive函数

export default {

  name: "HomeView",
  setup() {
    let show = ref(true);
    const showFunc = () => {
      show.value = !show.value
    }

    return {show, showFunc}
  },
  components: {
    HelloWorld,
  },
}
</script>
  • src\components\HelloWorld.vue
<script setup>

</script>

<template>
  <div class="hello">
    <h1>HelloWorld页面</h1>
  </div>
</template>

<script>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, reactive } from 'vue';

export default {
  name: "HelloWorld",
  // 生命周期钩子函数
  // beforeCreate===>setup()
  // created=======>setup()
  // beforeMount ===>onBeforeMount
  // mounted=======>onMounted
  // beforeUpdate===>onBeforeUpdate
  // updated =======>onUpdated
  // beforeUnmount ==>onBeforeUnmount
  // unmounted =====>onUnmounted
  setup() {
    // 在beforeCreate钩子中的逻辑可放置于setup函数中
    const data = reactive({
      message: 'Hello Vue 3!',
      count: 0
    });

    // created钩子中的逻辑也可以放置于setup函数中
    console.log('组件已创建');

    // beforeMount -> onBeforeMount
    onBeforeMount(() => {
      console.log('组件挂载前');
    });

    // mounted -> onMounted
    onMounted(() => {
      console.log('组件已挂载');
    });

    // beforeUpdate -> onBeforeUpdate
    onBeforeUpdate(() => {
      console.log('组件更新前');
    });

    // updated -> onUpdated
    onUpdated(() => {
      console.log('组件已更新');
    });

    // beforeUnmount -> onBeforeUnmount
    onBeforeUnmount(() => {
      console.log('组件卸载前');
    });

    // unmounted -> onUnmounted
    onUnmounted(() => {
      console.log('组件已卸载');
    });

    return {
      data
    };
  }
}
</script>


<style scoped>
</style>

【七】toRef

  • ...{对象}---> 相当于解压
  • 在setup函数中return时,使用return
    • 以后在templte中直接使用内层的变量即可
<script setup>
</script>

<template>
  <div>
    <h1>这是一个首页</h1>
<!--    <br>-->
<!--    {{ userInfo }}-->
    name ----- > {{name}}
    <br>
    age -----> {{ age}}
    <br>
    <button @click="clickImg">点我看美女</button>
  </div>
</template>

<script>
// 导入ref函数
import {ref, reactive, toRefs} from "vue";
// 导入reactive函数

export default {

  name: "HomeView",
  setup() {

    let userInfo = reactive({"name": "蚩梦", "age": 19})
    const clickImg = () => {
      alert("美女")
    }

    // 原来的写法
    // return {userInfo,clickImg}

    // 高级写法
    // 等同于  {name:data.name,age:data.age}
    return {...toRefs(userInfo), clickImg}
  },
}
</script>

【七】<script setup>

【1】详解

  • 在Vue 3中,<script setup> 是一个新的语法糖,用于编写组件的逻辑部分。
    • 它可以帮助我们更简洁地编写组件,并且提供了一些额外的好处。
  • 使用 <script setup> 有以下几个特点和注意事项:

(1)无需显式导入Vue函数

  • <script setup> 中,你无需显式导入Vue函数,所有的基础选项(如namecomponents等)会自动绑定到组件上。

(2)变量声明和初始化

  • <script setup> 中,你可以直接使用JavaScript的变量声明和初始化语法(如let, const, var),无需使用return将其暴露给组件模板。
  • 除了变量声明,也可以直接在响应式对象中进行属性的声明和初始化。

(3)模板中使用变量

  • 在模板中,你可以直接使用 <script setup> 中声明的变量,无需通过this或其他的变量访问方式。
  • 在非响应式的数据上,可以通过直接引用变量名来获取数据。
  • 在响应式的数据上,需要使用reftoRefs函数将其转换为响应式引用,以便在模板中使用。
  • 使用reftoRefs时,可以通过解构赋值将引用的属性直接暴露给模板。

(4)事件处理

  • <script setup> 中,你可以直接声明和使用函数作为事件处理程序(如点击事件、表单提交等)。
  • 在模板中,可以通过@v-on:指令绑定到声明的函数。

(4)生命周期钩子和侦听器

  • 你可以在 <script setup> 中直接使用生命周期钩子函数 (onMounted, onUpdated等) 和侦听器 (watch, watchEffect等)。

(5)小结

  • 使用 <script setup> 的好处包括了更简洁的代码、更直观的数据访问方式以及更好的性能优化。
    • 它使得编写组件变得更加清晰和高效,并且可以更好地与TypeScript等静态类型语言集成。
  • 需要注意的是,<script setup> 目前还处于实验阶段,并且需要配合Vue的新编译器(vue-template-compiler@^3.2.0)进行使用。
    • 此外,在使用<script setup>时,一些高级用例和特性可能会有一些限制,需要进行额外的学习和了解。

【2】示例

  • <script setup>表示,这个script里的所有东西是setup函数,原来写在setup中的,现在顶格写即可
<script setup>
import {ref, toRefs} from 'vue'
import HelloWorld from '@/components/HelloWorld.vue'

let name = ref('dream')

const handelClick = () => {
  name.value = '蚩梦'
}
// watch,computed
// 生命周期钩子
// 组件导入,自动注册

// 不需要return
</script>