0

Vue.js proxies its objects to catch property accesses. I seem to have found a leak in the abstraction: Object.keys doesn't return props in the list of keys.

With the following Vue component:

function callMe() {
  var comp = Vue.component("comp", {
    template: "<button @click='clickMe()'>xxx</button>",
    props: {
        label: String,
        cardId: String,
        collapsible: {
            type: Boolean,
            default: true,
        },
        collapsed: Boolean,
    },
    data() {
        console.log(Object.keys(this))
        console.log(this.collapsible)
        console.log(Object.keys(this).includes("collapsible"))
        return { isCollapsed: this.collapsed }
    },
    methods: {
       clickMe(){
         console.log(this)
       } 
    }

  })
  var vm = new Vue({
    el: '#root',
    template: "<comp></comp>",
  })
}
callMe();
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id='root'>
 
 <button @click="clickMe()" >Click Me</button>

</div>

The console output is:

(29) ["_uid", "_isVue", "$options", "_renderProxy", "_self", "$parent", "$root", "$children", "$refs", "_watcher", "_inactive", "_directInactive", "_isMounted", "_isDestroyed", "_isBeingDestroyed", "_events", "_hasHookEvent", "_vnode", "_staticTrees", "$vnode", "$slots", "$scopedSlots", "_c", "$createElement", "$attrs", "$listeners", "_watchers", "_props", "toggleThis"]
true
false

(Interestingly, when I call the check later, the isCollapsed item is in the list. You'll also notice that clickMe method is also present. It seems that only props are left out.)

Why is this happening?

More generally, how does Vue's Proxy object emit a different set of keys than it can then access?

This is a problem for me because I'm trying something fancy with pug-vdom and that internally uses Object.keys to enumerate the variables to inject into the Pug template.

Is this a Vue bug? Alternately, is it possible to access a list of props keys from the this object, and export an object whose keys contain the props as well?

edit: added a runnable code snippet that demonstrates the problem.

2
  • 1
    Kind of a pug-vdom bug. Non-enumerable properties are a thing, they don’t require proxies, and they aren’t included in Object.keys. Same with properties on an object’s prototype. Commented Aug 21, 2018 at 19:18
  • I mean, I don't think pug-vdom is at the stage where it would care to work with any random object someone passes them, I'll assume responsibility for passing weird things to the render method. Still, why would Vue make props non-enumerable? (thanks for a good keyword to search for though) Commented Aug 21, 2018 at 19:22

1 Answer 1

1

Object.keys() does not iterate over prototype properties.
Also child component is inherited from root component. This means props & data fields must be inside __proto__ of child components.

Hence if we do Object.keys(this__proto__).includes("collapsible") , it returns true in child component.

If you want to access those fields from child components then use this.$props and this.$data .

function callMe() {
  var comp = Vue.component("comp", {
    template: "<button @click='clickMe()'>xxx</button>",
    props: {
        label: String,
        cardId: String,
        collapsible: {
            type: Boolean,
            default: true,
        },
        collapsed: Boolean,
    },
    data() {
        console.log('Inside Child:',Object.keys(this))
        console.log(this.collapsible)
        console.log(Object.keys(this.__proto__).includes("collapsible"))
        console.log(Object.keys(this).includes("collapsible"))
        return { isCollapsed: this.collapsed }
    },
    methods: {
       clickMe(){
         console.log(this)
       } 
    }

  })
  var vm = new Vue({
    el: '#root',
    template: "<comp></comp>",
    props:{
    jack:{
          type: Boolean,
          default: true
         }
    },
    data(){
      console.log('Inside parent:', this.jack)
      return {}
    }
  })
}
callMe();
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<div id='root'>
 
 <button @click="clickMe()" >Click Me</button>

</div>

Sign up to request clarification or add additional context in comments.

11 Comments

It doesn’t return true, though, in the question…
Why do you think so? Can you see the snippet.
Because it says “the console output is … true false”, and the entire question is about why that is.
I replicated his code, but its not showing true false as collapsible key is present in the this object. you can see my snippet
You can check this.$props or this._props also if they contain the prop fields
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.