Home > Article > Web Front-end > Vue2 implements two-way binding of component props
Vue Study Notes-3 Preface
Compared with Vue 1.x, Vue 2.x, in addition to the implementation of Virtual-Dom, the biggest discomfort to users is the removal of props of components Two-way binding function.
In the past, the twoWay and .sync binding modifiers of props were used to implement the two-way binding function of props in Vue1.
Vue2 component props communication method
In Vue2, the data flow of component props has been changed to only one-way flow, that is, it can only be passed from outside the component (calling component party) through the DOM of the component The attribute passes props to the component. The component can only passively receive data passed from outside the component, and within the component, the props data passed from the outer layer cannot be modified.
The official explanation for this modification:
prop is one-way binding: when the properties of the parent component change, they will be transmitted to subcomponent, but not the other way around. This is to prevent child components from inadvertently modifying the parent component's state - which would make the application's data flow difficult to understand.
Although abandoning the two-way binding of props is beneficial and correct for the entire project as a whole, at some point we do need to modify props from within the component
Case
Suppose I want to make an iOS-style switch button. There are only two requirements:
Click the button to switch the on/off state.
You can also modify it externally without clicking the button. Data switching switch status, such as cascade linkage switch.
The code is roughly similar to this:
<div id="app"> <!--开关组件--> <switchbtn :result="result"></switchbtn> <!--外部控制--> <input type="button" value="change" @click="change"> </div>
//开关组件代码 Vue.component("switchbtn",{ template:"<div @click='change'>{{result?'开':'关'}}</div>", props:["result"], methods:{ change(){ this.result=!this.result; } } }); //调用组件 new Vue({ el: "#app", data:{ result:true//开关状态数据 }, methods:{ change(){ this.result=!this.result; } } });
But in vue2.0, the above code will report an error when clicking the switch. :
[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: " result" (found in component)
The value of props cannot be modified within the component, and the modified value will not be synchronized to the outer layer of the component, that is, the calling component does not know the current state inside the component
In Vue2.0, implement the two-way binding method of component attributes
1. Create a copy of the props attribute in the data object within the component
Because the result is not writable, so You need to create a copy of the myResult variable in data, with the initial value being the value of the props attribute result, and call this data object myResult wherever props need to be called in the component.
Vue.component("switchbtn", { template: "<div @click='change'>{{myResult?'开':'关'}}</div>", props: ["result"], data: function () { return { myResult: this.result//data中新增字段 }; }, ...... });
2. Create a watch for the props attribute to synchronize the modification of props outside the component
At this time, the props of the component are modified outside the component (parent component). It will be synchronized to the corresponding props in the component, but it will not be synchronized to the copy you just created in the data object, so you need to create a watch (monitor) for the props attribute result. When the props are modified, it will correspond to the copy in the data. The copy myResult also needs to synchronize data.
Vue.component("switchbtn", { template: "<div @click='change'>{{myResult?'开':'关'}}</div>", props: ["result"], data: function () { return { myResult: this.result }; }, watch: { result(val) { this.myResult = val;//新增result的watch,监听变更并同步到myResult上 } }, ......
3. Create a watch for the copy of props and notify it outside the component.
At this time, the copy of props myResult is modified in the component. The status of the props inside the component is not known outside the component, so you need to create another props copy myResult, which is a watch corresponding to the data attribute.
Send notifications within the component to the outer layer (parent component) to notify the property changes within the component, and then the outer layer (parent component) changes its data itself
Finally all the code:
<div id="app"> <switchbtn :result="result" @on-result-change="onResultChange"></switchbtn> <input type="button" value="change" @click="change"> </div>
Vue.component("switchbtn", { template: "<div @click='change'>{{myResult?'开':'关'}}</div>", props: ["result"], data: function () { return { myResult: this.result//①创建props属性result的副本--myResult }; }, watch: { result(val) { this.myResult = val;//②监听外部对props属性result的变更,并同步到组件内的data属性myResult中 }, myResult(val){ //xxcanghai 小小沧海 博客园 this.$emit("on-result-change",val);//③组件内对myResult变更后向外部发送事件通知 } }, methods: { change() { this.myResult = !this.myResult; } } }); new Vue({ el: "#app", data: { result: true }, methods: { change() { this.result = !this.result; }, onResultChange(val){ this.result=val;//④外层调用组件方注册变更方法,将组件内的数据变更,同步到组件外的数据状态中 } } });
So far, the two-way binding of data within the component and data outside the component has been achieved, and the synchronization of data inside and outside the component has been achieved. In the end, it boils down to one sentence: if the component changes internally, it tells the outside world, and the outside world decides whether to change it.
#4. What kind of props are suitable for two-way binding?
The first thing to declare is that two-way binding props are definitely not conducive to data state management between components, especially in complex businesses, so use two-way binding as little as possible, too much For complex data processing, it is recommended to use Vuex (http://vuex.vuejs.org/zh-cn/intro.html)
However, in our daily use, there is indeed a need for two-way binding of props. Personally It is considered that two-way binding props should only be used when the following conditions are met.
Props need to be modified inside the component.
Components need to be dynamically controlled by the outside at runtime, rather than simply initialized.
The outside of the component needs to read the status within the component for processing
Those that meet the above conditions are, for example, the switch component in this example, which requires external control of the switch status; another example is Tab The activeIndex attribute of the multi-tab component needs to be able to externally control which page the tab is currently open, etc.
Automated props two-way binding processing
Vue's mixin component-propsync
通过上例也可以看出在Vue2.0中实现props的双向绑定很麻烦,如果有两个props需要做双向绑定上面的代码就要实现两遍,代码极其冗余。
所以我写了一个mixin来自动化处理props的双向绑定的需求——propsync。
主要功能
实现了在组件内自动创建所有prop对应的data属性,方便组件内修改prop使用。解决了vue2.0中不允许组件内直接修改prop的设计。
实现了组件外修改组件prop,组件内自动同步修改到data属性。
实现了组件内修改了data属性(由prop创建的),自动向组件外发出事件通知有内部prop修改。由组件外决定是否要将修改同步到组件外
propsync的使用方法
编写组件
对于编写组件时,如果需要props双向绑定,则直接引入mixin,并在配置中声明mixin即可: mixins: [propsync]
此mixin会根据prop的名称生成对应的data属性名,默认为在prop属性名前面增加"p_",即若prop中有字段名为active,则自动生成名为p_active的data字段(props到data的名称变更方法可自行修改,详见propsync源码开头配置)
propsync默认会将所有props创建双向绑定,可通过propsync:false来声明此props不需要创建双向绑定。
例:
import propsync from './mixins/propsync';//引入mixin文件 export default { name: "tab", mixins: [propsync],//声明使用propsync的mixin props: { active: { type: [String, Number],//会被propsync自动实现双向绑定,在data中创建p_active变量 }, width: { type: [Number, String], propsync:false//不会被propsync实现双向绑定 } }, methods: { setActive(page, index, e) { this.p_active = index;//可以直接使用this.p_active } } }
调用组件
引入propsync后,会在内部双向绑定的data变更后触发一个onPropsChange事件。遂在调用组件处,增加一个事件监听 onPropsChange(可修改),当组件内修改了props时propsync会触发此事件,返回参与依次为:修改prop名称,修改后值,修改前值。可以由当前组件调用方(父组件)来决定是否要将组件内的变更同步到调用方
例:
<tab :active="active" @onPropsChange="change"></tab> ......略 { data:{ active:0 }, methods:{ change:function(propName,newVal,oldVal){ this[propName]=newVal; console.log("组件tab的" +propName+ "属性变更为" +newVal); } } }
更多Vue2 implements two-way binding of component props相关文章请关注PHP中文网!