Meituan-Dianping/mpvue · warning

Avoid adding reactive properties to a Vue instance or its…

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.

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.

Appendix: 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)