Meituan-Dianping/mpvue · warning

Avoid replacing instance root $data. Use nested data propert

Error message

Avoid replacing instance root $data. Use nested data properties instead.

What it means

In development builds, the $data and $props instance accessors are defined with getters only plus a setter that warns. Assigning vm.$data = {...} is rejected (the getter value is unchanged) because replacing the root reactive data object would break reactivity and proxies.

Source

Thrown at packages/weex-vue-framework/factory.js:3308

    handler = handler.handler;
  }
  if (typeof handler === 'string') {
    handler = vm[handler];
  }
  return vm.$watch(keyOrFn, handler, options)
}

function stateMixin (Vue) {
  // flow somehow has problems with directly declared definition object
  // when using Object.defineProperty, so we have to procedurally build up
  // the object here.
  var dataDef = {};
  dataDef.get = function () { return this._data };
  var propsDef = {};
  propsDef.get = function () { return this._props };
  if (process.env.NODE_ENV !== 'production') {
    dataDef.set = function (newData) {
      warn(
        'Avoid replacing instance root $data. ' +
        'Use nested data properties instead.',
        this
      );
    };
    propsDef.set = function () {
      warn("$props is readonly.", this);
    };
  }
  Object.defineProperty(Vue.prototype, '$data', dataDef);
  Object.defineProperty(Vue.prototype, '$props', propsDef);

  Vue.prototype.$set = set;
  Vue.prototype.$delete = del;

  Vue.prototype.$watch = function (
    expOrFn,
    cb,

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Mutate properties on $data instead: Object.keys(newData).forEach(k => this[k] = newData[k])
  2. Reset individual top-level keys to their initial values
  3. Use a factory that returns initial state and assign key-by-key
  4. Replace the component (v-if / :key) to get fresh state

Example fix

// before
resetState() { this.$data = initialState(); }
// after
resetState() {
  const fresh = initialState();
  Object.keys(fresh).forEach(k => { this.$data[k] = fresh[k]; });
}
Defensive patterns

Strategy: validation

Validate before calling

function resetData(vm, initialFactory) {
  const fresh = initialFactory();
  Object.keys(fresh).forEach(k => { vm.$data[k] = fresh[k]; });
}

Type guard

function isRootDataReplacement(stmt) {
  return /\$data\s*=/.test(stmt);
}

Prevention

When it happens

Trigger: Writing vm.$data = newObj or Object.assign used via direct replacement like this.$data = initialState() in a component, typically to 'reset' state.

Common situations: Trying to reset component state to initial values, copying state between components, or migration code that swapped whole data objects after Vue 1's looser behavior.

Related errors


AI-assisted analysis of Meituan-Dianping/mpvue@6c5d78ee04 (2026-09-02). Data as JSON: /api/errors/98786fcd3baf5406. Report an issue: GitHub.