关于javascript:在基于TypeScript的Vue中将vuex状态和突变绑定到复选框组件属性

Bind vuex state and mutations to checkbox component properties in TypeScript-based Vue

问题

将复选框创建为 Vue 组件,其中:

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

    概念

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

    零件

    帕格语模板:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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 }}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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;
    }

    存储模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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;
      }
    }

    用法

    1
    2
    3
    4
    5
    SimpleCheckbox(
      :checked="relatedStoreModule.doNotPreProcessMarkupEntryPointsFlag"
      :onClick="relatedStoreModule.toggleDoNotPreProcessMarkupEntryPointsFlag"
      parentElementCssClass="RegularCheckbox"
    )
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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 概念。

    enter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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" 中。看起来它编译为类似 <component>.$props.onClick="<vuex store manipulations ...>" 的东西 - 如果是这样,它是组件内部的隐式属性突变。

    试试 2

    概念

    基于 Vue 文档,自定义组件部分:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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 的等价物是:

    1
    2
    3
    4
    5
    6
    import { Vue, Component, Model } from 'vue-property-decorator'

    @Component
    export default class YourComponent extends Vue {
      @Model('change', { type: Boolean }) readonly checked!: boolean
    }

    零件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
        // ...
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;
    }

    用法

    1
    2
    3
    4
    SimpleCheckbox(
      v-model="doNotPreProcessMarkupEntryPointsFlag"
      rootElementCssClass="RegularCheckbox"
    )

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @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();
      }
    }

    警告

    相同的错误集:

    enter

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

    用法

    1
    2
    3
    4
    5
    SimpleCheckbox(
      :checked="relatedStoreModule.doNotPreProcessMarkupEntryPointsFlag"
      @toggled="relatedStoreModule.toggleDoNotPreProcessMarkupEntryPointsFlag"
      rootElementCssClass="RegularCheckbox"
    )

    警告

    enter


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

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

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

    结论

    npm link 引起这样的问题已经不是第一次了。这是另一种情况。

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


    我不确定这是基于typescript的问题。

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    props: {
      checked: {
        type: Boolean,
        default: false,
      },
      change: {
        type: Function,
        default: () => {},
      }
    },
    data: {
      checkedModel: false,
    },
    mounted() {
      this.checkedModel = this.checked; // init model value as prop
    }