Vue.js 章7:组件、使用props传递数据、父子组件通信

这一章感觉要学好久了...

<body>
    <!-- 组件:Vue.js最核心、设计最精彩的功能,也是最难掌握的(555) -->
    <!-- 组件:提高重用性,让代码能够复用 -->
    <div id="app1">
        <my-component-01></my-component-01>
        <my-component></my-component>
    </div>

    <!-- 在某些情况下,组件的模板会受到html的限制,如table标签内规定只允许是tr、td、th这些表格元素,
    所以直接在table标签内使用组件是无效的,这种情况下可以使用特殊的is属性来挂载组件 -->
    <div id="app2">
        <table>
            <tbody is = 'my-component-table'>

            </tbody>
        </table>
    </div>

    <div id="app3">
        <my-component-02></my-component-02>
    </div>

    <div id="app4">
        <my-component-03></my-component-03>
        <my-component-03></my-component-03>
        <my-component-03></my-component-03>
        <my-component-03></my-component-03>
        <my-component-03></my-component-03>
    </div>
</body>
<script>
    // 组件需要注册之后才能使用,有全局注册(任何Vue实例都可以使用)以及局部注册两种方式
    // 全局注册,注册的组件需要在初始化根实例之前注册了组件;
    // 局部注册,通过使用组件实例选项注册,可以使组件仅在另一个组件或者实例的作用域中可用:
    Vue.component('my-component-01',{
        template:'<div>这是第一个组件的内容</div>'
        //组件内容
    });
    Vue.component("my-component-table",{
        template:'<div>这是塞进表格组件里的内容</div>'//渲染时会将tbody替换
    });
    var childCompo = {
        template:'<div>这是第一个局部注册组件的内容</div>'
    };
    var app1 = new Vue({
        el:"#app1",
        components:{
            'my-component':childCompo
        }
    });

    var app2 = new Vue({
        el:"#app2"
    });//注意了,使用组件时要确保已经将元素挂载到Vue实例

    //组件还可以像vue实例那样使用其他选项,比如data、computed、methods等,但在使用data时有区别:
    //data必须是函数,将数据return出去
    Vue.component('my-component-02',{
        template:'<div>{{ message }}</div>',
        data() {
            return {
                message:'组件内容,这里的data是函数'
            }
        },
    });

    var app3 = new Vue({
       el:"#app3" 
    });


    //js的对象是引用关系(值传递和引用传递的相关知识),如果return的对象引用了外部的一个对象,那么这个对象就是共享的
    //任何一方修改都会同步
    var dataObj = {
        counter:0
    };
    Vue.component('my-component-03',{
        template:'<button v-on:click="counter++">记录点击次数:{{ counter }}</button>',
        // data:function(){
        //     return {
        //         dataObj//上面那个对象
        //     }
        // }会报错的写法
        data:function(){
            return dataObj
        },
        // data:function(){
        //     return {
        //         counter:0
        //     }
        // }
    })
    var app4 = new Vue({
       el:"#app4", 
    });

    //如果这样,每点击一个按钮,所有按钮的计数次数都会+1,改写为96~100行的形式
    //原理:给每个组件返回一个新的data对象
</script>

重难点是后面两个部分,使用props传递数据、父子组件通信

<body>
    <div id="app1">
        <my-component-01 message-from="来自父组件的数据"></my-component-01>
    </div>

    <div id="app2">
        <input type="text" v-model="parentMessage">
        <my-component-02 :message = "parentMessage"></my-component-02>
    </div>

    <div id="app3">
        <my-component-03 message="[1,2,3]"></my-component-03>
        <my-component-03 :message="[1,2,3]"></my-component-03>
        <!-- 直接传递数字、布尔值、数组、对象而不使用v-bind那么传递过去的只会是字符串!例如上面两个组件中的message.length,一个是7,一个是3 -->
    </div>

    <div id="app4">
        <my-component-04 :init-count="1"></my-component-04>
        <my-component-04 :width="200"></my-component-04>
    </div>

    <div id="app5">
        <input type="text" v-model="parentMessage">
        <my-component-05 :prop_a = "parentMessage"></my-component-05>
    </div>

    <div id="app6">
        <p>总计:{{ total }}</p>
        <my-component-06
            @increase = "handleGetTotal"
            @reduce = "handleGetTotal"></my-component-06>
        </my-component-06>
    </div>

    <div id="app7">
        <p>app7:{{ total }}</p>
        <my-component-07 v-model = "total"></my-component-07>
    </div>

    <div id="app8">
        <p>app8:{{ total }}</p>
        <my-component-08 v-model="total"></my-component-08>
        <button @click = "handleReduce">-1</button>
    </div>
