The words in front

   Most of the options accepted by components are related to Vue The instance is the same , And the options props Is a very important option in the component . stay Vue in , The parent-child relationship can be summarized as  props down,
events up. Parent component passed  props  Passing data down to subcomponents , Sub components pass  events  Send message to parent component . This article will introduce in detail Vue Component options props

 

Parent child component

   In the introduction props before , First, introduce the writing method of parent-child components

   It is important to decouple parent-child components as much as possible in a well-defined interface . This ensures that each component can be written and understood in a relatively isolated environment , It also greatly improves the maintainability and reusability of components

【 Wrong writing 】

   Now let's introduce two wrong ways to write parent-child components

   The following form is wrong , Because when a child component is registered with a parent component ,Vue.js The template of the parent component will be compiled , The content of the template has determined what the parent component will render HTML
  <parent>...</parent>
Runtime , Some of its sub tags will only be treated as normal HTML To execute ,<child></child> Not standard HTML label , Will be ignored by the browser directly
<div id="example"> <parent> <child></child> <child></child> </parent> </div>
    It is also an error to use a child component outside the parent component label
<div id="example"> <parent></parent> <child></child> </div>
【 Correct writing 】
<div id="example"> <parent></parent> </div> <script> var childNode = {
template: '<div>childNode</div>', } var parentNode = { template: ` <div
class="parent"> <child></child> <child></child> </div> `, components: {
'child': childNode } }; // Create root instance new Vue({ el: '#example', components: {
'parent': parentNode } }) </script>
 

static state props

   The scope of the component instance is isolated . That means no ( It shouldn't be ) Directly reference the data of the parent component within the template of the child component . To have child components use the data of the parent component , The  props  option

   use Prop Data transfer includes static and dynamic forms , Let's start with static props

   Subcomponents to be used explicitly  props  Option to declare the data it expects
var childNode = { template: '<div>{{message}}</div>', props:['message'] }
   static state Prop Pass values by adding properties to the placeholders of the child component in the parent component
<div id="example"> <parent></parent> </div> <script> var childNode = {
template:'<div>{{message}}</div>', props:['message'] } var parentNode = {
template: `<div class="parent"> <child message="aaa"></child> <child message="
bbb"></child> </div>`, components: { 'child': childNode } }; // Create root instance new
Vue({ el:'#example', components: { 'parent': parentNode } }) </script>
 

Naming convention

   about props For declared properties , At the parent level HTML In template , Attribute name needs to be written with middle dash
var parentNode = { template: ` <div class="parent"> <child my-message="aaa"></
child> <child my-message="bbb"></child> </div>`, components: { 'child':
childNode } };
   Subclass props When a property is declared , It can be written with small hump or middle dash ; When a child template uses a variable from the parent , It needs to use the corresponding small hump writing method
var childNode = { template: '<div>{{myMessage}}</div>', props:['myMessage'] }
var childNode = { template: '<div>{{myMessage}}</div>', props:['my-message'] }
 

dynamic props

   In template , To dynamically bind the data of the parent component to the data of the child template props, And bind to any normal HTML Similar characteristics , Just use  v-bind
. Whenever the data of the parent component changes , This change is also transmitted to the subcomponents
var childNode = { template: '<div>{{myMessage}}</div>', props:['myMessage'] }
var parentNode = { template: ` <div class="parent"> <child
:my-message="data1"></child> <child :my-message="data2"></child> </div>`,
components: { 'child': childNode }, data(){ return { 'data1':'aaa',
'data2':'bbb' } } };
  

Pass numbers

   A common mistake for beginners is to use literal grammar to transfer values
<!-- Passed a string "1" --> <comp some-prop="1"></comp> <div id="example"> <my-parent
></my-parent> </div> <script> var childNode = { template: '
<div>{{myMessage}} The type of {{type}}</div>', props:['myMessage'], computed:{ type(){
return typeof this.myMessage } } } var parentNode = { template: ` <div class="
parent"> <my-child my-message="1"></my-child> </div>`, components: { 'myChild'
: childNode } };// Create root instance new Vue({ el: '#example', components: { 'MyParent':
parentNode } })</script>
   Because it's a literal prop, Its value is a string "1" instead of number. If you want to pass on an actual number, Need to use v-bind
, So that its value is treated as JS Expression evaluation  
<!-- Pass the actual number --> <comp v-bind:some-prop="1"></comp> var parentNode = {
template: `<div class="parent"> <my-child :my-message="1"></my-child> </div>`,
components: { 'myChild': childNode } };
   Or you can use dynamic props, stay data Set the corresponding number in the 1
var parentNode = { template: ` <div class="parent"> <my-child :my-message
="data"></my-child> </div>`, components: { 'myChild': childNode }, data(){
return { 'data': 1 } } };
 

props verification

   Can be props Specify validation specifications . If the incoming data does not meet the specifications ,Vue Warning will be given . When components are used by others , It works

   To specify validation specifications , Need to be in the form of an object , Instead of string array
Vue.component('example', { props: { // Basic type test (`null` It means anything ) propA:
Number, // Multiple types propB: [String, Number], // Required and string propC: { type: String,
required: true }, // number , There are default values propD: { type: Number, default: 100 }, //
array / The default value of the object should be returned by a factory function propE: { type: Object, default: function () { return {
message: 'hello' } } }, // Custom validation function propF: { validator: function (value) {
return value > 10 } } } })
  type It can be the following native constructor
String Number Boolean Function Object Array Symbol
  type It can also be a custom constructor function , use instanceof testing .

   When prop Validation failed ,Vue Warning will be thrown ( If you are using a development version ).props Verification will be performed before the component instance is created , So in default or
