Можно ли наблюдать за введенным свойством

#vue.js #vuejs3

#vue.js #vuejs3

Вопрос:

Я создаю приложение, которое использует Vue 3, и я предоставляю свойство в родительском компоненте, которое я впоследствии внедряю в несколько дочерних компонентов. Есть ли какой-либо способ для компонента, которому вводится это свойство, отслеживать его изменения?

Родительский компонент выглядит примерно так:

 <template>
  <child-component/>
  <other-child-component @client-update="update_client" />
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      client: {}
    }
  },
  methods: {
    update_client(client) {
      this.client = client
    }
  },
  provide() {
    return {
      client: this.client
    }
  },
}
</script>
  

Дочерний компонент выглядит так:

 <script>
export default {
  name: 'ChildComponent',
  inject: ['client'],
  watch: {
    client(new_client, old_client) {
      console.log('new client: ', new_client);
    }
  }
}
</script>
  

Я пытаюсь добиться этого, когда предоставленная переменная обновляется в родительском, дочерние компоненты, в которые она вводится, должны получать уведомления. По какой-то причине метод просмотра клиента не вызывается при обновлении клиента.

Есть ли лучший способ добиться этого?

Обновить

После дальнейшего тестирования я вижу, что здесь есть более серьезная проблема: в дочернем компоненте даже после обновления клиента в родительском, свойство client остается исходным пустым объектом и не обновляется. Поскольку предоставленное свойство является реактивным, все места, в которые оно вводится, должны автоматически обновляться.

Комментарии:

1. Вы добавили реактивность к введенному client ? v3.vuejs.org/guide /…

2. Да, поскольку его значение задается из свойства в data(), оно автоматически реагирует.

Ответ №1:

Обновить

При использовании Object API reactive definition ( data(){return{client:{}} ), даже если переменная является реактивной внутри компонента, значение inject ed будет статическим. Это связано с тем, что provide будет установлено значение, на которое оно изначально установлено. Чтобы реактивность работала, вам нужно будет обернуть ее в computed

 provide(){
  return {client: computed(()=>this.client)}
}
  

Документы:
https://vuejs.org/guide/components/provide-inject.html#working-with-reactivity


Вам также может потребоваться использовать deep для ваших часов

Пример:

 <script>
  export default {
    name: 'ChildComponent',
    inject: ['client'],
    watch: {
      client: {
        handler: (new_client, old_client) => {
          console.log('new client: ', new_client);
        },
        deep: true
      }
    }
  }
</script>
  

Комментарии:

1. Добавление deep в конфигурацию watch не помогло

Ответ №2:

Как описано в официальной документации ( https://v2.vuejs.org/v2/api/#provide-inject ), по умолчанию привязки provide и inject не являются реактивными. Но если вы передаете наблюдаемый объект, свойства этого объекта остаются реактивными.

Для объектов Vue не может обнаружить добавление или удаление свойства. Итак, проблема в вашем коде может быть здесь:

   data() {
    return {
      client: {}
    }
  },
  

Поскольку вы изменяете свойство client этого объекта ( this.client.client = client ), вы должны объявить этот ключ в data, например:

   data() {
    return {
      client: { client: null }
    }
  },
  

Теперь оно становится реактивным.

Я создал песочницу кода, воспроизводящую ваш код, наблюдающий за введенным свойством: https://codesandbox.io/s/vue-inject-watch-ffh2b

Ответ №3:

По какой-то причине единственным способом заставить это работать было только обновление свойств исходного введенного объекта вместо замены всего объекта. Я также не смог начать watch работать с введенным свойством, несмотря на настройку deep: true .

Обновленный родительский компонент:

 <template>
  <child-component/>
  <other-child-component @client-update="update_client" />
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      client: {}
    }
  },
  methods: {
    update_client(client) {
      this.client.client = client
    }
  },
  provide() {
    return {
      client: this.client
    }
  },
}
</script>
  

Обновленный дочерний компонент:

 <template>
  <button @click="get_client">Get client</button>
</template>
<script>
export default {
  name: 'ChildComponent',
  inject: ['client'],
  methods: {
    get_client() {
      console.log('updated client: ', client);
    }
  }
}
</script>
  

Ответ №4:

создайте новое значение и ссылайтесь на значение из inject в него

 inject: ['client'],
data: () => ({
  value: null,
}),
created() {
  this.value = this.client;
},
watch: {
  value: {
    handler() {
      /* ... */
    },
    deep: true,
  }
}
  

Теперь вы можете посмотреть значение.

Примечание: «inject» должен быть объектом

Ответ №5:

Я столкнулся с той же проблемой. Но мне просто нужно было более внимательно изучить детали в документах, чтобы заставить его работать. В итоге у меня все работало нормально.

Я создал плагин vue, предоставляющий карту вместе с некоторой функцией в качестве ссылки только для чтения. Затем он начинает изменять содержимое карты раз в секунду:

plugin.js

     import { ref, readonly } from 'vue';

    const rRuns = ref( new Map() );
    let time = 0;
    
    export default
    {
      install(app, defFile)
      {
        ...
      
        app.provide( "runs", readonly(
        { ref: rRuns,
          get: (e) => rRuns.value.get( e ),
          locationNames: () => rRuns.value.keys(),
          size: () => rRuns.value.size,
        } ) );
    
        ...
        
        setInterval( () => 
          { time  ;
            const key = (time * 7) % 10;
            console.log("  runs update", key, time);
            rRuns.value.set( key.toString(), time )
          }, 1000);
          console.log("  time Interval start" );
      }
    }
  

main.js:

     import { createApp } from 'vue'
    import App from './App.vue'
    
    import plugin from 'plugin.js'; 
    
    const app = createApp(App);
    app.config.unwrapInjectedRef = true;
    app.use(game, 'gamedefs.json');
    app.mount('#app');
  

запускает.vue:

     <template>
      <h1>Runs:</h1>
      <p v-if="!runs.size()">amp;< no runs amp;></p>
      <p v-else>runs: {{ runs.size() }}</p>
      <button v-for="r of runs.locationNames()" :key="r" @click="display( r )">[{{ r }}]</button>
    </template>
    
    <script>
    export default {
      name: 'Runs',
    
      inject: 
      { 
        runs: { from: 'runs' },
      },
    
      watch:
      { 
        'runs.ref':
        {
          handler( v )
          {
            console.log("runs.ref watch", v );
          },
          immediate: true,
          deep: true,
        },
      },
    }
    </script>