Vue笔记6--组合式API setup

1、组合式api-setup

组合式api将同一个逻辑关注点的代码收集在一起。在组件被创建前执行,props解析完成后被作为组合式api入口。setup取代了beforeCreate()created() ,由于是组件在被创建之前执行,不要使用this.

那么现在的问题是原本的数据data:{}监听器watch:计算属性computed:生命周期钩子函数这四个基本的部分该怎样去改写。

注:还有一个基础部分方法方法methods:

export default {
  data() {···},
  setup() {
    console.log('setup');
    const msg = 'hello';
    console.log(msg);
  },
  ···
}



1.1响应式数据

ref

实现原本data:{},普通的setup中变量不是响应式需要借助ref(),ref是一个函数会返回一个带有value属性的对象。先引入

import { ref, reactive ,toRef} from 'vue';
  setup() {
    const counter = ref(0)
    function changeCounter() {
      counter.value++
    }
    return {counter,changeCounter};
  },
<!-- 模板自动解析value值不需要counter.vlaue -->
    <h2>{{ counter }}</h2>
    <button @click="changeCounter">改变counter</button>

封装看似没必要,但是可以保持不同数据类型行为统一。

reactive

reactive定义响应式的引用的数据对象

    const obj = reactive({
      name: "张三",
      age: "18",
      children: {
        name: "小张"
      }
    })
    return { obj, changeObj };
    <h2>{{ obj }}--{{ obj.children.name }}</h2>
    <button @click="changeObj">改变obj</button>

这其中我想要直接拿到孩子的名字,可以使用...解构(es6语法)。代价是没有响应式。

return { ...obj};

toRef

toRef(object)使解构后数据重新获得响应式两种return都可

let {name,children}=toRef(obj)
return {name,children };
return { ...toRef(obj)};

这个时候既可以直接拿到孩子的名字,也可以响应式

<h2>{{ children.name }}</h2>



1.2响应式监听

watch

响应式监听,在选项式api中watch是一个对象,这里是函数。接受三个参数:

  1. 一个想要监听的响应式引用或者getter函数
  2. 一个回调
  3. 可选配置选项(用的少)
  setup() {
    const counter = ref(0)
    function changeCounter() {
      counter.value++
    }

    const user = reactive({
      name: '张三',
      age: 18
    })
    function changeUserName() {
      user.name = '李四'
    }

    //watch(侦听的响应式引用,回调函数)
    watch(counter,(newVal,oldVal)=>{
      console.log("newVal",newVal)
      console.log("oldVal",oldVal)
    })
    return { counter, changeCounter, user, changeUserName };
  }

但是此时用这种方式并不能监听到user属性的改变

watchEffect

可以深度监听,参数只有一个回调函数。不需要指定监听的属性,组件初始化的时候执行一次。只要在回调中引用到了响应式的属性,自动收集依赖。

    watchEffect(() => {
      console.log(user.name)
    })

注意watch和watchEffect区别。



1.3Computed:

想在想要将个一个字符串反转后输出

const msg =ref('helloworld')

