四、自定义组件、组件中插槽的用法、父子组件、混入基础

组件就是一个小型的Vue实例,它里面除了不能设置el选项,其他选项它都有。组件的名称不要跟

原生html元素重名,一般用短横线分隔命名,例如 my-component

组件有两种注册方法:1.全局注册 2.局部注册

全局注册: 通过Vue.component() 来创建组件:  定义全局组件,让每一个Vue实例都可以使用

Vue.component('my-component', {
  // ... 选项 ...
})

局部注册 :在vue实例中通过components选项创建组件,只能在当前实例使用

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

props选项用来用于定义组件标签上属性,它的值可以是一个字符串数组,里面定义每一个标

签属性名称,这是简单用法,不能对属性做严格的验证。props的值也可以是一个对象,里面定义

的每个标签属性名称也可以是一个对象,在这个对象里面定义该属性的完整信息。一般来说父组件

给子组件传值通过props,而子组件可以通过触发自定义事件的方式给父组件传值。这样一来就实现

了父子组件的通信。

    // 组件的模板
    template:
        `<div class='box'>
           <h4 class='id'>{{id}}</h4>
           <p class='name'>{{name}}</p>
           <p class='price'>{{price}}</p>
         </div>`,
    //定义组件标签上的属性
    props: {
                id: {
                    // 数据类型
                    type: String,
                    //require:true时,数据不能为空
                    require: true,
                    //默认值
                    default: ''
                },
                name: {
                    type: String,
                    require: true,
                    default: ''
                },
                price: {
                    type: String,
                    require: true,
                    default: ''
                }
            },
   //组件自己的数据
    data() {
        return {
            name: '我是组件'
        }
    },

注意:Vue实例的data选项可以是一个对象,也可以是一个方法,由该方法返回一个对象。

但是在组件中,data选项必须是一个方法,由该方法返回一个对象。因为组件可能会使用很多次,

如果data选项是对象的话,会导致多个组件使用了同一份数据,从而导致混乱。

① 自定义组件

1.counter组件

    <div id="app">
        衣服:{{clothesNum}}<a-counter :count="clothesNum" :min-count='2' :max-count='25' @sync-count='syncClothes'>
        </a-counter>
        鞋子:{{shoesNum}}<a-counter :count="shoesNum" :min-count='4' :max-count='30' @sync-count='syncShoes'></a-counter>
        裤子:{{pantsNum}}<a-counter :count="pantsNum" :min-count='5' :max-count='15' @sync-count='syncPants'></a-counter>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        Vue.component('a-counter', {
            // template选项,用于定义组件的模板
            template:
                `<div class="counter">
                            <button @click='sub' :disabled='myCount==minCount'>-</button>
                            <input type="text" readonly :value='myCount'>
                            <button @click='add' :disabled='myCount==maxCount'>+</button>
                        </div>`,
            props: {
                // 数量
                count: {
                    // 数据类型
                    type: Number,
                    //require:true时,数据不能为空
                    require: true,
                    //默认值
                    default: 5
                },
                minCount: {
                    type: Number,
                    default: 1
                },
                maxCount: {
                    type: Number,
                    default: 20
                }
            },
            // // 定义组件的数据
            data() {
                return {
                    // 从props里面将count的值给myCount复制一份
                    myCount: this.count
                }
            },
            methods: {
                //数量减方法
                add() {
                    this.myCount++
                },
                // 数量加方法
                sub() {
                    this.myCount--
                },
            },
            watch: {
                // 侦听myCount是否发生变化
                myCount() {
                    // 触发自定义事件,注意:事件名称中不能采用大写字母
                    this.$emit("sync-count", this.myCount)
                }
            },
        })
        new Vue({
            el: "#app",
            data() {
                return {
                    // 衣服的数量
                    clothesNum: 3,
                    // 裤子的数量
                    shoesNum: 5,
                    // 鞋子的数量
                    pantsNum: 6
                }
            },
            methods: {
                // 同步衣服的数量
                syncClothes(e) {
                    this.clothesNum = e
                },
                // 同步裤子的数量
                syncShoes(e) {
                    this.shoesNum = e
                },
                // 同步鞋子的数量
                syncPants(e) {
                    this.pantsNum = e
                },
            },
            // 局部注册组件
            components: {
            }
        })
    </script>

