In this series of articles, we will use questions and answers to master vUE development and make progress together. Welcome to discuss in the comments area

1 q

How to achieve bidirectional binding without using v-Model?

Some people say, this kind of small white question, or mean to ask?

You don’t say, WHEN I first learned VUE, but was tortured by these problems, hard to write according to the official website document demo, can use, daily development has also written a lot of bugs related to V-Model, later determined to carefully study, only to find that there are more ways here, and listen to me.

Here’s the solution:

<template> <div class="test-v-model"> <p> <input v-model=" MSG "placeholder="edit me" /> <p>{{MSG}}</p> {{msg1}}</p> </div> </template> <script> export default { name: 'test-v-model', data() { return { msg: '', msg1: '' } }, methods: { handleInput(e) { this.msg1 = e.target.value } } } </script>Copy the code

Instead of using the V-Model, you need to achieve bidirectional binding by changing the binding value of the value attribute and the input event.

In other words, v-Model is just shorthand

In fact, the V-Model is essentially a syntax sugar that listens for user input events to update data and does special processing for extreme scenarios. — Official documents

Internally, the V-Model uses different properties for different input elements and throws different events:

  • Text and Textarea elements are usedvalueThe property andinputEvents;
  • Checkbox and radio usecheckedThe property andchangeEvents;
  • The select field willvalueAs prop and willchangeAs an event.

Extension of knowledge associated with this topic

  • Two-way binding
  • One-way data binding
  • A one-way data flow for interaction between VUE components

Q: What is bidirectional binding?

Two-way binding is when the data changes, the view updates synchronously, and when the view changes, the data updates.

Q: What is one-way data binding?

One-way data binding is when the data changes, the view updates synchronously, when the view changes, the data does not update.

In VUE, the instruction V-model is used to achieve two-way binding and v-bind is used to achieve one-way data binding

Watch the following code and the GIF demo it runs to see the difference.

< template > < div > < / p > < p > the bidirectional binding < input v - model = "MSG" placeholder = "edit me" / > < p > {{MSG}} < / p > < p > a one-way data binding < / p > < input v-bind:value="msg1" placeholder="edit me" /> <p>{{ msg1 }}</p> </div> </template> <script> export default { name: 'test-v-model', data() { return { msg: '', msg1: '' } } } </script>Copy the code

As you can see from the GIF, with v-Model, when the data changes, the view is updated synchronously, and when the view changes, the data is also updated, which is a two-way binding.

With V-bind, when the data changes, the view updates synchronously. When the view changes, the data does not update. This is one-way data binding.

Q: What is vUE one-way data flow?

A child component cannot change a prop property passed to it by its parent; instead, it is recommended that it throw an event telling the parent to change the binding value itself. So the sum up is data goes down, events go up.

The VUE documentation introduced the concept of one-way data flow when introducing Prop. Click here to see what the VUE documentation says about one-way data flow.

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.

Let’s look at this example:

What happens when a child component binds a prop value bidirectionally?

Parent component code:

<template> <child-component :value="fatherValue" /> </template> <script> import ChildComponent from './child.vue' export  default { name: 'father-component', components: { ChildComponent }, data() { return { fatherValue: '' } } } </script>Copy the code

Sub-component code:

<template>
  <div class="child-component">
    <input v-model="value" placeholder="edit me" />
    <p>{{ value }}</p>
  </div>
</template>

<script>
export default {
  name: 'child-component',
  props: {
    value: {
      type: String,
      default: ''
    }
  }
}
</script>
Copy the code

As you can see, the prop value in childComponent is bidirectional, but the data value in FatherComponent is unchanged, and the console throws a warning:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"

Avoid directly changing the prop value, as this value will be overwritten every time the parent component is re-rendered. Instead, use data or computed based on prop values.

Obviously, changing prop values of child components directly is prohibited by VUE.

How do I manipulate prop values passed to child components

But many times we do want to manipulate prop values passed to child components. What about that?

As the warning above says, there are two ways:

  • This prop is used to pass an initial value, define a local data property and use the prop as its initial value
props: {
  initialCounter: {
    type: Number,
    default: 0
  },
},
data() {
  return {
    counter: this.initialCounter
  }
}
Copy the code
  • The prop is passed in as a raw value and needs to be transformed, with the value of the prop used to define a calculated property
props: {
  size: {
    type: String,
    default: ''
  }
},
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}
Copy the code

In this way, no matter how the data is manipulated, it is the child of the operation and does not affect the parent data.

So, we want to implement bidirectional binding with the data passed in by prop:

The parent component code remains unchanged

A child component receives an incoming value with innerValue:

<template>
  <div class="child-component">
    <input v-model="innerValue" placeholder="edit me" />
    <p>{{ innerValue }}</p>
  </div>
</template>

<script>
export default {
  name: 'child-component',
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      innerValue: this.value
    }
  }
}
</script>
Copy the code

⚠ One thing to note here

Objects and arrays are passed in by reference in JavaScript, so for a prop of an array or object type, changing the object or array itself in a child component will affect the state of the parent component.

Again, we change the value passed in to an object:

Parent component code:

<template>
  <child-component :obj="fatherObj" />
</template>

<script>
import ChildComponent from './child.vue'

export default {
  name: 'father-component',
  components: {
    ChildComponent
  },
  data() {
    return {
      fatherObj: {
        name: 'lin'
      }
    }
  }
}
</script>
Copy the code

Sub-component code:

<template>
  <div class="child-component">
    <input v-model="innerObj.name" placeholder="edit me" />
    <p>{{ innerObj.name }}</p>
  </div>
</template>

<script>
export default {
  name: 'child-component',
  props: {
    obj: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      innerObj: this.obj
    }
  }
}
</script>
Copy the code

This. Obj is a reference type assigned to innerObj, so innerObj actually refers to the parent component’s data, and changes to innerObj’s name will still affect the parent component

Therefore, when processing data of this reference type, you need to make a deep copy

import { clone } from 'xe-utils'
export default {
  name: 'child-component',
  props: {
    obj: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      innerObj: clone(this.obj, true)
    }
  }
}
Copy the code

As shown in the figure above, the data between the component and its parent does not interact with each other.

conclusion

So far, I have finally explained the bidirectional binding and one-way data flow clearly. I really did not expect that it took so much space to explain the concept that I always understood when developing. It is not easy, but it is also a kind of exercise for myself.

Q: Are V-models bidirectional binding?

Yeah, but it’s just grammar sugar

Q: Is v-model a one-way data stream?

Yes, data goes down, events go up

There are some other questions.

  • What is the difference between vUE’s bidirectional binding and one-way data flow?
  • Why does vUE’s bidirectional binding and one-way data flow not conflict?

By the end of this article, you should be able to understand these two concepts no matter how you ask them.