</body>
<script>
    Vue.component('my-component-01',{
        //使用props声明需要从父级接收的数据
        props:['messageFrom'],//如果要传递多个数据,直接在props数组中添加项即可
        //注意短横杠分隔命名与驼峰式命名
        template:'<div>{{ messageFrom }}</div>',
    });
    var app1 = new Vue({
        el:"#app1"
    });

    //上面是写死的数据,但大多数情况传递的数据是来自父级的动态数据,此时应使用v-bind动态绑定
    Vue.component('my-component-02',{
        props:['message'],
        template:'<div>子组件:{{ message }}</div>'
    });
    var app2 = new Vue({
       el:"#app2",
       data:{
           parentMessage:''
           //数据传递:pM更改-->v-model更改-->message值更改(:message = "parentMessage")-->props中的message-->子组件中的message
       } 
    });

    Vue.component('my-component-03',{
        props:['message'],
        template:'<div>{{ message.length }}</div>',
        data:function(){
            console.log(this,this.message);
            return {messages:this.message};
        }
    });
    var app3 = new Vue({
       el:"#app3",
        
    });

    //业务中经常会遇到的两种改变prop的情况:
    //1.父组件传递初始值,子组件将其保存起来,在自己的作用域下任意传值与修改,
    //2.另一种情况:prop作为需要进行转变的初始值传入
    Vue.component('my-component-04',{
        props:['initCount','width'],
        template:'<div>
        <p>{{ count }}</p>
        <div :style="style"></div>
        </div>',
        data:function(){
            return {
                count:this.initCount,//在组件初始化时会获取来自父组件的initCount,之后就与之无关了
            }//返回的必须是一个数据对象哈小老弟
        },
        computed: {
            style:function(){
                return {
                    this.width + 'px'
                    //这里会得到:style="200px"
                }
            }
        },
    });
    var app4 = new Vue({
       el:"#app4", 
    });

    //数据验证,当某个数据不符合输入类型时会在控制台弹出警告
    Vue.component('my-component-05',{
        props:{
            //数字类型限定
            prop_a:Number,
            // propA:[Number,String],
            //布尔值限定,默认为true,必须传入
            propB:{
                type:Boolean,
                default:true,
                // required:true
            },
            //默认值必须是一个函数来返回
            propC:{
                default:function(){
                    return [];
                }
            },
            //自定义验证函数
            propD:{
                validator:function(value){
                    return value > 500;
                }
            }
        },
        template:'<div>子组件只能是数字:{{ prop_a }}</div>',
    })
    var app5 = new Vue({
       el:"#app5",
        data:{
            parentMessage:0
        }
    });


    //当子组件需要向父组件传递数据时需要用到自定义事件以及$emit()方法
    Vue.component('my-component-06',{
        template:'
        <div>
            <button @click="handleIncrease()">+1</button>
            <button @click="handleReduce">-1</button>
        </div>',
        data:function(){
            return {
                counter:0
            }
        },
        methods: {
            handleIncrease:function(){
                this.counter++;
                this.$emit('increase',this.counter);
            },
            handleReduce:function(){
                this.counter--;
                this.$emit('reduce',this.counter);
            }
        },
    });
    //分析一下数据的传递:
    //1,total最初为0
    //点击+1键:触发组件06methods中的handleIncrease方法,counter++,通过$emit()方法(第一个参数是自定义,第二个是可不填或多填的参数)
    //父组件上的v-on监听到increase事件,触发handleGetTotal()方法,同时$emit()方法的第二个参数被传入handleGetTotal()
    //this.total=totalNum,即新的counter值
    //v-on还可以用于监听原生的dom事件,但需要加上.native修饰符
    //eg:<m-c @click.native="handleClick"></m-c>
    var app6 = new Vue({
       el:"#app6",
       data:{
           total:0
       },
       methods:{
           handleGetTotal:function(totalNum){
                this.total = totalNum;
           }
       }
    });

    Vue.component('my-component-07',{
        template:'
        <button @click = "handleClick">+1</button>',
        data:
            function(){
                return {
                    counter:0
                }
            },
        methods: {
            handleClick:function(){
                this.counter++;
                this.$emit('input',this.counter);
            }
        },
    });
    // 这一块的数据流动有点奇怪,我尽力分析一下:
    // 点击按钮  触发handleClick事件---counter++,并将事件名称input与当前counter值传递给父组件,

    // 注意,在前面几章有一个知识点,v-model实际上是一个语法糖,从前面复制过来并加上些详细点的解释:
    // v-model你可以理解成是value的更高级,:value(v-bind)属于数据单向绑定(从script到html的单向),v-model属于双向绑定
    // v-model官方给出的说法是:这其实是一个简写的形式,v-model实际执行的是下面的绑定:
    // <input type="text" v-bind:value="dataA" v-on:input="dataA = $event.target.value" />
    // (会根据在不同的表单控件上执行不同的作用,如option与button)
    // 在本例(app7)中则是:<input type="text" v-bind:value="total" v-on:input ="total = $event.target.value" />
    //然后更新total值后就会影响视图如{{ total }}辣~
    // 接前面对于数据流动的分析:子组件将input事件传递给父组件后,相当于组件07上触发了
    //input事件,使得v-model="total"获得更新,继而更新{{ total }}

    //前面碰到的例子都是这样写的:<input v-model="a">
    //                          <p>{{ a }}</p>
    var app7 = new Vue({
       el:"#app7",
       data:{
           total:0
       } 
    });

    Vue.component('my-component-08',{
        props:['value'],
        template:'<input :value = "value" @input = "updateValue">',
        methods:{
            updateValue:function(event){
                this.$emit('input',event.target.value);
            }
        }        
    });
    var app8 = new Vue({
       el:"#app8",
       data:{
           total:0
       },
       methods:{
           handleReduce:function(){
               this.total--;
           }
       }
    });
    //这一部分的数据变化方向:
    //1:点击-1按钮,执行handleReduce方法,total--,直接反馈到视图中的{{ total }}与v-model="total"(双向绑定)
    //2.输入值,执行updateValue方法,同时更新value(记得前面说的v-model是个语法糖以及实际展现)
    //value被更新,子组件获取的是实时的value,也会获得更新
</script>

一个需要注意点的地方:

app5、组件05部分:

如果规定了类型,那么一开始传入的值就必须是符合类型要求的,否则会报错

原文地址:https://www.cnblogs.com/linbudu/p/10857963.html