vue 组合式API

目录

为什么要引入  组合式API?

 setup 组件选项

简单使用

 setup中 非响应式变量 带 ref 的响应式变量

理解

代码

效果

watch 响应式更改

代码 

 watch和watchEffect的区别:

独立的 computed 属性

简单使用

生命周期钩子

简单使用

Setup 参数   props  context

props

 简单使用

toRefs 

简单使用 

Contex

简单探索

context.emit context.expose

Provide / Inject

简单使用

运行结果

单文件组件 script setup

为什么使用

简单使用

运行结果 


为什么要引入  组合式API?

下面是一个大型组件的示例,其中逻辑关注点按颜色进行分组。

问题:

  1. 这种碎片化使得理解和维护复杂组件变得困难。
  2. 选项的分离掩盖了潜在的逻辑问题。
  3. 在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。

优化点:

如果能够将同一个逻辑关注点相关代码收集在一起会更好。

而这正是组合式 API 使我们能够做到的。

 setup 组件选项

在 setup 中你应该避免使用 this

原因:它不会找到组件实例。

setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法在 setup 中被获取。

简单使用

代码

<script>
export default{
  data(){
    return{
    
    }
  },
  //组合式API 将同一个逻辑关注点相关代码收集起来
  setup(){
    //组件被创建之前执行 不能使用this
    const msg='hello';
    console.log(msg);
  }
}
</script>
<template>
</template>
<style>
</style>

效果

 setup中 非响应式变量 带 ref 的响应式变量

理解

ref 接收参数并将其包裹在一个带有 value property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值

ref 为我们的值创建了一个响应式引用

注意:从 setup 返回的 refs 在模板中访问时是被自动浅解包的,因此不应在模板中使用 .value 但是函数中需要.value

代码

<script>
import { ref } from '@vue/reactivity';
export default{
  data(){
    return{
    
    }
  },
  //组合式API 将同一个逻辑关注点相关代码收集起来
  setup(){
    //组件被创建之前执行 不能使用this
    //msg的代码逻辑 
    //没有响应式
    let msg='hello';
    function changeMsg(){
      msg='你好呀';
      console.log(msg);
    }
    //通过ref定义响应式变量
    //counter的逻辑代码 counter 为一个对象 静态 可变对象中的值
    const counter=ref(0);
    //ref() 返回带有value属性的对象
    function changeCounter(){
      counter.value='16';
      console.log(counter.value);
    }
    //将函数 属性 暴露出去 可被其他使用
    return {
      msg,
      changeMsg,
      counter,
      changeCounter
    }
  }
}
</script>
<template>
<h2>{{msg}}</h2>
<button @click="changeMsg">非响应式====改变msg</button>
<br>
<!-- 模块会自动解析value值 如果填写counter.value 样式会没有 -->
<h2>{{counter}}</h2>
<button @click="changeCounter">响应式====改变counter</button>
</template>
<style>
</style>

效果

点击按钮前 

点击按钮后

watch 响应式更改

  • 一个想要侦听的响应式引用或 getter 函数
  • 一个回调
  • 可选的配置选项

代码 

