Vue3+Ts的setup()函数的Prop、Emit、Computed、WatchEffect、Watch、Provide/Inject用法

本文详细介绍了Vue.js中Prop、Emit、Computed等核心概念的应用方法,并深入探讨了WatchEffect和Watch的区别及使用场景,同时提供了Provide/Inject的示例。

一、 Prop、Emit

Prop: 通过Prop父组件 向子组件传递数据
Emit: 通过Emit子组件向父组件传递数据
父组件

<template>
    <div>
      <Child :propRef="propRef" :propReactive="propReactive" @emitValue="getEmitValue" />
    </div>
  </div>
</template>

<script lang="ts">
import { onMounted, ref, reactive, nextTick, toRefs, computed } from 'vue';//引入所需的api
import Child from './components/Child.vue'; //引入子组件
export default {
  components: {
    Child,
  },
  setup() {
    const propRef = ref('我是父组件通过Ref定义的值');
    const propReactive = reactive({ name: '我是父组件的通过Reactive定义的值', path: 'index' });
    //接收子组件emit传的值
    function getEmitValue(value) {
      console.log('emit的值', value);
    }
    return {
      propRef,
      propReactive,
      getEmitValue,
    };
  },
};
</script>

子组件

<template>
  <div>
    <h2>子组件:</h2>
    <div>
      <h3>Props:</h3>
      <p>propRef:{{ propRef }}</p>
      <p>propReactive:{{ propReactive }}</p>
      <p>propDefault:{{ propDefault }}</p>
    </div>

  <div>
    <h3>Emit:</h3>
    <a-button type="primary" @click="emitToParent">Emit给父组件</a-button>
  </div>
</template>

<script lang="ts">
import { ref, computed, watch } from 'vue';

export default {
  //接收父组件传过来的值
  props: {
    propRef: { type: String },
    propReactive: { type: Object },
    propDefault: { type: String, default: '默认值' },
  },
  //注册所需emit的名称
  emits: ['emitValue'],
  setup(props, context) {
    //传入props,获取props的值,渲染到页面中
    //context :上下文,包括 attrs 、 emit 、slots

    /** emit使用 start */
    
    //定义emit的值
    const emitValue = ref('我是子组件的emit,我被发射了');
    //通过emit将值emit到父组件
    function emitToParent() {
      context.emit('emitValue', emitValue.value);
    }
   /** emit使用 end */
   
    return {
      props,
      emitValue,
      emitToParent
    };
  },
};
</script>

二、Computed(计算属性)

computed: 是vue的计算属性,它根据依赖关系(如computedvalue1 、computedvalue2 )进行缓存的计算,只有在它的相关依赖发生改变时才会进行更新,一般情况下,computed默认使用的是getter属性

    /** 计算属性 start */
    //定义计算属性值
    const computedvalue1 = ref(1);
    const computedvalue2 = ref(10);

    //写法一
    const computerMode1 = computed(() => {
      return computedvalue1.value + computedvalue2.value;
    });

    //写法二
    const computerMode2 = computed({
      get() {
        return computedvalue1.value + computedvalue2.value;
      },
      set(value) {
        return computedvalue1.value + computedvalue2.value;
      },
    });

    /** 计算属性 end */

三、WatchEffect

watchEffect: 立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

    /* watchEffect start */
    watchEffect(() => {
      console.log('%c [ 通过watchEffect监听watchValue的值的变化 ]-73', watchValue.value);
    });
    /* watchEffect end */

四、Watch

watch: 可以侦听特定的数据源(value、object、array),并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。

watch与 watchEffect 相比,watch 允许我们:
1、惰性地执行副作用;
2、更具体地说明应触发侦听器重新运行的状态;
3、访问被侦听状态的先前值和当前值。

<template>
  <div>
    <h3>Watch:</h3>
    <a-button type="primary" @click="addWatch">触发Watch</a-button>
  </div>
</template>
    /** watch 使用start */
    //定义watch被监听的值
    const watchValue = ref(1);

    function addWatch() {
      watchValue.value++;
    }

    watch(
      watchValue,
      (oldValue, newValue) => {
        console.log('%c [ oldValue,newValue ]', oldValue, newValue);
      },
      { immediate: true, deep: true }
    );

    /** watch 使用end */

五、Provide/Inject

provide 和 inject: 无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

    /* 父组件定义需要通过provide注射到子组件的值 start */
    const provideValueData = reactive({ name: 'provide', type: 'object' });
    provide('provideValue', provideValueData);
    /* 父组件定义需要通过provide注射到子组件的值 end */
    
    /*子组件通过inject接收父组件传过来的值 start*/
     export default {
     inject: ['provideValue'],
     }
    /*子组件通过inject接收父组件传过来的值 end*/