2.评分组件

    <div id="app">
        质量:{{quality}}<a-star :score='quality' @syncscore='syncquality'></a-star>
        物流:{{logistics}}<a-star :score='logistics' @syncscore='synclogistics'></a-star>
    </div>
    <div id="app2">
        外观:{{exterior}}<a-star :score='exterior' @syncscore='syncexterior'></a-star>
        客服:{{service}}<a-star :score='service' @syncscore='syncservice'></a-star>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        Vue.component('a-star', {
            template:
                `<div class="star">
                      <i :class="index<myScore?'icon-shixinxingxing':'icon-xingxing' " class="iconfont" 
                      v-for='(item,index) in 10' :key='index' @mouseenter="mouseenter(index)" @mouseleave='mouseleave' @click='click(index)'></i>
                </div>`,
            //定义组件的标签属性
            props: ['score'],
            data() {
                return {
                    // 将组件接收到的值,在组件内部中转一下
                    myScore: this.score,
                    // 备份一下score值
                    backMyScore: this.score
                }
            },
            methods: {
                // 鼠标进入时,调用的方法
                mouseenter(index) {
                    this.myScore = index + 1
                },
                //鼠标离开时,调用的方法
                mouseleave() {
                    this.myScore = this.backMyScore
                },
                //鼠标点击时,调用的方法
                click(index) {
                    this.backMyScore = index + 1
                }
            },
            watch: {
                // 触发自定义事件
                myScore() {
                    this.$emit('syncscore', this.myScore)
                }
            },
        })

        new Vue({
            el: '#app',
            data() {
                return {
                    //质量
                    quality: 2,
                    //物流
                    logistics: 4,
                }
            },
            // 方法
            methods: {
                // 同步质量评分
                syncquality(e) {
                    this.quality = e
                },
                // 同步物流评分
                synclogistics(e) {
                    this.logistics = e
                },
            },
        })
        new Vue({
            el: '#app2',
            data() {
                return {
                    //外观
                    exterior: 5,
                    //客服
                    service: 3,
                }
            },
            methods: {
                // 同步外观评分
                syncexterior(e) {
                    this.exterior = e
                },
                // 同步客服评分
                syncservice(e) {
                    this.service = e
                },
            },
        })
    </script>

②  组件中插槽的用法

slot标签 用于在组件的内部定义插槽,组件标签之间的内容,会在插槽所在位置呈现

    <div id="app">  
        <b-box title="iphone11" class="box">
           <input type="text">
           <button>购买</button>
        </b-box>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        new Vue({
            el: '#app',
            data: {

            },
            // 定义组件
            components: {
                'b-box': {
                    // 组件的模板
                    template:
                        `<div class="box">
                            <div class="title">{{title}}</div>
                            <div class="content">
                                <slot></slot>
                            </div>
                        </div>`,
                    // 定义组件标签上的属性
                    props: ["title", "content"],
                }
            }
        })
    </script>

具名插槽:

在组件内部通过slot标签定义插槽,在通过name属性给插槽定义名字,这样的插槽称之为:具名插

槽。插槽的默认名称是:default。如果有多个插槽,允许其中一个插槽不定义名称

    <div id="app">
        <b-box>
            <!-- template标签指定它里面的内容在哪一个具体的插槽中呈现 -->
            <template v-slot:header>
                <h4>好好学习</h4>
                <h4>天天向上</h4>
            </template>
            <template v-slot:default>
                <h4>你好</h4>
                <h4>世界</h4>
            </template>
            <!-- v-slot:的简写是# -->
            <template #footer>
                <a href="http://www.baidu.com">百度</a>
                <a href="http://www.taobao.com">淘宝</a>
            </template>
        </b-box>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        Vue.component('b-box', {
            template:
                `<div class="box">
                    <h2>我是组件</h2>
                    <div class="header">
                        <slot name="header"></slot>
                    </div>
                    <div class="main">
                        <slot></slot>
                    </div>
                    <div class="footer">
                        <slot name="footer"></slot>    
                    </div>
                </div>`
        })
        new Vue({
            el: '#app',
        })
    </script>