validator In function , such as data,computed or methods Instance properties such as

   Here is a simple example , If the message It's not a number , Then a warning is thrown
<div id="example"> <parent></parent> </div> <script> var childNode = {
template: '<div>{{message}}</div>', props:{ 'message':Number } } var parentNode
= { template: ` <div class="parent"> <child :message="msg"></child> </div>`,
components: { 'child': childNode }, data(){ return{ msg: '123' } } }; // Create root instance
new Vue({ el: '#example', components: { 'parent': parentNode } }) </script>
   Incoming number 123 Time , No warning . Incoming string '123' Time , The results are as follows

   In the above code , The content of the subcomponent is modified as follows , Customizable validation function , When the function returns to false Time , Output warning prompt
var childNode = { template: '<div>{{message}}</div>', props:{ 'message':{
validator: function (value) { return value > 10 } } } }
   Pass in in the parent component msg The value is 1, Due to less than 10, Output warning prompt
var parentNode = { template: ` <div class="parent"> <child
:message="msg"></child> </div>`, components: { 'child': childNode }, data(){
return{ msg:1 } } };
 

One way data flow

  prop It's one-way bound : When the properties of the parent component change , Transmit to sub assembly , But not the other way around . This is to prevent the child component from inadvertently modifying the state of the parent component —— This makes the application's data flow difficult to understand

   in addition , Every time the parent component is updated , All of the subcomponents prop Will be updated to the latest value . This means that changes should not be made within subcomponents prop. If you do ,Vue A warning will be given at the console

   Here is a typical example
<div id="example"> <parent></parent> </div> <script> var childNode = {
template: ` <div class="child"> <div> <span> Subcomponent data </span> <input
v-model="childMsg"> </div> <p>{{childMsg}}</p> </div> `, props:['childMsg'] }
var parentNode = { template: ` <div class="parent"> <div> <span> Parent component data </span>
<input v-model="msg"> </div> <p>{{msg}}</p> <child :child-msg="msg"></child>
</div> `, components: { 'child': childNode }, data(){ return { 'msg':'match' }
} }; // Create root instance new Vue({ el: '#example', components: { 'parent': parentNode } })
</script>
   When the parent component data changes , Sub component data will change accordingly ; When the data of sub components changes , Parent component data unchanged , And display a warning on the console



   When modifying subcomponent data , Open the browser console and a warning will appear as shown in the figure below

 

modify prop data

   modify prop Data in , There are usually two reasons

  1,prop After passed in as initial value , Subcomponent wants to use it as local data

  2,prop Passed in as initial value , Processing from sub components to other data output

  [ be careful ]JS Objects and arrays in are reference types , Point to the same memory space , If prop Is an object or array , Changing it within a child component affects the state of the parent component

   For both cases , The right response is

  1, Define a local variable , Combined use prop Initialize it with the value of
props: ['initialCounter'], data: function () { return { counter:
this.initialCounter } }
   however , Defined local variables counter Can only accept initialCounter Initial value of , When the value to be passed by the parent component changes ,counter Cannot receive latest value
<div id="example"> <parent></parent> </div> <script src="https://unpkg.com/vue"
></script> <script> var childNode = { template: ` <div class="child"> <div> <
span> Subcomponent data </span> <input v-model="temp"> </div> <p>{{temp}}</p> </div> `,
props:['childMsg'], data(){ return{ temp:this.childMsg } }, }; var parentNode =
{ template: `<div class="parent"> <div> <span> Parent component data </span> <input v-model="msg">
</div> <p>{{msg}}</p> <child :child-msg="msg"></child> </div> `, components: {
'child': childNode }, data(){ return { 'msg':'match' } } }; // Create root instance new Vue({
el:'#example', components: { 'parent': parentNode } }) </script>
   In the following example , Except for the initial value , The value of the parent component cannot be updated into the child component



  2, Define a calculation property , handle prop And return
props: ['size'], computed: { normalizedSize: function () { return
this.size.trim().toLowerCase() } }
   however , Because it's a calculated property , Only values can be displayed , Instead of setting the value
<script src="https://unpkg.com/vue"></script> <script> var childNode = {
template: `<div class="child"> <div> <span> Subcomponent data </span> <input v-model="temp"> <
/div> <p>{{temp}}</p> </div> `, props:['childMsg'], computed:{ temp(){ return
this.childMsg } }, }; var parentNode = { template: ` <div class="parent"> <div>
<span> Parent component data </span> <input v-model="msg"> </div> <p>{{msg}}</p> <child :child-msg
="msg"></child> </div> `, components: { 'child': childNode }, data(){ return {
'msg':'match' } } }; // Create root instance new Vue({ el: '#example', components: { 'parent':
parentNode } })</script>
   In the following example , Because subcomponents use calculated properties , therefore , Data of subcomponent cannot be modified manually



  3, The more appropriate solution is , Using variable storage prop Initial value of , And use watch To observe prop Change in value of . When there is a change , Update value of variable
<div id="example"> <parent></parent> </div> <script src="https://unpkg.com/vue"
></script> <script> var childNode = { template: ` <div class="child"> <div> <
span> Subcomponent data </span> <input v-model="temp"> </div> <p>{{temp}}</p> </div> `,
props:['childMsg'], data(){ return{ temp:this.childMsg } }, watch:{ childMsg(){
this.temp = this.childMsg } } }; var parentNode = { template: ` <div class="
parent"> <div> <span> Parent component data </span> <input v-model="msg"> </div> <p>{{msg}}</p> <
child :child-msg="msg"></child> </div> `, components: { 'child': childNode },
data(){return { 'msg':'match' } } }; // Create root instance new Vue({ el: '#example',
components: {'parent': parentNode } }) </script>