Subscribe on changes!

strange behaviors of Proxy objects with getter in script setup

avatar
Jun 1st 2023

Vue version

3.3.4

Link to minimal reproduction

https://play.vuejs.org/#eNp9Ut9vmzAQ/ldOVqUQKcCWvjGCOk172sve60rlxzUhBduzDU1E+d93tpuKptNAiDt/n7/Pd76JfVcqGQdkGctNrVtlwaAdVMEFQNsrqS1MGsvatiPO8KRlDyvir745gvtqKYwFjX8GNBabX3g2sKM8bInuH9aeGmiyOhI4lRmsShjLjoRmgrl9h7cOrwivFvjSSWl5OhNJ4Av8dnFEuzYwOQbAHm0GkS01BRt4xvMadsUFvEh4YZIItHtiPcDrq3f3iXd0z4eqEjWYQ/R4MxFlzuBm8jLzY6gv0O2gRZAPp7az+82Okqehv9RZSiz2qist+j7nh9timkJlSQnznKe08gmprhAY4/Zpx9nbPs6KH1I0rW2lKLt/Swgpfp5aY81SKrC2he/mpWbo5J4Y26Bh8UTNwhIyXxyZfmzNUbYiWnEuVmvOQMsXQ5SvXyiuZRdiSlISy9P30tmGhQmL+1IlRyMFTaG/Kv4GGM6yy+VxRmPncs4O1iqTpekg1PM+qWWf3hGW6kHYtse4kf3dbUJv2lCpy+UETR9X7nioyZCzzUI7pcURdaxRNKhR/9frirv0u4I+efqJ4GJm819VZS2R

Steps to reproduce

  1. Define Proxy object with getter in <scrip setup> or define it in another module and import (result are same). For example:
<script setup>
    const obj = {a: 1, b: 2, c: 3};
    const proxy = new Proxy(obj, {
        get: (target, key) => key in target ? target[key] : 'Not present'
    });
</script>
  1. Call any property from proxy in <template> section. For example:
<template>
    <div>{{ proxy.a }}</div>
    <div>{{ proxy.notExists }}</div>
</template>

What is expected?

Proxy getter call result

What is actually happening?

Each property on template calls getter twice. First time with '__v_isRef' key, second call with requested key.

This can be seen if add some logic to Proxy getter function:

const proxy = new Proxy(obj, {
    get: (target, key) => {
        console.log(key);
        return key in target ? target[key] : 'Not present'
    };
});

System Info

System:
    OS: Windows 10 10.0.22621
    CPU: (16) x64 AMD Ryzen 7 3700X 8-Core Processor
    Memory: 1.68 GB / 31.93 GB
  Binaries:
    Node: 18.3.0 - D:\ZBIN\node-v18.3.0-x64\node.EXE
    npm: 8.12.1 - D:\ZBIN\node-v18.3.0-x64\npm.CMD
  Browsers:
    Chrome: 113.0.5672.127
    Edge: Spartan (44.22621.1344.0), Chromium (113.0.1774.57)
    Internet Explorer: 11.0.22621.1
  npmPackages:
    vue: 3.3.4 => 3.3.4

Any additional comments?

Temporary fix in user code:

const proxy = new Proxy(obj, {
    get: (target, key) => {
        if (key === '__v_isRef') {
            return undefined;
        }
        ...
    }
});
avatar
Jun 3rd 2023

@Bobsans Why do you want to use Proxy directly when you have ref/reactive?

Here is the line where __v_isRef accessedhttps://github.com/vuejs/core/blob/a95e612b252ae59eaf56e0b8ddba66948d4ac20e/packages/reactivity/src/ref.ts#L80

In template unref is used to avoid .value access https://github.com/vuejs/core/blob/a95e612b252ae59eaf56e0b8ddba66948d4ac20e/packages/reactivity/src/ref.ts#L212

unref is using isRef which is accessing __v_isRef, hence the console logs.

Here is list of non-trackable keys (Vue takes care of these properties internally) https://github.com/vuejs/core/blob/a95e612b252ae59eaf56e0b8ddba66948d4ac20e/packages/reactivity/src/baseHandlers.ts#L35

Please explain the intention behind using Proxy directly instead of ref/reactive, for me this is working as expected.