Vue数据单向流动

介绍

  官方说法叫单向数据流,意思是父级prop的更新会向下流动到子组件中,但是反过来是不行的。为的是防止子组件修改父组件的状态。但是实际开发中我们可能会遇到以下两种情况:

  1. 需要使用并修改父组件传递的数据,但不需要回传给父组件

  2. 需要使用并修改父组件传递的数据并回传给父组件

问题

  如果我们将父组件prop的数据直接在input中进行了修改,vue在控制台中给出警告,栗子:

// html
<div id="app">
    <p>{{ title }}</p>
    <conponent-son :title="title"></conponent-son>
</div>
<script>
        Vue.component('conponent-son', {
            props: ['title'],
            template: `<div>
                <input type="text" v-model="title">
            </div>`
        });

        const app = new Vue({
            el: '#app',
            data() {
                return {
                    title: '文章',
                };
            },
            mounted() {
                // 模拟异步加载数据
                window.setTimeout(()=>{
                    this.title = '文章';
                },100);
            }
        });
</script>

错误

解决方案

1.使用父组件数据,修改不回传

  这种情况通常是需要修改已经存在的文章或者其他的个人信息之类的表单数据,通过父组件获取数据,传给子组件,子组件需要进行修改,然后直接请求接口修改信息,不需要回传给父组件再请求接口修改。这种方式的解决方法再vue给出的警告已经说的很清楚了,使用data或者计算方法去代替这种操作。先看第一种data方法:

Vue.component('conponent-son', {
        props: ['title'],
        data(){
            return {
                currentTitle: this.title
            };
        },
        template: `<div>
            <input type="text" v-model="currentTitle">
        </div>`
    });

  这种方法主要是通过组件的一个局部变量,将props的数据赋值给局部变量currentTitle,这样就不会存在数据回流的情况了,因为我们是修改的并不是原数据。另外也可以使用计算属性getter&setter,不推荐,毕竟有简单的方法实现了,何必钻牛角尖呢。

2.使用父组件数据需要回传

  这种情况呢就跟上面的有一定区别,在于通过父组件获取数据后,传给子组件修改,需要通过父组件请求接口完成数据的更改。所以需要回传,但是这种操作在vue看来是不安全的,因为子组件去修改了父组件的数据状态,有时候我们不得不这样做比如所说的这种情况。栗子:

<div id="app">
    <p>{{ title }}</p>
    <conponent-son :title="title" @input-done="receiveInput"></conponent-son>
</div>
Vue.component('conponent-son', {
        props: ['title'],
        data() {
            return {
                currentTitle: this.title
            };
        },
        template: `<div>
            <input type="text" v-model="currentTitle" @blur="commitInput">
        </div>`,
        methods: {
            commitInput() {
                this.$emit('input-done', this.currentTitle);
            }
        }
    });

    const app = new Vue({
        el: '#app',
        data() {
            return {
                title: '文章',
            };
        },
        mounted() {
            // 模拟异步加载数据
            window.setTimeout(() => {
                this.title = '文章';
            },100);
        },
        methods: {
            receiveInput(newTitle) {
                this.title = newTitle;
            }
        }
    });

  验证了之后发现直接修改原数据还是会给warning,所以还是需要用到第一种解决方案来处理一下数据,折中,毕竟对于强迫症来说一直给wraning也挺糟的,虽然用户感觉不到,但我们还是得处理一下。

  主要就是添加自定义事件input-done,然后在父组件中监听该事件,输入完成,焦点离开后,就触发该提交事件,父组件监听到该事件就会将数据修改为newTitle,这样就完成了提交。

总结

  数据单向流动就是确保数据的安全性,可以向下但不能(是不建议)向上,因为向上可能会涉及一些问题,所以有时候有需求的时候,麻烦还是得麻烦一下。因为这样做虽然麻烦了,但是避免了修改原数据,保证了数据的安全、可靠,值得推荐使用(我当然不是觉得一堆warning看着难受:)。