作用域插槽:

可以在slot标签上绑定属性,这样外面在使用该插槽时,就可以获取到上面绑定的数据。这样的插

槽,我们称之为:作用域插槽。

    <div id="app">
        <b-box>
            <!-- 注意:一定要用使用具名插槽的方式去接收作用域插槽传递回来的数据,
            通常我们会定义scope变量,就收传回来的数据。 -->
            <!-- 此props变量是用来接收所有的插槽的属性的对象=>props可以任意取名-->
            <template v-slot:exit="props">
                <button @click="props.item.name='苹果'">修改</button>
                <button @click="props.list.splice(props.index,1)">删除</button>
            </template>
        </b-box>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        Vue.component('b-box', {
            template:
                `<div class="box">
                    <ul>
                        <li v-for="(item,index) in list" :key="index">{{item.id}}--{{item.name}}
                        <slot  name="exit"  v-bind:item="item" v-bind:list="list" v-bind:index="index"></slot>
                        </li>
                    </ul>
                </div>`,
            data() {
                return {
                    list: [
                        {
                            id: 1001,
                            name: '薯片'
                        },
                        {
                            id: 1002,
                            name: '饼干'
                        },
                        {
                            id: 1003,
                            name: '面包'
                        },
                        {
                            id: 1004,
                            name: '糖果'
                        },
                    ]
                }
            },
        })
        new Vue({
            el: '#app',
        })
    </script>

③父子组件

一般是先创建父组件再创建子组件,先渲染子组件再渲染父组件。在父组件创建完成到挂载完成之

间,包含完整的子组件的生命周期。注意:定义插槽后才能嵌套子组件。在子组件中可以通过

$parent获取父组件的实例,$root获取根组件实例,而父组件通过$children获取的是所有子组件的

实例。但是页面的结构出现了调整,这里获取的具体的组件可以就对不上号了,所以给组件标签

添加一个ref属性,然后就可以通过$refs返回拥有ref属性的组件,在$refs对象中,保存着所有添加

了ref属性的组件实例。

    <div id="app">
        <div class="box">
            <h4>app</h4>
            <h4>姓名:{{name}}</h4>
            <h4>年龄:{{age}}</h4>
            <button @click="updateCar">修改b-one的汽车</button>
            <button @click="updatePlane">修改b-two的飞机</button>
        </div>
        <b-one ref="one">
            <b-three></b-three>
        </b-one>
        <b-two ref="two"></b-two>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        // 定义b-one组件
        Vue.component('b-one', {
            template:
                `<div class="one">
                    <h4>b-one--------{{car.name}}-------{{car.price}}</h4>
                    <h4>App----姓名:{{myname}},年龄:{{myage}}</h4>
                    <slot></slot>
                </div> `,
            data() {
                return {
                    car: {
                        name: '奔驰',
                        price: '50W'
                    }
                }
            },
            computed: {
                myname() {
                    return this.$parent.name
                },
                myage() {
                    return this.$parent.age
                }
            },
            created() {
                console.log('b-one----created');
            },
            mounted() {
                console.log('b-one----mounted');
            },
        })
        // 定义b-two组件
        Vue.component('b-two', {
            template:
                `<div class="two">
                    <h4>b-two------{{plane.name}}-------{{plane.price}}</h4>
                </div> `,
            data() {
                return {
                    plane: {
                        name: '波音747',
                        price: '20亿'
                    }
                }
            },
        })
        // 定义b-three组件
        Vue.component('b-three', {
            template:
                ` <div class="three">
                    <h4>b-three---App----姓名:{{name}}----年龄:{{age}}</h4>
                </div>`,
            computed: {
                name() {
                    // 通过$parent还可以继续获取它的父级组件
                    return this.$parent.$parent.name
                },
                age() {
                    // $root获取根组件
                    return this.$root.age
                }
            },
            created() {
                console.log('b-three----created');
            },
            mounted() {
                console.log('b-three----mounted');
            },
        })

        // 创建vue实例
        new Vue({
            el: '#app',
            created() {
                console.log('app----created');
            },
            mounted() {
                console.log('app----mounted');
                // 根据组件在页面中的顺序返回所有的组件
                console.log(this.$children);
                // 返回所有带有ref属性的组件
                console.log(this.$refs);
            },
            data() {
                return {
                    name: '张三',
                    age: 20
                }
            },
            methods: {
                //修改汽车的方法
                updateCar() {
                    /* this.$children[0].car = {
                                name:'宝马',
                                price:'40W'
                            } */
                    this.$refs.one.car = {
                        name: '宝马',
                        price: '40W'
                    }
                },
                // 修改飞机的方法
                updatePlane() {
                    /* this.$children[1].plane = {
                                name:'长征2号',
                                price:'30亿'
                            } */
                    this.$refs.two.plane = {
                        name: '长征2号',
                        price: '30亿'
                    }
                }
            },
        })
    </script>

