【问题标题】:Bind vuex state and mutations to checkbox component properties in TypeScript-based Vue在基于 TypeScript 的 Vue 中将 vuex 状态和突变绑定到复选框组件属性
【发布时间】:2020-01-26 09:00:58
【问题描述】:

问题

将复选框创建为 Vue 组件,特此:

  1. 复选框组件内不允许有逻辑:所有事件处理程序以及checked 属性完全依赖于外部逻辑,可能是vuex 存储。
  2. 我们不应该看到复选框“选中”状态:选中与否,这又取决于外部逻辑,例如。 G。 vuex 状态或吸气剂。

试试 1

概念

复选框组件具有checkedonClick 属性,它们的值是偏离的,可能是动态的。

组件

Pug 语言的模板:

label.SvgCheckbox-LabelAsWrapper(:class="rootElementCssClass" @click.prevent="onClick")
  input.SvgCheckbox-InvisibleAuthenticCheckbox(
    type="checkbox"
    :checked="checked"
    :disabled="disabled"
  )
  svg(viewbox='0 0 24 24').SvgCheckbox-SvgCanvas
    path(
      v-if="!checked"
      d='M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z'
    ).SvgCheckbox-SvgPath.SvgCheckbox-SvgPath__Unchecked
    path(
      v-else
      d='M10,17L5,12L6.41,10.58L10,14.17L17.59,6.58L19,8M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3Z'
    ).SvgCheckbox-SvgPath.SvgCheckbox-SvgPath__Checked
  span(v-if="text").SvgCheckbox-AppendedText {{ text }}
import { Vue, Component, Prop } from 'vue-property-decorator';

@Component
export default class SimpleCheckbox extends Vue {

  @Prop({ type: Boolean, required: true }) private readonly checked!: boolean;

  @Prop({ type: Boolean, default: false }) private readonly disabled!: boolean;

  @Prop({ type: String }) private readonly text?: string;
  @Prop({ type: String }) private readonly parentElementCssClass?: string;

  @Prop({ type: Function, default: () => {} }) private readonly onClick!: () => void;
}

存储模块

import { VuexModule, Module, Mutation } from "vuex-module-decorators";
import store, { StoreModuleNames } from "@Store/Store";


@Module({ name: StoreModuleNames.example, store, dynamic: true, namespaced: true })
export default class ExampleStoreModule extends VuexModule {

  private _doNotPreProcessMarkupEntryPointsFlag: boolean = true;

  public get doNotPreProcessMarkupEntryPointsFlag(): boolean {
    return this._doNotPreProcessMarkupEntryPointsFlag;
  }

  @Mutation
  public toggleDoNotPreProcessMarkupEntryPointsFlag(): void {
    this._doNotPreProcessMarkupEntryPointsFlag = !this._doNotPreProcessMarkupEntryPointsFlag;
  }
}

用法

SimpleCheckbox(
  :checked="relatedStoreModule.doNotPreProcessMarkupEntryPointsFlag"
  :onClick="relatedStoreModule.toggleDoNotPreProcessMarkupEntryPointsFlag"
  parentElementCssClass="RegularCheckbox"
)
import { Component, Vue } from "vue-property-decorator";
import { getModule } from "vuex-module-decorators";
import ExampleStoreModule from "@Store/modules/ExampleStoreModule";
import template from "@Templates/ExampleTemplate.pug";
import SimpleCheckbox from "@Components/Checkboxes/MaterialDesign/SimpleCheckbox.vue";

@Component({ components: { SimpleCheckbox } })
export default class MarkupPreProcessingSettings extends Vue {
  private readonly relatedStoreModule: ExampleStoreModule = getModule(ExampleStoreModule);
}

华林斯

点击复选框时出现。复选框按我们的需要工作,但是违反了一些 Vue 概念。

vue.common.dev.js:630 [Vue warn]: $attrs is readonly.

found in

---> <SimpleCheckbox> at hikari-frontend/UiComponents/Checkboxes/MaterialDesign/SimpleCheckbox.vue
       <MarkupPreProcessingSettings>
         <Application> at ProjectInitializer/ElectronRendererProcess/RootComponent.vue
           <Root>

vue.common.dev.js:630 [Vue warn]: $listeners is readonly.

found in

---> <SimpleCheckbox> at hikari-frontend/UiComponents/Checkboxes/MaterialDesign/SimpleCheckbox.vue
       <MarkupPreProcessingSettings>
         <Application> at ProjectInitializer/ElectronRendererProcess/RootComponent.vue
           <Root>

vue.common.dev.js:630 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "checked"

found in

---> <SimpleCheckbox> at hikari-frontend/UiComponents/Checkboxes/MaterialDesign/SimpleCheckbox.vue
       <MarkupPreProcessingSettings>
         <Application> at ProjectInitializer/ElectronRendererProcess/RootComponent.vue
           <Root>

沉思

此警告经常发出,原因是某些 vue 属性的新值已在组件内部分配。明确地说,我没有这样的操作。

问题出在:onClick="relatedStoreModule.toggleDoNotPreProcessMarkupEntryPointsFlag"。看起来它编译成类似&lt;component&gt;.$props.onClick="&lt;vuex store manipulations ...&gt;" 的东西 - 如果是这样,它是组件内部的隐式属性突变。

试试 2

概念

基于Vue documentation, Customizing Component section:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

带有vue-property-decorator 的 TypeScript 的等效项是:

