Vue.js学习笔记(三)(组件的基本使用)

1.全局组件与局部组件

组件的意义:写一个可以复用的小模块,
举例:写一段可以复用的标签
使用步骤1创建组件对象2注册组件(分为全局和局部)3使用组件
写法代码:

<body>
<div id="app">
    <!--3.使用组件-->
    <cpn></cpn>
</div>

<div id="app2">
    <cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
    // 1.创建组件构造器对象
    const cpn = Vue.extend({
     
     
        template:
            `
            <div>
                <h2>模板</h2>
                <h2>模板</h2>
           </div>
            `
    })
    // 2.全局注册组件
    // Vue.component('cpn', cpn);

    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        }
    })
       //局部组件的使用,此时id=app的div里不可使用此组件
    const aaa2 = new Vue({
     
     
        el: "#app2",
        components :{
     
     
            cpn: cpn
        }
    })
</script>
</body>

效果:
在这里插入图片描述一个浏览器读出来了标签一个没读出来,因为没有注册

2.父组件与子组件

介绍:如果一个组件能调用另一个组件,那么这个组件是父组件,被调用的是子组件
代码:

<body>
<div id="app">
    <cpn1></cpn1>
    <cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
    //创造子组件
    const cpn2 = Vue.extend({
     
     
        template:
            `
            <div>
                <h2>模板2</h2>
           </div>
            `
    })
    //创造父组件
    const cpn1 = Vue.extend({
     
     
        template:
            `
            <div>
                <h2>模板1</h2>
                <cpn2></cpn2>
           </div>
            `,
        components:{
     
     
            cpn2: cpn2
        }

    })

    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            cpn1: cpn1
        }
    })
</script>
</body>

效果:
在这里插入图片描述
不难看出模板2被模板1调用,所以模板2是子组件,模板1是父组件
值得一提的是cpn2(模板2)并不能直接被id=app的div调用,因为他是在模板1生成的时候注册的,模板1生成时可以调用,但是模板1注册的时候(在new vue)不会顺带模板2,除非模板2也在new vue里注册

3.组件构造的语法糖

介绍:此语法糖就是把构造组件放在注册组件里面,请看写法:

<script>
    //1.非语法糖
    const cpn1 = Vue.extend({
    
    
        template:
            `
            <div>
                <h2>模板1</h2>
           </div>
            `,

    })
    Vue.component('cpn', cpn1);//全局注册
    //局部注册
    const aaa = new Vue({
    
    
        el: "#app",
        data: {
    
    
            message: "hello,guys",
        },
        components: {
    
    
            cpn1: cpn1
        }
    })

    //2.语法糖
    //全局注册
    Vue.component('cpn', {
    
    
        template:
            `
            <div>
                <h2>模板1</h2>
           </div>
            `,

    });
    //局部注册
    const aaa = new Vue({
    
    
        el: "#app",
        data: {
    
    
            message: "hello,guys",
        },
        components: {
    
    
            cpn1: {
    
    
                template:
                    `
            <div>
                <h2>模板1</h2>
           </div>
            `,

            }
        }
    })

</script>

4.组件模板的分离写法

介绍:创建组件构造器的时候的template很麻烦,可以用代替
两种方式写模板,用法如下:

<body>
<div id="app">
    <cpn></cpn>
</div>
//method1
<template id="cpn">
    <div>
        <h2>我是模板标题</h2>
        <p>我是模板文字</p>
    </div>
</template>
//method2
<script type="text/x-template" id="cpn">
<div>
    <h2>我是模板标题</h2>
    <p>我是模板文字</p>
</div>
</script>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            cpn: {
     
     
                template: "#cpn",
            }
        }
    })
</script>
</body>

两个的结果都是:在这里插入图片描述

5.组件中数据以及数据格式的思考

介绍:我们new Vue对象的时候会写data,组件也有data,而且是组件自身的
意义:组件可以存放数据,才能体现组件复用的好处,复用时每个组件数据不能粘连
案例写法(用同一个组件写3个计数器,数据相互独立):