1.自定义表格组件--仿iview

    <div id="app">
        <b-table :data="data1" :columns="columns1"></b-table>
        <hr>
        <b-table :data="data2" :columns="columns2"></b-table>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        // 定义一个b-table组件
        Vue.component('b-table', {
            template:
                `<table>
                    <tr>
                        <th v-for="(item,index) in columns" :key="index">{{item.title}}</th> 
                    </tr>
                    <tr v-for="(item,index) in data" :key="index">
                        <td v-for="(item2,index2) in columns" :key="index2">{{item[item2.key]}}</td>
                    </tr>
                </table>`,
            // 定义组件标签上的属性,用于接收外部传给组件的数据
            props: ['data', 'columns'],
        })

        // 创建vue实例
        new Vue({
            el: '#app',
            data() {
                return {
                    // 表格的列数据
                    columns1: [
                        {
                            title: '姓名',
                            key: 'name'
                        },
                        {
                            title: '年龄',
                            key: 'age'
                        },
                        {
                            title: '性别',
                            key: 'sex'
                        },
                        {
                            title: '工资',
                            key: 'money'
                        }
                    ],
                    // 表格数据
                    data1: [{
                        name: '张三',
                        age: 20,
                        sex: '男',
                        money: 10000
                    }, {
                        name: '李四',
                        age: 22,
                        sex: '女',
                        money: 20000
                    }, {
                        name: '王五',
                        age: 24,
                        sex: '女',
                        money: 30000
                    }],
                    columns2: [
                        {
                            title: '编号',
                            key: 'id'
                        },
                        {
                            title: '车名',
                            key: 'name'
                        },
                        {
                            title: '产地',
                            key: 'address'
                        },
                    ],
                    // 表格数据2
                    data2: [{
                        id: '1001',
                        name: '奔驰',
                        address: '德国'
                    }, {
                        id: '1002',
                        name: '红旗',
                        address: '中国'
                    }, {
                        id: '1003',
                        name: '捷豹',
                        address: '英国'
                    }]
                }
            },
        })
    </script>

2.自定义表格组件--仿element-ui

    <div id="app">
        <b-table :data="data1">
            <b-table-column prop="name" label="姓名"></b-table-column>
            <b-table-column prop="age" label="年龄"></b-table-column>
            <b-table-column prop="sex" label="性别"></b-table-column>
            <b-table-column prop="money" label="工资"></b-table-column>
        </b-table>
        <hr>
        <b-table :data="data2">
            <b-table-column prop="id" label="编号"></b-table-column>
            <b-table-column prop="name" label="车名"></b-table-column>
            <b-table-column prop="address" label="产地"></b-table-column>
        </b-table>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        // 定义一个b-table-column组件
        Vue.component('b-table-column', {
            template: `<th>{{label}}</th>`,
            props: ['prop', 'label'],
            mounted() {
                // 子组件挂载完成时,将获取到的属性的名称,添加到父组件的columns数组中。
                this.$parent.columns.push(this.prop)
            },
        })
        // 定义一个b-table组件
        Vue.component('b-table', {
            template:
                `<table>
                    <tr>
                       <slot></slot>
                    </tr>
                    <tr v-for="(item,index) in data" :key="index">
                       <td v-for="(item2,index2) in columns" :key="index2">{{item[item2]}}</td>
                    </tr>
                </table>`,
            // 定义组件标签上的属性,用于接收外部传给组件的数据
            props: ['data'],
            data() {
                return {
                    // 该数组存放属性的名称
                    columns: []
                }
            },
        })

        // 创建vue实例
        new Vue({
            el: '#app',
            data() {
                return {
                    // 表格数据
                    data1: [{
                        name: '张三',
                        age: 20,
                        sex: '男',
                        money: 10000
                    }, {
                        name: '李四',
                        age: 22,
                        sex: '女',
                        money: 20000
                    }, {
                        name: '王五',
                        age: 24,
                        sex: '女',
                        money: 30000
                    }],
                    // 表格数据2
                    data2: [{
                        id: '1001',
                        name: '奔驰',
                        address: '德国'
                    }, {
                        id: '1002',
                        name: '红旗',
                        address: '中国'
                    }, {
                        id: '1003',
                        name: '捷豹',
                        address: '英国'
                    }]
                }
            },
        })
    </script>