<script>
import { reactive, ref } from '@vue/reactivity';
import { watch,watchEffect } from '@vue/runtime-core';
export default{
  data(){
    return{
    
    }
  },
  //组合式API 将同一个逻辑关注点相关代码收集起来
  setup(){
    const counter=ref(0);
    function changeCounter(){
      counter.value='16';
    }
     //watch(侦听的响应式引用,回调函数)
    watch(counter,(newValue,oldValue)=>{
      console.log("new counter======{}==",newValue);
      console.log("old counter======{}==",oldValue);
    });
    const user=reactive({
      name:"张三"
    })
    function changeUserName(){
      user.name="王五";
    }
     watch(user,(newValue,oldValue)=>{
      console.log("new user======{}==",newValue);
      console.log("old user======{}==",oldValue);
    });
    //watchEffect(回调函数) 注意:不需要指定监听的属性 组件初始化的时候会执行一次回调函数 自动收集依赖
    watchEffect(()=>{
      console.log(user.name);
    })
    //将函数 属性 暴露出去 可被其他使用
    return {
      counter,
      user,
      changeCounter,
      changeUserName
    }
    /*
     watch和watchEffect的区别:
     1.watchEffect不需要指定监听的属性 自动收集依赖 只要在回调中引用到了响应式的属性 这些属性发生改变 回调就会执行
       watch 只能侦听指定的属性 做出回调函数的执行 可以侦听多个
     2.watch可以获取到新值和旧值 watchEffect获取不到    
     3.watchEffect在组件初始化的时候就会自动执行一次 用来收集依赖 watch不需要 一开始就指定了
    */
  }
}
</script>
<template>
<h2>{{counter}}</h2>
<button @click="changeCounter">改变counter</button>
<br>
<!-- 模块会自动解析value值 如果填写counter.value 样式会没有 -->
<h2>{{user.name}}</h2>
<button @click="changeUserName">改变userName</button>
</template>
<style>
</style>

结果 

 watch和watchEffect的区别:

     1.watchEffect不需要指定监听的属性 自动收集依赖 只要在回调中引用到了响应式的属性 这些属性发生改变 回调就会执行
       watch 只能侦听指定的属性 做出回调函数的执行 可以侦听多个
     2.watch可以获取到新值和旧值 watchEffect获取不到    
     3.watchEffect在组件初始化的时候就会自动执行一次 用来收集依赖 watch不需要 一开始就指定了

独立的 computed 属性

computed 函数传递了第一个参数,它是一个类似 getter 的回调函数,输出的是一个只读响应式引用。为了访问新创建的计算变量的 value,我们需要像 ref 一样使用 .value property。

简单使用

<script>
import { reactive, ref } from '@vue/reactivity';
import { computed} from '@vue/runtime-core';
export default{
  data(){
    return{
    
    }
  },
  //组合式API 将同一个逻辑关注点相关代码收集起来
  setup(){
    const msg=ref("hello world");
    const reverseMsg = computed(()=>{
      //返回一个带value的对象
      return msg.value.split("").reverse().join("");
    })
    console.log(reverseMsg);
    const user=reactive({
      name:"张三",
      reverseMsg: computed(()=>{
      //返回一个带value的对象
      return msg.value.split("").reverse().join("");
    })
    })
    console.log(user.reverseMsg);
    //将函数 属性 暴露出去 可被其他使用
    return {
      counter,
      user,
      changeCounter,
      changeUserName
    }
  
  }
}
</script>
<template>
</template>
<style>
</style>

结果

生命周期钩子

下表包含如何在 setup () 内部调用生命周期钩子:

选项式 APIHook inside setup
beforeCreateNot needed*
createdNot needed*
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
errorCapturedonErrorCaptured
renderTrackedonRenderTracked
renderTriggeredonRenderTriggered
activatedonActivated
deactivatedonDeactivated

因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

这些函数接受一个回调函数

简单使用

<script>

import { onBeforeMount, onMounted} from '@vue/runtime-core';
export default{
  data(){
    return{
    
    }
  },
  //组合式API 将同一个逻辑关注点相关代码收集起来
  setup(){
    //生命周期钩子函数中有一个参数 回调函数
     onMounted(() => {
      console.log('Component is mounted!')
    })
     onBeforeMount(()=>{
      console.log('Component is onBeforeMount!')
     })
  }
}
</script>
<template>
</template>
<style>
</style>

运行效果

Setup 参数   props  context

props

setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新。

 简单使用

App.vue

<script>
import InputVue from './components/Input.vue'
export default{
  data(){
    return{
     message:"父组件"
    }
  },
   components:{
    //加载组件
    InputVue
  }
}
</script>
<template>
<InputVue :msg='message'></InputVue>
</template>
<style>
</style>

 Input.vue