<think>在Vue 3中,子组件向父组件传值有多种方式,其中使用Pinia是状态管理的一种方式,但也可以使用事件发射(emit)或依赖注入(provide/inject)等。不过,根据问题描述,我们关注的是使用Pinia来实现子组件向父组件传值。 ### 使用Pinia在子组件和父组件之间共享状态 Pinia是Vue官方推荐的状态管理库,它允许你在组件之间共享状态。使用Pinia,你可以在一个store中定义状态和方法,然后在任何组件中导入并使用这个store。这样,子组件可以通过修改store中的状态来间接传递值给父组件,因为父组件也可以访问同一个store。 #### 步骤: 1. **安装Pinia**(如果尚未安装): ```bash npm install pinia ``` 2. **创建Pinia实例并挂载到Vue应用**: 在`main.ts`中: ```typescript import { createApp } from &#39;vue&#39; import { createPinia } from &#39;pinia&#39; import App from &#39;./App.vue&#39; const pinia = createPinia() const app = createApp(App) app.use(pinia) app.mount(&#39;#app&#39;) ``` 3. **定义一个store**: 例如,创建一个`useMessageStore`: ```typescript // stores/message.ts import { defineStore } from &#39;pinia&#39; export const useMessageStore = defineStore(&#39;message&#39;, { state: () => ({ message: &#39;&#39; as string, }), actions: { setMessage(newMessage: string) { this.message = newMessage }, }, }) ``` 4. **在子组件中修改状态**: 子组件可以通过调用store的action来设置消息,从而更新状态。 ```vue <script setup lang="ts"> import { useMessageStore } from &#39;@/stores/message&#39; const store = useMessageStore() const sendMessageToParent = () => { store.setMessage(&#39;Hello from child!&#39;) } </script> <template> <button @click="sendMessageToParent">Send Message</button> </template> ``` 5. **在父组件中读取状态**: 父组件可以监听store中状态的变化(通过计算属性或watch)来获取子组件传递的值。 ```vue <script setup lang="ts"> import { useMessageStore } from &#39;@/stores/message&#39; import { computed } from &#39;vue&#39; const store = useMessageStore() const messageFromChild = computed(() => store.message) </script> <template> <div>Message from child: {{ messageFromChild }}</div> <ChildComponent /> </template> ``` ### 为什么使用Pinia? - **状态共享**:Pinia提供了一个全局的状态管理,使得任何组件(无论父子关系)都可以轻松地访问和修改同一个状态。 - **响应式**:Pinia的状态是响应式的,当状态改变时,依赖该状态的组件会自动更新。 - **解耦**:使用Pinia可以避免在组件树中通过多层传递事件和props,使代码更清晰。 ### 注意事项 - 使用Pinia进行组件通信适用于多个组件需要共享状态的场景。如果只是简单的父子组件通信,使用emitprops可能更直接。 - 确保在组件销毁时,如果有必要,可以重置状态(例如在父组件的`onUnmounted`中调用store的`$reset`方法),以避免状态残留。 ### 相关问题 根据要求,提出以下相关问题: §§相关问题§§: 1.Vue 3中,除了Pinia,还有哪些方式可以实现子组件向父组件传值? 2. Vue 3的`<script setup>`语法糖中,如何使用`defineEmits`来触发事件实现子传父? 3. 在Pinia中,如何定义带有类型的状态和操作(使用TypeScript)? 4. 使用Pinia时,如何避免在组件间共享状态导致的状态管理混乱?</think>在 Vue 3 + TypeScript 项目中,子组件向父组件传值时使用 Pinia 是可行的,但 Pinia 主要用于全局状态管理。以下是实现方法和解释: ### 解决方案:使用 Pinia Store 实现跨组件通信 ```typescript // stores/messageStore.ts import { defineStore } from &#39;pinia&#39; export const useMessageStore = defineStore(&#39;message&#39;, { state: () => ({ childMessage: &#39;&#39; as string }), actions: { setChildMessage(message: string) { this.childMessage = message } } }) ``` ```vue <!-- 子组件 ChildComponent.vue --> <script setup lang="ts"> import { useMessageStore } from &#39;@/stores/messageStore&#39; const store = useMessageStore() const sendMessage = () => { store.setChildMessage(&#39;Hello from child!&#39;) } </script> <template> <button @click="sendMessage">Send to Parent</button> </template> ``` ```vue <!-- 父组件 ParentComponent.vue --> <script setup lang="ts"> import { useMessageStore } from &#39;@/stores/messageStore&#39; import { storeToRefs } from &#39;pinia&#39; const store = useMessageStore() const { childMessage } = storeToRefs(store) // 保持响应式 </script> <template> <div>Received: {{ childMessage }}</div> <ChildComponent /> </template> ``` ### 为什么使用 Pinia? 1. **跨层级通信**:当组件层级较深时,Pinia 避免了 prop/emit 的层层传递 2. **多组件共享状态**:多个父/子组件可同时访问同一状态 3. **响应式保证**:Pinia 状态天然响应式,自动触发视图更新 4. **TypeScript 支持**:完整的类型推断和类型安全 ### 与传统 emit 的对比 | 方式 | 适用场景 | 优点 | 缺点 | |-------------|-------------------------|--------------------------|---------------------| | `emit()` | 直接父子关系 | 简单直接,意图明确 | 多层组件需逐层传递 | | **Pinia** | 跨组件/全局状态 | 解耦组件,集中管理状态 | 略重,需额外配置 | ### 最佳实践建议 1. **状态命名规范**:使用明确的状态名(如 `childMessage` 而非 `message`) 2. **使用 TypeScript 接口**: ```typescript interface MessageState { childMessage: string // 其他状态... } export const useMessageStore = defineStore(&#39;message&#39;, { state: (): MessageState => ({ childMessage: &#39;&#39; }) }) ``` 3. **组件卸载时重置状态**(如需): ```typescript // 在父组件中 import { onUnmounted } from &#39;vue&#39; onUnmounted(() => { store.$reset() }) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值