④ 混入基础

1.混入提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任

意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

        // 1.创建构造器
        var Profile = Vue.extend({
            template: '<p>1.{{name}}---{{age}}---{{sex}}</p>',
            data: function () {
                return {
                    name: '张三',
                    age: 26,
                    sex: '男'
                }
            }
        })
        // 创建 Profile 实例,并挂载到一个元素上。
        new Profile().$mount('#app1')
        //2.定义一个混入对象
        let myMixin = {
            data() {
                return {
                    name: "混入-----mixin"
                }
            },
        }
        //定义一个使用混入对象的组件
        let component = Vue.extend({
            mixins: [myMixin]
        })
        new component().$mount('#app2')

2.当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。比如,数据对象在内

部会进行递归合并,并在发生冲突时以组件数据优先。

        let mixin = {
            data() {
                return {
                    name: '张三',
                    age: 25,
                    sex: '男',
                    address: '上海',
                    phone: 18546797959,
                    email: '1659562326@qq.com'
                }
            },
        }
        new Vue({
            el: '#app3',
            mixins: [mixin],
            data() {
                return {
                    name: '李四',
                    age: 28,
                    sex: '女',
                }
            },
            methods: {
                fun1() {
                    console.log(this.$data);
                    // {name: '李四',age: 28, sex: '女',
                            address: '上海',
                            phone: 18546797959,
                            email: '1659562326@qq.com'}
                }
            },
        })

3.同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之

前调用。

        let mixin2 = {
            data() {
                return {
                    content1: '混入对象的钩子被调用'
                }
            },
            created() {
                console.log(this.content1);
            },
        }

        new Vue({
            el: '#app4',
            mixins: [mixin2],
            data() {
                return {
                    content2: '组件钩子被调用'
                }
            },
            created() {
                console.log(this.content2);
            },
        })
        // => "混入对象的钩子被调用"
        // => "组件钩子被调用" 

4.值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。两个对

象键名冲突时,以组件对象优先。

        let mixin3 = {
            methods: {
                foo: function () {
                    console.log('foo')
                },
                conflicting: function () {
                    console.log('from mixin')
                }
            }
        }

        var vm = new Vue({
            mixins: [mixin3],
            methods: {
                bar: function () {
                    console.log('bar')
                },
                conflicting: function () {
                    console.log('from self')
                }
            }
        })
        vm.foo() // => "foo"
        vm.bar() // => "bar"
        vm.conflicting() // => "from self"

5.全局混入,混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之

后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。

        Vue.mixin({
            created: function () {
                let myOption = this.$data.myOption
                if (myOption) {
                    console.log(myOption)
                }
            }
        })
        new Vue({
            data() {
                return {
                    myOption: 'hello!'
                }
            },
        })
        // => "hello!"

