Meituan-Dianping/mpvue · warning

Avoid adding reactive properties to a Vue instance or its ro

Error message

Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option.

What it means

Warning from Vue.set / vm.$set (set in observer/index.js) when the target is a Vue instance or the root $data object. Adding properties to these at runtime cannot be made reactive reliably, so Vue warns and returns the value without making it reactive.

Source

Thrown at src/core/observer/index.js:238

/**
 * Set a property on an object. Adds the new property and
 * triggers change notification if the property doesn't
 * already exist.
 */
export function set (target: Array<any> | Object, key: any, val: any): any {
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  if (hasOwn(target, key)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  // Vue.set 添加对象属性,渲染时候把 val 传给小程序渲染
  if (!target.__keyPath) {
    def(target, '__keyPath', {}, false)
  }
  target.__keyPath[key] = true
  ob.dep.notify()
  return val
}

View on GitHub (pinned to 6c5d78ee04)

Solutions

  1. Declare all keys upfront in the data() option, even with placeholder values
  2. If the set of keys is dynamic, nest them: data(){return{fields:{}}} and Vue.set(this.fields, key, val)
  3. Restructure dynamic items into arrays, which Vue can react to via splice/push
  4. If purely non-reactive metadata is needed, store it on this outside data and skip reactivity

Example fix

// before
this.$set(this.$data, 'field_' + i, '')
// after
export default {
  data() { return { fields: {} } },
  methods: { addField(i) { this.$set(this.fields, 'field_' + i, '') } }
}
Defensive patterns

Strategy: validation

Validate before calling

function isSafeSetTarget(target) {
  return target != null && typeof target === 'object' && !target._isVue && !(target.__ob__ && target.__ob__.vmCount)
}
// only call Vue.set when isSafeSetTarget(target)

Type guard

function isVueInstanceOrRootData(t) { return t != null && (t._isVue === true || (t.__ob__ && t.__ob__.vmCount > 0)) }

Prevention

When it happens

Trigger: Vue.set(vm, 'newKey', value); Vue.set(this.$data, 'newKey', value); this.$set(this.$data, key, val) for dynamic form fields; assigning new keys onto vm directly after creation and expecting reactivity.

Common situations: Dynamic form schemas adding fields after mount; generic key/value editors bound directly to $data; plugins attempting to extend instances post-creation instead of at init.

Related errors


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