При использовании props в компонентах следует учитывать, что данные в props представляют однонаправленный поток данных от родительского компонента к дочерним компонентам. Изменение свойства родителя приведет к изменению в дочерних компонентах. Однако дочерние компоненты не могут изменить свойство родителя. То есть поток данных идет только в одном направлении: родителя к потомкам. Props представяют свойства, доступные только для чтения.
Более того при любом обновлении родительского компонента у дочерних компонентов обновляются значения, передаваемые через props. При обратной ситуации, если мы попробуем изменить в дочерних компонентах значения, переданные через props, то на консоль браузера будет выведено предупреждение о том, что это не следует делать, а сами значения никак не изменятся. Например, рассмотрим следующую ситуацию:
<!DOCTYPE html> <html> <head> <title>Компоненты Vue 3</title> <meta charset="utf-8" /> </head> <body> <div id="app"> <h2>Hello, {{name}}</h2> <user-edit :user="name"></user-edit> </div> <script src="https://unpkg.com/vue"></script> <script> const app = Vue.createApp({ data(){ return { name: 'Tom' } } }); app.component('user-edit', { props: ["user"], template: '<div><input type="text" v-model="user" /><p>Name: {{user}}</p></div>' }); app.mount('#app'); </script> </body> </html>
Из родителя - объекта приложения Vue в компонент user-edit через props передается значение name. В этом компоненте мы можем попробовать управлять переданным значением через поле ввода и механизм двусторонней привязки:
<input type="text" v-model="user" />
Но если мы попробуем изменить через поле ввода данное значение, то оно изменится только в рамках компонента user-edit, а на консоль будет выведено предупреждение:
Однако могут быть ситуации, когда нам все таки нужно изменить значения в props. Что делать в этом случае? В этом случае мы можем определить локальную переменную. Через props устанавливается ее начальное значение, и затем компонент работает только с этой переменной:
<!DOCTYPE html> <html> <head> <title>Компоненты Vue 3</title> <meta charset="utf-8" /> </head> <body> <div id="app"> <h2>Hello, {{name}}</h2> <user-edit :user="name"></useredit> </div> <script src="https://unpkg.com/vue"></script> <script> const app = Vue.createApp({ data(){ return { name: 'Tom' } } }); app.component('user-edit', { props: ["user"], data() { return { userName: this.user} }, template: '<div><input type="text" v-model="userName" /><p>Name: {{userName}}</p></div>' }); app.mount('#app'); </script> </body> </html>
Теперь все действия будут идти с локальной переменной userName. Значения из props в компоненте не будут изменяться, а изменения в родительском компоненте никак не повлияют на значение переменной userName.
Предложенное выше решение позволяет изменять внутри компонента данные, полученные от родительского компонента. Но тогда мы можем столкнуться с другой проблемой - а что, если родительский компонент также изменит данные:
<!DOCTYPE html> <html> <head> <title>Компоненты Vue 3</title> <meta charset="utf-8" /> </head> <body> <div id="app"> <div><h3>Vue App</h3><input type="text" v-model="name" /><p>Hello {{name}}</p></div> <user-edit :user="name"></useredit> </div> <script src="https://unpkg.com/vue"></script> <script> const app = Vue.createApp({ data(){ return { name: 'Tom' } } }); app.component('user-edit', { props: ["user"], data() { return { userName: this.user} }, template: '<div><h3>Component</h3><input type="text" v-model="userName" /><p>User name: {{userName}}</p></div>' }); app.mount('#app'); </script> </body> </html>
В данном случае при измнении в родительском компоненте в дочерний компоненте по прежнему изменяется свойство "user" из props. Однако привязка элементов интерфейса идет не к свойству user из props, а к свойству userName из data. Поэтому компонент визуально никак не отразит изменения в родительском компоненте:
Что делать в этом случае? В этом случае мы можем отслеживать изменение user из props и соответственно обновлять свойство userName:
app.component('user-edit', { props: ["user"], data() { return { userName: this.user} }, watch: { user(newVal) { this.userName = newVal; } }, template: '<div><h3>Component</h3><input type="text" v-model="userName" /><p>User name: {{userName}}</p></div>' });
Теперь с помощью параметра watch определяется наблюдаемое свойство, которое называется также, как и отслеживаемое свойство user из props. Параметр newVal представляет новое значение, которое мы можем присвоить свойству userName и таким образом синхронизировать дочерний компонент с родительским.