6. 混入ajax

    <div id="app1">
        <template>
            <el-table :data="students" style="width: 100%" height="250">
                <el-table-column fixed prop="StudentNo" label="学号" width="150">
                </el-table-column>
                <el-table-column prop="StudentName" label="姓名" width="120">
                </el-table-column>
                <el-table-column prop="Grade.GradeName" label="年级" width="120">
                </el-table-column>
                <el-table-column prop="Phone" label="电话" width="120">
                </el-table-column>
                <el-table-column prop="Address" label="地址" width="300">
                </el-table-column>
                <el-table-column prop="Email" label="邮箱" width="120">
                </el-table-column>
            </el-table>
        </template>
    </div>
    <hr>
    <div id="app2">
        <template>
            <el-table :data="subjects" style="width: 100%" height="250">
                <el-table-column fixed prop="SubjectName" label="课程名称" width="150">
                </el-table-column>
                <el-table-column prop="Grade.GradeName" label="课程年级" width="120">
                </el-table-column>
                <el-table-column prop="ClassHour" label="课程课时" width="120">
                </el-table-column>
            </el-table>
        </template>
        <hr>
        <p>课程名称:<input type="text" v-model="subject.subjectName"></p>
        <p>课程年级:<select v-model='subject.gradeId'>
                <option value="0">S1</option>
                <option value="1">S2</option>
                <option value="2">Y2</option>
            </select></p>
        <p>课程课时:<input type="text" v-model="subject.classHour"></p>
        <p><button @click="add">添加</button></p>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.24.0/axios.js"></script>
    <script>
        // 将ajax请求相关的操作,通过mixin混入给Vue
        Vue.mixin({
            data() {
                return {
                    // 接口根路径
                    base_url: 'https://bingjs.com:8001'
                }
            },
            methods: {
                // get方法
                async $get(url, params) {
                    let { data } = await axios.get(this.base_url + url, { params })
                    return data
                },
                // post方法
                async $post(url, params) {
                    let { data } = await axios.post(this.base_url + url, params)
                    return data
                }
            },
        })
        new Vue({
            el: '#app1',
            data() {
                return {
                    // 学生信息数组
                    students: []
                }
            },
            created() {
                this.getStudents()
            },
            methods: {
                async getStudents() {
                    let students = await this.$get('/Student/GetAll')
                    this.students = students
                }
            },
        })
        new Vue({
            el: '#app2',
            data() {
                return {
                    // 课程信息
                    subjects: [],
                    subject: {
                        subjectName: '',
                        classHour: '',
                        gradeId: '0'
                    }
                }
            },
            created() {
                this.getSubjects()
            },
            methods: {
                async getSubjects() {
                    let subjects = await this.$get('/Subject/GetAll')
                    this.subjects = subjects
                },
                async add() {
                    let r = await this.$post('/Subject/Add', this.subject)
                    if (r) {
                        alert('添加成功!')
                        this.getSubjects()
                    }
                }
            },
        })
    </script>

⑤ 深入理解v-model和sync修饰符

v-model指令是对v-bind:value 和 v-on:input的简写。将自定义组件,绑定数据的属性改成value,

监听事件的名称改成input,也可以使用v-model简写形式。

    <div id="app">
        <div>
            姓名:{{name}}<input type="text" v-bind:value="name" v-on:input="name=$event.target.value">
        </div>
        <div>
            姓名:{{name}}<input type="text" v-model="name">
        </div>
        <div>
            衣服:{{yfcount}}<b-counter v-bind:value="yfcount" v-on:input="yfcount=$event"></b-counter>
        </div>
        <div>
            衣服:{{yfcount}}<b-counter v-model="yfcount"></b-counter>
        </div>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        // 定义一个数据加减的组件
        Vue.component('b-counter', {
            template:
                ` <div class="counter">
                    <button @click="jian" :disabled="myCount===minCount">-</button>
                    <input type="text" :value="myCount" readonly>
                    <button @click="jia" :disabled="myCount===maxCount">+</button>
                </div>`,
            props: {
                // 数量
                value: {
                    // 类型
                    type: Number,
                    // 非空
                    required: true,
                },
                // 最小值
                minCount: {
                    type: Number,
                    // 默认值
                    default: 1
                },
                // 最大值
                maxCount: {
                    type: Number,
                    default: 10
                }
            },
            // 定义组件的数据
            data() {
                return {
                    // 从props里面将value的值给myCount复制一份
                    myCount: this.value
                }
            },
            // 定义组件的方法
            methods: {
                //数量减方法
                jian() {
                    this.myCount--
                },
                // 数量加方法
                jia() {
                    this.myCount++
                }
            },
            // 侦听器
            watch: {
                // 侦听myCount是否发生变化
                myCount() {
                    // 触发自定义事件,注意:事件名称中不能采用大写字母
                    this.$emit('input', this.myCount)
                }
            }
        })

        new Vue({
            el: "#app",
            data() {
                return {
                    name: '张三',
                    // 衣服的数量
                    yfcount: 2
                }
            },
        })
    </script>

