“This is the 20th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

What if Vue subcomponents cannot modify Prop directly?

Scene description

The business scenario is abstracted as follows:

Click the button open in the parent component and v-show=true in the child component

Click the button close in the subcomponent, the subcomponent V-show =false

Repetition code

The simple code is as follows:

The parent component

<template>
  <div id="app">
    <button @click="open">open</button>
    <HelloWorld :visible="visible" />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  data() {
    return {
      visible: false
    }
  },
  components: {
    HelloWorld
  },
  methods: {
    open() {
      this.visible = true
    }
  }
}
</script>
Copy the code

Child components

<template>
  <div>
    <div v-show="visible" style="height: 200px; width: 200px; background: pink">
       <button @click="close">close</button>
    </div>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    visible: Boolean,
  },
  methods: {
    close() {
      this.visible = false
    }
  }
};
</script>
Copy the code

Execute the process

  1. Click on theopenButton, triggerclickEvent, callopenMethod, parent componentvisible=true
  2. Child componentspropReceive, subcomponentvisible=true, child component display
  3. Click on thecloseButton, triggerclickEvent, callcloseMethod, subcomponentvisible=false, child components hidden ·

There is a problem

At first glance, it looks fine, but after step 3, the console displays a warning:

And when you click the Open button again, the child component no longer appears

You can see from the console warning message that Vue does not allow direct modification of prop

The official documentation

All prop forms a one-way downlink binding between their parent prop: updates to the parent prop flow down to the child, but not the other way around. This prevents accidental changes in the state of the parent component from the child, which can make the data flow of your application difficult to understand.

Additionally, every time the parent component changes, all prop in the child component will be refreshed to the latest value. This means that you should not change a prop inside a child component. If you do, Vue will issue a warning in the browser console.

Conclusion: One-way data flow is formed between parent and child prop, and the child prop can only be changed by changing the parent component

The solution

Since we can’t do this by directly modifying the child component to Visible, is there a way to change the parent component’s Visible first and then indirectly change the child’s visible via one-way data flow?

I think you’ve got it: $emit

We can use the $emit method to trigger the parent component’s method to change the value of Visible

// Child component close method
close() {
	this.$emit("close")}Copy the code
<! <HelloWorld :visible="visible" @close="visible = false" />Copy the code

further

To reinterpret the above requirement, what we really want is “bidirectional binding” to a prop.

For this requirement of “bidirectional binding” a prop, Vue recommends that we use $emit as such

/ / child component
this.$emit("update:propName", newProp)
Copy the code
<! <Component v-bind:propName="parentData" V-on :update:propName="parentData = $event" />Copy the code

In our example propName and parentData are visible, and newProp is false, so change it like this:

// Child component close method
close() {
	this.$emit("update:visible".false)}Copy the code
<! <HelloWorld :visible="visible" @update:visible=" $event" />Copy the code

Sync modifier

For convenience, Vue provides an abbreviation for the update:propName pattern above: the.sync modifier

// Child component close method
close() {
	this.$emit("update:visible".false)}Copy the code
<! <HelloWorld :visible. Sync ="visible" />Copy the code

supplement

The scenario we describe is similar to a common dialog component. Read the source code for element-UI, which implements the dialog