使用 Typescript 在 Vue3 组合 API 中观察 Value Prop

Posted

技术标签:

【中文标题】使用 Typescript 在 Vue3 组合 API 中观察 Value Prop【英文标题】:Watching Value Prop in Vue3 Composition API with Typescript 【发布时间】:2021-07-30 19:30:43 【问题描述】:

据我所知,根据我在网上找到的几个示例以及我相信我从 Wave UI 的抽屉式源代码中正确理解的一般模式,我正在做的事情应该可以正常工作。

基本上我有一个侧边栏组件,我希望它可以根据提供给它的 value 属性隐藏(想法是它可以根据父组件的需要、屏幕大小、切换按钮等进行更改) .所以侧边栏组件上有一个监视功能,它应该对 props.value 的变化做出反应

Sidebar.vue

<template>
  <aside v-if="mountSidebar" class="min-h-screen">
    <slot></slot>
  </aside>
</template>

<script lang="ts">
import  defineComponent, ref, watch  from "vue";

export default defineComponent(
  name: "Sidebar",
  props: 
    value: 
      type: Boolean,
      default: true,
    ,
  ,
  setup(props) 
    const mountSidebar = ref(props.value);

    watch(
      () => props.value,
      (value) => 
        mountSidebar.value = value;
      
    );

    return 
      close,
      mountSidebar,
    ;
  ,
);
</script>

然后是当前显示侧边栏的 App.vue。

<template>
  <div>
    <navbar>
          <dm-button rounded type="primary" @click="showSidebar = !showSidebar">Toggle Sidebar</dm-button>
      Navbar Content
    </navbar>
    <div class="flex flex-row min-h-screen bg-gray-100 text-gray-800">
      <sidebar v-model="showSidebar">
        Sidebar Content
      </sidebar>
      <div class="flex justify-center w-full">
        <router-view />
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import  defineComponent, ref  from "vue";
import Navbar from "@/components/Navbar.vue";
import Sidebar from "@/components/Sidebar.vue";
import DmButton from "@/components/Button.vue";

export default defineComponent(
  components: 
    Navbar,
    Sidebar,
    DmButton,
  ,
  setup() 
    const showSidebar = ref(true);

    return 
      showSidebar,
    ;
  ,
);
</script>

我的 App.vue 有一个 showSidebar 布尔引用,它在侧边栏组件上作为 v-model 提供。还有一个 dm 按钮(我编写的扩展按钮组件)在单击时切换 showSidebar 的值(我知道这是可行的,我可以在 Vue Devtools 的 App.vue 组件中看到 showSidebar 值发生变化)。但是,更改不会反映在侧边栏组件中。我不确定我在这里缺少什么。

仅供参考,此代码已简化为删除了一些不必要的内容(例如侧边栏和导航栏内的内容)。

【问题讨论】:

【参考方案1】:

要将v-model 与组件一起使用,您应该将属性命名为modelValue 并发出一个名为update:modelValue 的事件来改变它:

<template>
  <aside v-if="modelValue" class="min-h-screen">
    <slot></slot>
  </aside>
</template>

<script lang="ts">
import  defineComponent, ref, watch  from "vue";

export default defineComponent(
  name: "Sidebar",
  props: 
    modelValue: 
      type: Boolean,
      default: true,
    ,
  ,
emits:['update:modelValue'],
  setup(props,emit) 
  

      function close()
         emit('update:modelValue',false)
      

    return 
      close,
      mountSidebar,
    ;
  ,
);
</script>

【讨论】:

我想我只是不明白孩子需要发射部分来监听 v-model 的变化,我认为这只是为了让父母可以监听发射并进行调整如果子组件也有办法改变它自己的状态,父组件需要知道它的状态。名称 modelValue 重要还是只是为了清楚起见?【参考方案2】:

通过从 props.value 创建一个新的 ref,你失去了反应性。 propsreactive,所以你可以使用toReftoRefs

const mountSidebar = toRef(props, 'value');
// or
const value:mountSidebar = toRefs(props);

【讨论】:

mountSidebar 并不意味着反应,它只是从道具中获取其初始值。最终会有一些逻辑,以便在更新 v-model 时启动过渡动画,这将手动改变 mountSidebar 以及可能我尚未编码的另一个值。 @BoussadjraBrahim 的解决方案解决了我的问题。

以上是关于使用 Typescript 在 Vue3 组合 API 中观察 Value Prop的主要内容,如果未能解决你的问题,请参考以下文章

在Vue 3中使用Typescript和Jsx

Vite2+Vue3+TypeScript:搭建企业级轻量框架实践

如何使用 Vue3 和 Typescript 在 Quasar Framework 中定义 ref 方法的类型

Vue 3 使用 Typescript 注入

vue3组合式API介绍

vite + vue3 添加 typescript