组件只回传一份数据,用v-model,但是如果组件回传多份数据,则用.sync修饰符,

如果触发的事件名称是update:属性名,那么就可以使用.sync修饰符简化调用的过程。

    <div id="app">
        <div>
            <h4>衣服数量:{{yfcount}}</h4>
            <h4>裤子数量:{{kzcount}}</h4>
            <h4>鞋子数量:{{xzcount}}</h4>
            <!-- <b-box :yfcount="yfcount" :kzcount="kzcount" :xzcount="xzcount"
            @update:yfcount="yfcount=$event" @update:kzcount="kzcount=$event" 
            @update:xzcount="xzcount=$event"></b-box> -->
            <b-box :yfcount.sync="yfcount" :kzcount.sync="kzcount" :xzcount.sync="xzcount"></b-box>
        </div>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
    <script>
        // 定义一个数据加减的组件
        Vue.component('b-counter', {
            template:
                `<div class="counter">
                    <button @click="del" :disabled="myCount===minCount">-</button>
                    <input type="text" :value="myCount" readonly>
                    <button @click="add" :disabled="myCount===maxCount">+</button>
                </div>`,
            props: {
                // 数量
                value: {
                    // 类型
                    type: Number,
                    // 非空
                    required: true,
                },
                // 最小值
                minCount: {
                    type: Number,
                    // 默认值
                    default: 1
                },
                // 最大值
                maxCount: {
                    type: Number,
                    default: 10
                }
            },
            // 定义组件的数据
            data() {
                return {
                    // 从props里面将value的值给myCount复制一份
                    myCount: this.value
                }
            },
            // 定义组件的方法
            methods: {
                //数量减方法
                del() {
                    this.myCount--
                },
                // 数量加方法
                add() {
                    this.myCount++
                }
            },
            // 侦听器
            watch: {
                // 侦听myCount是否发生变化
                myCount() {
                    // 触发自定义事件,注意:事件名称中不能采用大写字母
                    this.$emit('input', this.myCount)
                }
            }
        })

        // 定义一个b-box组件
        Vue.component('b-box', {
            // 模板中必须包含一个根标签
            template:
                `<div>
                    <div>衣服:<b-counter v-model="myyfcount"></b-counter></div>
                    <div>裤子:<b-counter v-model="mykzcount"></b-counter></div>
                    <div>鞋子:<b-counter v-model="myxzcount"></b-counter></div>
                </div>`,
            props: ['yfcount', 'kzcount', 'xzcount'],
            data() {
                return {
                    // 将props传进来的数据,在data中备份一下,从此以后操作data中的数据
                    myyfcount: this.yfcount,
                    mykzcount: this.kzcount,
                    myxzcount: this.xzcount
                }
            },
            watch: {
                myyfcount() {
                    // 注意:这里触发事件的名称是:update:属性名;
                    // 如果满足该规则,那么在触发事件时,就可以使用sync修饰符简化调用的过程。
                    this.$emit('update:yfcount', this.myyfcount)
                },
                mykzcount() {
                    this.$emit('update:kzcount', this.mykzcount)
                },
                myxzcount() {
                    this.$emit('update:xzcount', this.myxzcount)
                }
            }
        })

        new Vue({
            el: "#app",
            data() {
                return {
                    // 衣服的数量
                    yfcount: 2,
                    // 裤子的数量
                    kzcount: 3,
                    // 鞋子的数量
                    xzcount: 5
                }
            },
        })
    </script>

⑥ 组件和vue实例生命周期的执行顺序

 在vue的整个生命周期中在渲染前到渲染后会把组建的整个生命周期走完

即vue的beforeCreate()=>created()=>beforeMount()=>组件的beforeCreate()=>created()

=>beforeMount()=>mounted()=>vue的mounted()

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值