<body>
<div id="app">
    <cpn></cpn>
    <cpn></cpn>
    <cpn></cpn>
</div>
<template id="cpn">
    <div>
        <h2>当前计数: {
   
   {counter}}</h2>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            cpn: {
     
     
                template: "#cpn",
                //注意写法
                data(){
     
     
                    return {
     
     
                        counter: 0
                }
                },
                methods: {
     
     
                    increment(){
     
     
                        this.counter++;
                    },
                    decrement(){
     
     
                        this.counter--;
                    }
                }

            }
        }
    })
</script>
</body>

效果:在这里插入图片描述
思考:为什么组件的数据能不互相粘连
举个例子:
思考以下代码会打印什么:

<script>
  function f() {
    
    
    return {
    
    
      name: "aaa",
      age: 18
    }
  }

  let obj1 = f();
  let obj2 = f();
  obj1.name = "kobe"

  console.log(obj1);
  console.log(obj2);
</script>

结果:
在这里插入图片描述
我们可以发现,obj1 和 obj2 创建时调用了两个不同的地址空间,所以obj1和obj2的内容相互独立,数据没有粘,data同理

6.父组件传值给子组件

意义:开发场景中,通常是根组件接受后台传来的数据,然后再传递给下级子组件
小实例展示父组件如果传值子组件:

<body>
<div id="app">
    <cpn :c-message="message" :c-movies="movies"></cpn>
</div>
<template id="cpn">
    <div>
        <ul>
            <li v-for="item in cmovies">{
   
   {item}}</li>
        </ul>
        <h2>{
   
   {cmessage}}</h2>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: '你好啊',
            movies: ['海王', '海贼王', '海尔兄弟']
        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
                // method1 三种方法都可以,已debug
                // props: ['cmessage', 'cmovies'],

                //method2 可以进行类型判断
                // props: {
     
     
                //     cmessage: String,
                //     cmovies: Array
                // },

                //method3 设置默认参数
                props: {
     
     
                    cmessage: {
     
     
                        type: String,
                        default: '默认参数',
                        require: true  //一定要传的参数
                    },
                    cmovies: {
     
     
                        type: Array,
                        default() {
     
     
                            return ['空数组1', '空数组2']
                        },
                        require: false  //不一定要传的参数
                    }
                },
                data() {
     
     
                    return {
     
     }
                }
            }
        }
    })
</script>
</body>

效果:
cpn没有被传参时:在这里插入图片描述
cpn里有参数的时候:在这里插入图片描述
另外,值得注意的是,html标签不能识别驼峰原则的属性,如果js里面属性是驼峰,应该这样:

html:
<cpn :c-message="message" :c-movies="movies"></cpn>
js:
props: {
                    cMessage: {
                        type: String,
                        default: '默认参数',
                        require: true  //一定要传的参数
                    },
                    cMovies: {
                        type: Array,
                        default() {
                            return ['空数组1', '空数组2']
                        },
                        require: false  //不一定要传的参数
                    }
                },

7.子组件传值给父组件

意义:想象一个场景,子组件发生点击事件,页面另一处改变,就需要所点击组件传值给父
案例:点击按钮让父组件收到数据
逻辑流程:模板里v-for产生按钮→点击按钮触发点击事件(点击事件写在子组件里)→点击事件通过$emit函数发送从子组件接受到的数据→html在使用子组件的时候接受子组件传来的数据

<cpn @receive-message="receivedMessage"></cpn>
(至于为什么这样写:和此组件并列的div并不能收到数据,只有这种写法才能正确传递数据,应该是只有
子组件最外层,就是写到html里的那层(在dom层面会被解析)才能获取子组件内部所产生的数据)

→在父组件的方法receivedMessage里就可以展示收到的数据

<body>
<div id="app">
    <cpn @receive-message="receivedMessage"></cpn>