在选项式api中,computed是一个属性,做法:

  conmputed:{
    reverseMsg:function(){
      return this.message.split('').reverse().join('')
    }

在组合式api中,computed()是一个函数,做法:

<script>
import { computed } from '@vue/reactivity';
...
</script>
  setup() {
    ...
    const reverseMsg = conmputed(() => {
      return msg.value.split('').reverse().join('')
    })
    console.log(reverseMsg.value)
  },

computed()返回值为一个带有value属性的对象

但疑惑的是

    const user = reactive({
      name: '张三',
      age: 18,
      reverseMsg: computed(() => {
        return msg.value.split('').reverse().join('')
      })
    })

想要console.log打印,console.log(user.reverseMsg.value)报undefind得不到结果,得console.log(user.reverseMsg)

猜想:user的内容是以key;value的键值对的形式储存,reverseMsg只不过是个键对应的也只有值。



1.4生命周期钩子

在setup中,引入后,通过在前面加“on”来访问组件的生命周期钩子,比如beforeMount通过onBeforeMount访问。

import { onBeforeMount,onMounted,onBeforeUpdate,onUpdated } from 'vue';

但是没有onBeforeCreate和onCreated,因为setup本身就包含着他们,直接在setup中写即可。

函数中有一个参数,接受一个回调函数,钩子被组件调用时将会执行。可重复执行

  setup() {
    onBeforeMount(() => {//挂载渲染之前
      console.log('onBeforeMount');
    })
    onMounted(() => {//挂载渲染之后
      console.log('onMounted');
    })
    onMounted(() => {//挂载渲染之后
      console.log('onMounted');
    })
    return {};
  }





2、Setup中组件间交互

解决了组件基本部分改写的问题,新的问题变成组件之间如何传递数据。Setup接受两个参数propscontext

2.1props

父传子响应式,传入新的prop将被更新

父组件App.vue

<Content :message='message' />

子组件Content.vue

export default {
    props: {
        message: {
            type: String,
            default: "你好"
        }
    },
    setup(props){
        console.log(props.message)
    },
    ...
}

因为ES6解构会消除prop的响应性,解构可以在setup中使用toRefs完成此操作,如

    setup(props) {
        const { message } = props
        console.log(message)
        onUpdated(() => {
            console.log(message)
        })
    },

这样从props拿到message不是响应式,onUpdated返回的还是最固定的值,要改为:

    setup(props) {
        console.log(props.message)
        const { message } = toRefs(props)
        console.log(message.value)
        onUpdated(() => {
            console.log(message.value)
        })
    },

注:toRefs()返回的是对象,访问需要.value

如果props中有一个数据是可选的(父组件不给也没关系),那么需要props中可能就不存在他。这时torefs()不会管他,需要用toRef特别照顾,如

    setup(props) {
        const 可选数据 = toRef(props,'可选数据')
    },



2.2context

context是一个普通js对象,非响应式可以安全进行ES6解构

暴露其他可能在setup中有用的值

  • context.attrs Attribute,非响应式对象=$attrs
  • context.slots 插槽,非响应式对象=$slots
  • context.emit 触发事件,方法=$emit
  • context.expose 暴露公共property (函数)

.attrs Attribute

<Content class="box" id="content"/>

在父组件App.vue中被这样使用时,class和id就会在attrs

.slots插槽

见以前笔记

.emit触发事件

在子组件Content.vue中定义了一个counter想传给父组件,子传父自定义事件

const counter = ref(20)
function sendeParent() {
    context.emit('injectCountent', counter.value)
}
<button @click="sendePrarent">发送数据</button>

父组件App.vue中

通过@绑定了在子组件里写的inject事件,点击按钮事件触发

<Content @injectCountent="injectCountent"/>
  methods:{
    injectCountent(value){
      console.log(value)
    }
  },

.expose暴露

暴露公共property(函数、财产、所有物),如使用渲染函数时

子组件Content.vue中

先引入,再让setup返回一个渲染函数

import { ref, h } from 'vue'
    setup(props, context) {
        const counter = ref(20)
        function sendeParent() {
            context.emit('injectCountent', counter.value)
        }
        return () => h('div', counter.value)
    },

父组件App.vue中

<Content class="box" id="content"/>

这时候页面上就会显示一个20,只要返回了渲染函数,子组件Content.vue其他标签(比如按钮)就会被渲染函数替换掉。那么就意味着sendeParent()没法通过设置一个按钮点击事件使用,这时可以用expose解决。

子组件Content.vue中

    setup(props, context) {
        const counter = ref(20)
        function sendeParent() {
            context.emit('injectCountent', counter.value)
        }
        context.expose({
            sendeParent, counter
        })
        return () => h('div', counter.value)
    },

父组件App.vue中

可以直接通过ref来调用,这时候就又可以使用sendeParent来拿到子组件的数据了。

<Content ref="content" @injectCountent="injectCountent"/>
  mounted(){
    console.log(this.$refs.content)
    this.$refs.content.sendeParent()
  },



2.3访问组件的property

执行setup的时候只能访问到以下property(财产):props、attrs、slots、emit。换句话说无法访问以下组件选项:data、computed、methods、refs。



2.4Provide/Inject

import引入后

父组件App.vue中

两个参数1 name(string类型) 2 value

  setup() {
    provide('name', '张三')
    return {};
  },

子组件Content.vue中

两个参数1 name(string类型)2 默认值(可选)

    setup(props, context) {
        const name = inject('name')
        return { name }
    },
<h2>{{ name }}</h2>

但是这样拿到的数据并不是响应式的,需要使用ref如下

父组件App.vue中

 setup() {
   const name =ref('张三')
   provide('name',name)
   return {};
 },





3、语法糖

在单文件组件(SFC)中使用组合式api编译式语法糖,SFC允许组件的模板、逻辑、样式被封装在单个组件中。

<script setup>
    import Content from './components/Content.vue';
</script>

可以直接替代export default注册等vue2的语法,甚至下面template也可以抛弃dvi根元素(但是建议还是就加上)。

<script >
import Content from './components/Content.vue';
export default {
  components: { Content }
}
</script>
<template>
  <div>
    <Content/>
  </div>
  <Content/>
</template>

script setup语法糖

  • 引入组件不需要注册
  • 定义变量,在模板使用不需要return暴露出去,模板直接使用

定义响应式的变量还是需要从vue中引入ref,addB()也无需返回直接使用。

import { ref } from 'vue';
const b = ref(10)
function addB(){
  b.value++
}
posted @   北溟有咸其名为鸽  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
点击右上角即可分享
微信分享提示