<script>
export default{
  data(){
    return {
      message:"子组件"
    }
  },
  props:{
    msg:{
      type:String,
      default:"hello"
    }
  },
  setup(props){
    console.log(props)
  }
}
</script>
<template>
</template>
<style>
</style>

结果

toRefs 

因为 props 是响应式的,你不能使用 ES6 解构,它会消除 prop 的响应性。

setup 函数中使用 toRefs 函数来完成 

简单使用 

App.vue

<script>
import InputVue from './components/Input.vue'
export default{
  data(){
    return{
     message:"父组件"
    }
  },
   components:{
    //加载组件
    InputVue
  },
  methods:{
    changeMsg(){
      this.message="parent component";
    }
  }
}
</script>
<template>
<InputVue :msg='message'></InputVue>
<button @click="changeMsg">改变message</button>
</template>
<style>
</style>

Input.vue

未使用 toRefs

<script>

import { onUpdated, toRefs } from '@vue/runtime-core';
export default{
  data(){
    return {
      introduce:"子组件"
    }
  },
  props:{
    msg:{
      type:String,
      default:"hello"
    }
  },
  setup(props){
    console.log(props.msg);
     const { msg } = props;
    onUpdated(()=>{
      console.log("进入onUpdated函数")
      console.log("未使用toRef====>"+props.msg);
     console.log("未使用toRef====>"+msg);
    })
  }
}
</script>
<template>
</template>
<style>
</style>

运行效果 

 使用 toRefs

<script>

import { onUpdated, toRefs } from '@vue/runtime-core';
export default{
  data(){
    return {
      introduce:"子组件"
    }
  },
  props:{
    msg:{
      type:String,
      default:"hello"
    }
  },
  setup(props){
    console.log(props.msg);
     const { message } =toRefs(props);
    onUpdated(()=>{
      console.log("进入onUpdated函数");
      console.log("使用了toRef====>props.msg==>"+props.msg);
     console.log("使用了toRef====>message.value==>"+message.value);
    })
  }
}
</script>
<template>
</template>
<style>
</style>

运行效果

Contex

context 是一个普通 JavaScript 对象,暴露了其它可能在 setup 中有用的值

简单探索

Input.vue

<script>
export default{
  data(){
    return {
      introduce:"子组件"
    }
  },
  props:{
    msg:{
      type:String,
      default:"hello"
    }
  },
  setup(props,context){
     // Attribute (非响应式对象,等同于 $attrs)
    console.log(context.attrs);
     
    console.log("=====================");
    // 插槽 (非响应式对象,等同于 $slots)
    console.log(context.slots);
    
    console.log("=====================");

    // 触发事件 (方法,等同于 $emit)
    console.log(context.emit);

    console.log("=====================");

    // 暴露公共 property (函数);
    console.log(context.expose);
    }
  }

</script>
<template>
</template>
<style>
</style>

App.vue

<script>
import InputVue from './components/Input.vue'
export default{
  data(){
    return{
     message:"父组件"
    }
  },
   components:{
    //加载组件
    InputVue
  },
  methods:{
    changeMsg(){
      this.message="parent component";
    }
  }
}
</script>
<template>
<InputVue :msg='message' id="input" class="shape"></InputVue>
<button @click="changeMsg">改变message</button>
</template>
<style>
</style>

运行结果 

context.emit context.expose

简单使用

Input.vue

<script>
import { ref } from '@vue/reactivity'
import { h } from '@vue/runtime-core';
export default{
  data(){
    return {
      introduce:"子组件"
    }
  },
  props:{
    msg:{
      type:String,
      default:"hello"
    }
  },
  setup(props,context){
     const counter=ref(30);
     function sendParent(){
      context.emit('toPare',counter.value);
     };
     //通过expose暴露
     context.expose({
       counter,
       sendParent
      });
      //返回渲染函数 没有暴露数据
     return ()=>h('div',counter.value);
    }
  }

</script>
<template>
<!-- 可在返回结果 直接暴露sendParent()使用 -->
<button @click="sendParent">按钮实现emit</button>
</template>
<style>
</style>

 App.vue

