#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>