</div>
<template id="cpn">
    <div>
        <button  v-for="item in categories" @click="sendMessage(item)">{
   
   {item.name}}</button>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            'cpn': {
     
     
                template: '#cpn',
                data() {
     
     
                    return {
     
     
                        categories: [
                            {
     
     id: 'aaa', name: '热门推荐'},
                            {
     
     id: 'bbb', name: '手机数码'},
                            {
     
     id: 'ccc', name: '家用家电'},
                            {
     
     id: 'ddd', name: '电脑办公'},
                        ]
                    }
                },
                methods: {
     
     
                    sendMessage(item){
     
     
                        this.$emit('receive-message', item);
                    }
                }

            }
        },
        methods: {
     
     
            receivedMessage(item){
     
     
                console.log("received", item);
            }
        }
    })
</script>
</body>

效果:
在这里插入图片描述

8.父子组件双向通讯

意义:想象一个需求,在一个组件里输入值,让父组件传过来的属性改变,同时父组件里的值也要改变,
思考:这无法直接改属性,假设属性是pnum,直接用this.pnum=xxxx(输入的值),会报错,子组件无法直接更改父组件的属性在这里插入图片描述
,所以需要先把属性用数据data保存起来data(){ return{ dnum: this.pnum } },然后再改变数据里面的值,同时输入时input标签添加输入函数@input=“changeNumAndSend”,修改数据同时传值给父组件,父组件再接受所改变的值,赋值给data里面传给子组件的对象,对象又改变传给子组件的属性prop
代码:

<body>
<div id="app">
    <h2>num:{
   
   {num}}</h2>
    <cpn :pnum="num" @change-num="changeNum"></cpn>
</div>
<template id="cpn">
    <div>
        <h2>prop:{
   
   {pnum}}</h2>
        <h2>data:{
   
   {dnum}}</h2>
        <input type="text"  @input="changeNumAndSend">
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            num: 8888,
        },
        methods: {
     
     
            changeNum(newNum){
     
     
                this.num = newNum;
            }
        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
                props: {
     
     
                    pnum: Number,
                },
                data(){
     
     
                    return{
     
     
                        dnum: this.pnum + 1,
                    }
                },
                methods: {
     
     
                    changeNumAndSend(event){
     
     
                        this.dnum = parseInt(event.target.value);
                        this.$emit('change-num', this.dnum);
                    }
                }
            }
        }
    })
</script>
</body>

效果:
在这里插入图片描述
输入框里输入什么,num(父组件里面),prop(父组件传的属性),data(子组件数据)都随时变,data与输入函数牢牢绑定,第一个改变,num是输入函数顺便传值给父组件所以改变,prop是随着父组件数据改变所以变了

9.父组件访问子组件里的值

介绍:父组件访问子组件里的值有两种方式,第一种是用$children实现,直接在父组件的methods里面用就好了,以数组的形式返回
案例代码:

<body>
<div id="app">
    <button @click="showChildrenData">在父组件里的按钮</button>
    <cpn ></cpn>
    <cpn ></cpn>
</div>
<template id="cpn">
    <div>
        <h2>我是子组件的模板</h2>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        methods: {
     
     
            showChildrenData(){
     
     
                console.log(this.$children[0].cdata);
                this.$children[0].showMessage();


            }

        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
                data() {
     
     
                    return {
     
     
                        cdata: "我是子组件里的一个data"
                    }
                },
                methods: {
     
     
                    showMessage(){
     
     
                        console.log("我是子组件里的一个函数");
                    }
                }
            }
        }
    })
</script>
</body>

结果:
在这里插入图片描述
成功调用子组件的data和methods,但是需要通过数组返回

$refs方法,直接访问绑定的标签,耦合度低:
代码:

<body>
<div id="app">
    <button @click="showChildrenData">在父组件里的按钮</button>
    <cpn ref="aaa"></cpn>
    <cpn ref="bbb"></cpn>