import { Vue, Component, Model } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @Model('change', { type: Boolean }) readonly checked!: boolean
}
零件
label.SvgCheckbox-LabelAsWrapper(:class="rootElementCssClass")
  input.SvgCheckbox-InvisibleAuthenticCheckbox(
    type="checkbox"
    :checked="checked"
    :disabled="disabled"
    @change="$emit('change', $event.target.checked)"
  )
  svg(viewbox='0 0 24 24').SvgCheckbox-SvgCanvas
    // ...
import { Vue, Component, Prop, Model } from "vue-property-decorator";

@Component
export default class SimpleCheckbox extends Vue {

  @Model('change', { type: Boolean }) readonly checked!: boolean;

  @Prop({ type: Boolean, default: false }) private readonly disabled!: boolean;

  @Prop({ type: String }) private readonly text?: string;
  @Prop({ type: String }) private readonly rootElementCssClass?: string;
}

用法

SimpleCheckbox(
  v-model="doNotPreProcessMarkupEntryPointsFlag"
  rootElementCssClass="RegularCheckbox"
)

在 TypeScript 中,要使用 v-model,我们需要声明 getter 和同名 setter:

@Component({
  template,
  components: {
    SimpleCheckbox,
    // ...
  }
})
export default class MarkupPreProcessingSettings extends Vue {

  private readonly relatedStoreModule: MarkupPreProcessingSettingsStoreModule =
      getModule(MarkupPreProcessingSettingsStoreModule);
  //...
  private get doNotPreProcessMarkupEntryPointsFlag(): boolean {
    return this.relatedStoreModule.doNotPreProcessMarkupEntryPointsFlag;
  }

  private set doNotPreProcessMarkupEntryPointsFlag(_newValue: boolean) {
    this.relatedStoreModule.toggleDoNotPreProcessMarkupEntryPointsFlag();
  }
}

警告

相同的错误集:

限制

首先,我们需要在 Vue 组件类中创建新的 getter 和 setter。如果可能的话,避免 id 会很酷。不幸的是,对于 vuex 类(vuex-module-decorators),TypeScript setter 不可用,我们需要使用@Mutation-decorated 方法。

此外,此解决方案不适用于v-for 呈现的元素。它使这个解决方案毫无用处。

试试 3

概念

事件发射器和自定义事件监听器的使用。此解决方案也可以正常工作,但 Vue 会发出警告。

组件

label.SvgCheckbox-LabelAsWrapper(:class="rootElementCssClass" @click.prevent="$emit('toggled')")
  // ...

用法

SimpleCheckbox(
  :checked="relatedStoreModule.doNotPreProcessMarkupEntryPointsFlag"
  @toggled="relatedStoreModule.toggleDoNotPreProcessMarkupEntryPointsFlag"
  rootElementCssClass="RegularCheckbox"
)

警告


更新

还有一些谜题,但是问题已经解决了。请参阅下面的答案。

【问题讨论】:

  • label.SvgCheckbox-LabelAsWrapper - 这是什么东西?如果您使用的是外部库,可以标记它们并在问题中提及它们。
  • @MatJ,它是Pug,预处理语言。如果您对 Pug 不熟悉,请使用this tool 获取预处理的 HTML。

标签: javascript typescript vue.js vue-component vuex


【解决方案1】:

此警告出现在 Electron 应用程序中。 SimpleCheckbox 来自node_modules,但是这个库还在开发中,所以它已经由npm link 提供。

当我尝试进行复制时,我为浏览器创建了 SPA,并将SimpleCheckbox 放置到同一个项目中(不是从node_modules 获得的)。第一个解决方案有效! (我不在乎第二个和第三个 - 我只需要从剥离优雅的解决方案中提炼出来)。

我建议原因是npm link,发布我的库并通过npm install 安装它。警告消失了!

结论

npm link 引起这样的问题已经不是第一次了。这里是another case

我仍然没有深入了解这个案例 - 我刚刚发布了一些实验数据。 “那么,如果图书馆还在开发中呢?”问题仍然没有答案。我试过Lerna - 第一次警告消失了,但是当我将我的项目移动到 Lerna 时,警告再次出现 - 我还不清楚规律性。

【讨论】:

    【解决方案2】:

    我不确定这是基于打字稿的问题。

    根据您的警告信息和代码,我可以注意到您使用prop 作为input 模型。

    默认情况下,prop 不允许被变异。

    突变prop 可能是个坏主意,即使它是ObjectArray。 (如果prop是ObjectArray,可以在children中变异,但不推荐)

    为避免出现此警告,您可以在子级中使用作为 prop 克隆的数据,如下所示:

    props: {
      checked: {
        type: Boolean,
        default: false,
      },
      change: {
        type: Function,
        default: () => {},
      }
    },
    data: {
      checkedModel: false,
    },
    mounted() {
      this.checkedModel = this.checked; // init model value as prop
    }
    

    【讨论】:

    • 感谢您的回答。不幸的是,它不适用于&lt;input :checked="checkedModel" /&gt;&lt;input v-model="checkedModel" /&gt;
    猜你喜欢
    • 2017-01-10
    • 2020-12-22
    • 2023-04-11
    • 2018-07-26
    • 2019-07-28
    • 2017-12-31
    • 2014-07-22
    • 2020-10-18
    • 2016-08-20
    相关资源
    最近更新 更多