[Vue warn]: $attrs is readonly.

背景: 封装confirm组件,当点击按钮弹出confirm时,报错[Vue warn]: $attrs is readonly.$listeners is readonly.第一次的时候不会出现,点击第二次第三次累加出现报错。使用方式this.$Confirm.error({xxxx})使用

vue@2.6.14版本

<script>
// component.vue
import Vue from 'vue';
import Modal from '../modal';

const timestamp = new Date().getMilliseconds();
let count = 0;

const Confirm = {
  name: 'confirm',

  components: {
    Modal
  },

  props: {
    value: {
      type: Boolean,
      default: false
    },
    modalProps: {
      type: Object,
      default() {
        return {};
      }
    },
    title: {
      type: String,
      default: '提示'
    },
    icon: String,
    color: String,
    content: [String, Function]
  },

  data() {
    return {
      visible: this.value
    };
  },

  watch: {
    value(v) {
      this.visible = v;
    },
    visible(v) {
      this.$emit('input', v);
    }
  },

  methods: {
    onInput(v) {
      this.visible = v;
    },
    onConfirm() {
      this.$emit('confirm');
    },
    onCancel() {
      this.$emit('cancel');
    }
  },
  render(h) {
    const {
      visible,
      modalProps,
      title,
      icon,
      color,
      content,
      $slots
    } = this;
    const node = typeof content === 'string' ? content : content(h);

    return (
      <modal
        title={title}
        width={450}
        {...{ props: modalProps }}
        value={visible}
        on-input={this.onInput}
        on-confirm={this.onConfirm}
        on-cancel={this.onCancel}
      >
        <div class="mc-confirm-body">
          <div class="mc-confirm-text">{ $slots.default || node }</div>
        </div>
      </modal>
    );
  }
};

const Popup = {
  name: 'PopUp',
  components: {
    Confirm
  },
  props: {
    popupClass: String,
    Component: {}
  },
  data() {
    return {
      popups: [],
      colorMap: {
        info: 'info',
        success: 'success',
        error: 'error',
        warning: 'warning '
      },
      iconMap: {
        info: 'info-circle-o',
        success: 'check-circle-',
        error: 'close-circle-o',
        warning: 'exclamation-circle-o '
      }
    };
  },
  methods: {
    add(props) {
      count++;
      props.name = props.name || `${timestamp}-${count}`;
      this.popups.push(props);

      return () => {
        this.close(props.name);
      };
    },
    close(name) {
      let index = -1;
      this.popups.forEach((item, i) => {
        if (item.name === name) {
          index = i;
        }
      });

      if (index !== -1) {
        this.popups.splice(index, 1);
      }
    },
    closeAll() {
      this.popups = [];
    }
  },
  render() {
    const {
      popupClass,
      popups,
      colorMap,
      iconMap
    } = this;
    const fn = () => {};

    const nodes = popups.map((item, i) => {
      item.color = colorMap[item.type];
      item.icon = item.icon || iconMap[item.type];

      return (
        <confirm
          {...{ props: item }}
          value={item.value}
          key={i}
          on-confirm={item.onOk || fn}
          on-cancel={item.onCancel || fn}
        >
          { item.content }
        </confirm>
      );
    });

    return (
      <div
        class={popupClass}
      >{nodes}</div>
    );
  }
};

Confirm.createWrapper = (props = {}) => {
  const Instance = new Vue({
    render(h) {
      return h(Popup, {
        props: props
      });
    }
  });
  const popup = Instance.$mount();
  document.body.appendChild(popup.$el);
  return Instance.$children[0];
};

export default Confirm;
</script>
index.js
Confirm.install = function (Vue) {
  const types = ['info', 'success', 'error', 'warning'];

  types.forEach(type => {
    Confirm[type] = xxx;

  Confirm.confirm = xxxx;
  Confirm.closeAll =xxxx;

  Vue.prototype.$Confirm = Confirm;
  Vue.component(Confirm.name, Confirm);
};

export default Confirm;
阅读 8.7k
2 个回答
新手上路,请多包涵

我这边解决的方法 是 vue 和 vue-template-compiler的版本一致 就可以了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