</div>
<template id="cpn">
    <div>
        <h2>我是子组件的模板</h2>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        methods: {
     
     
            showChildrenData(){
     
     
                console.log(this.$refs);
                console.log(this.$refs.aaa.cdata);
                this.$refs.aaa.showMessage();
            }

        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
                data() {
     
     
                    return {
     
     
                        cdata: "我是子组件里的一个data"
                    }
                },
                methods: {
     
     
                    showMessage(){
     
     
                        console.log("我是子组件里的一个函数");
                    }
                }
            }
        }
    })
</script>
</body>

效果:
在这里插入图片描述

10.子组件访问父组件里面的值

介绍:子组件访问里的值用$parent
案例:

<body>
<div id="app">
    <cpn></cpn>
</div>
<template id="cpn">
    <div>
        <h2>我是子组件的模板</h2>
        <button @click="visitParent">我是子组件里面的一个按钮</button>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        methods: {
     
     
        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
                data() {
     
     
                    return{
     
      }
                },
                methods: {
     
     
                    visitParent(){
     
     
                        console.log(this.$parent);
                        console.log(this.$parent.message);

                        console.log(this.$root);
                        console.log(this.$root.message);
                    }
                }
            }
        }
    })
</script>
</body>

结果:
在这里插入图片描述
$root可以访问根组件的值

11.插槽的基本用法

介绍:插槽就是组件中可变的标签,比如一个组件复用两次,两次使用中所用到的标签略有差异,那就把可变的标签用插槽表示
案例:三个组件对比:不使用插槽,插槽放一个标签,插槽放两个标签

<body>
<div id="app">
    <cpn></cpn>
    <cpn><h2>这是slot</h2></cpn>
    <cpn>
        <h2>这是h2-slot</h2>
        <p>这是p-slot</p>
    </cpn>
</div>
<template id="cpn">
    <div>
        <h2>模板标题</h2>
        <p>模板内容</p>
        <slot></slot>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
            }
        }
    })
</script>
</body>

效果:
在这里插入图片描述

12.给插槽命名

意义:假设一个场景,一个组件里有三个插槽,每次使用三个插槽里的内容都不相同,那就要给每个插槽都命名,某个标签代替插槽的时候通过插槽名字找到对应插槽然后再替代
代码:

<body>
<div id="app">
    <cpn></cpn>
    <cpn><span slot="left">标题</span></cpn>
    <cpn><span slot="left">标题</span> <button slot="right">按钮</button></cpn>

</div>
<template id="cpn">
    <div>
        <slot name="left"><span>左边插槽</span></slot>
        <slot name="center"><span>中间插槽</span></slot>
        <slot name="right"><span>右边插槽</span></slot>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
            }
        }
    })
</script>
</body>

效果:
在这里插入图片描述

13.作用域插槽

编译的作用域:
我们在使用组件的时候,<cpn v-show="isShow"></cpn>isShow函数写在Vue里面,
组件里面的函数写在模板里面,写在组件的methods里面

作用域插槽没明白有什么用,等明白了再改
就是读取子组件的值,渲染到父组件,看弹幕说$children要渲染完成才能使用,这个在渲染前就加载好
代码:

<body>
<div id="app">
    <cpn></cpn>
    <cpn>
        <template slot-scope="slot">
            <span v-for="item in slot.data"> {
   
   {item}} </span>
        </template>
    </cpn>

</div>
<template id="cpn">
    <div>
        <slot :data="pLanguages">
            <ul>
                <li v-for="item in pLanguages">{
   
   {item}}</li>
            </ul>
        </slot>
    </div>
</template>
<script src="../js/vue.js"></script>
<script>
    const aaa = new Vue({
     
     
        el: "#app",
        data: {
     
     
            message: "hello,guys",
        },
        components: {
     
     
            'cpn': {
     
     
                template: "#cpn",
                data(){
     
     
                    return{
     
     
                        pLanguages: ["java", "c++", "Python"]
                    }
                }
            }
        }
    })
</script>
</body>

效果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43249043/article/details/106888998