<script>
import InputVue from './components/Input.vue'
export default{
  data(){
    return{
     message:"父组件"
    }
  },
   components:{
    //加载组件
    InputVue
  },
  mounted(){
    console.log("查看暴露数据=====");
   console.log(this.$refs.InputVue);
   console.log("使用sendParent函数=====");
   this.$refs.InputVue.sendParent();
  },
  methods:{
    sendPare(value){
       console.log("$emit 子传父 数据");
      console.log(value);
    }
  }
}
</script>
<template>
<InputVue :msg='message' id="input" class="shape" ref="InputVue" @toPare='sendPare'></InputVue>
</template>
<style>
</style>

 运行结果

Provide / Inject

provide 函数允许你通过两个参数定义 property:

  1. name (<String> 类型)
  2. value

inject 函数有两个参数:

  1. 要 inject 的 property 的 name
  2. 默认值 (可选)

简单使用

App.vue

<script>
import { ref } from '@vue/reactivity';
import NavigationBarVue from './components/NavigationBar.vue';
import { provide } from '@vue/runtime-core';
export default{
  data(){
    return{
     message:"根组件"
    }
  },
   components:{
    //加载组件
    NavigationBarVue
  },
  setup(){
    const name=ref('张三');
   //把整个对象都传进入 不要name.value 要不然就不会响应式了
    provide('name',name);
    function changeName(){
      name.value='王五';
    }
    return {
     changeName
    };
  }
}
</script>
<template>
<NavigationBarVue></NavigationBarVue>
<button @click="changeName">响应式改变名称</button>
</template>
<style>
</style>

 NavigationBar.vue

<template>
  <SearchVue></SearchVue>
</template>

<script>
import SearchVue from './Search.vue'
export default {
    data(){
    return{
      message:"祖先组件",
    }
  },
   components:{
    //加载组件
    SearchVue
  }
}
</script>

<style>

</style>

Search.vue


<template>
  <InputVue></InputVue>
</template>

<script>
import InputVue from './Input.vue'
export default {
      data(){
    return{
      message:"父组件"
    }
  },
  components:{
    //加载组件
    InputVue
  }
}
</script>

<style>

</style>

 Input.vue

<script>
import { inject } from '@vue/runtime-core';
export default{
  data(){
    return {
      introduce:"子组件"
    }
  },
  setup(){
     //可跨级通信 案例 App.vue->NavigationBar.vue->Search.vue->Input.vue 
     const name=inject('name');
     return {
      name
     }
  }
}

</script>
<template>
<h2>{{name}}</h2>
</template>
<style>
</style>

运行结果

注意点: 传入整个的对象 不用.value 要不然就没有响应式了 

 点击按钮前

点击按钮后

单文件组件 script setup

为什么使用

官网:script setup

<script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 <script> 语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 TypeScript 声明 props 和抛出事件。
  • 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
  • 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。

简单使用

App.vue

<script setup>
// 顶层的绑定会被暴露给模板
// 当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用
import { ref } from '@vue/reactivity';
import NavigationBarVue from './components/NavigationBar.vue';
import { provide } from '@vue/runtime-core';
//引入组件不需要注册
import InputVue from './components/Input.vue';
//定义变量 在模板中不需要暴露 直接使用
const number=20;
const name=ref('你好呀');
function changeName(){
  name.value="hello";
}
</script>
<template>
<InputVue></InputVue>
<h2>{{number}}</h2>
<br>
<h2>{{name}}</h2>
<button @click="changeName">响应式改变名称</button>
</template>
<style>
</style>

Input.vue

<script>
import { inject } from '@vue/runtime-core';
export default{
  data(){
    return {
      introduce:"子组件"
    }
  }
}

</script>
<template>
<h2>{{introduce}}</h2>
</template>
<style>
</style>

运行结果 

点击按钮前

点击按钮后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新生代农民工-小王八

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值