zoukankan      html  css  js  c++  java
  • [前端] VUE基础 (4) (组件基础、局部组件、全局组件、父子传值、平行传值)

    一、组件

    1.组件概念

    我们将一个页面看成一个最大的组件(app),这个组件里面又由多个子组件构成。这样形成一颗组件树。如上图所示;

    例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。

    为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。

    这里有两种组件的注册类型:全局注册和局部注册

    2.template的优先级

    在前面章节的vue使用中,我们的vue实例与一个<div id="app">标签绑定,使用的是"el"属性来绑定。

    vue实例绑定模板,还有另外一种方式:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Component</title>
    </head>
    <body>
        <div id="app">
            el:<h2>{{ msg }}</h2>
        </div>
        <script src="./static/vue.js"></script>
        <script>
            var vm = new Vue({
                el:"#app",
                data(){
                    return {
                        msg:"hello"
                    }
                },
                template:`
                    <div class="app">
                        <h2>temp:{{ msg }}</h2>
                    </div>
                `
            })
        </script>
    </body>
    </html>

    执行结果:

    可以看到,实际绑定的模板是template属性定义的模板。

    所以,template属性定义的模板的绑定优先级高于el属性指定的模板。

    注意:虽然template可以定义模板,但是el所指定的模板也不可少,相当于template模板替换了el指定的模板(el可以说是指定了替换的位置),其实实际原因和声明周期有关系。

    二、局部组件的定义、挂载和使用

    局部组件的主要流程就是三步:

    • 定义组件
    • 挂载组件
    • 使用组件

    1.组件的定义

    // 1.定义组件
    let App = {
        data(){
            return {
                msg:'我是组件'
            }
        },
        template:`
            <div class="App_component">
                <h2>{{ msg }}</h2>
            </div>
        `
    }

    注意,组件名要么使用首字母大写(驼峰命名),例如AppComponent,要么使用"-"命名形式,例如:app-component,必须与html的普通标签区分开。组件是一个对象,和vue实例的参数对象差不多,但是没有"el"。

    注意:组件的template必须要用一个闭合标签包起来,例如用一个<div>包起来,例如:

    template:`
        <h2>{{ msg }}</h2>
        <h2>{{ msg }}</h2>
    `

    则会报错。

    2.挂载组件

    挂载组件,就是将定义好的组件挂载到vue实例中。

    var vm = new Vue({
        el:"#app",
        data(){
            return {
                msg:"hello"
            }
        },
        // 2.挂载组件
        components:{
            App  // 这里相当于App:App,key个value一致的话,在ES6中可以只写一个
        }
    })

    3.使用组件

    var vm = new Vue({
        el:"#app",
        data(){
            return {
                msg:"hello"
            }
        },
        // 2.挂载组件
        components:{
            App  // 这里相当于App:App,key个value一致的话,在ES6中可以只写一个
        },
        // 3.使用组件
        template:`
            <div class="app">
                <App></App>
            </div>
        `
    })

    注意,使用组件的时候,标签名就是组件的名称(这就是为什么组件名首字母要用大写,为了区分普通标签)。

    当我们使用<App>的时候,就相当于把组件里的template替换到这个位置。然后又被替换到了el:"#app"对应的页面位置。如下图所示:

    可以对比一下HTML代码中,id="app"的div不存在,因为已经被class="app"的div替换了(也就是vue实例的template),然后该div中的<App>也被class="App_component"的div所替代(也就是组件的template)。

    注意,使用<App>的时候,可以在vue实例的template中使用,也可以在el对应的标签中去使用(此时就不能有template属性了,因为他的优先级高于el):

    <body>
    <div id="app">
        <App></App>
    </div>
    <script src="./static/vue.js"></script>
    ...
    ...

    4.组件定义、挂载、使用完整代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Component</title>
    </head>
    <body>
        <div id="app">
            el:<h2>{{ msg }}</h2>
        </div>
        <script src="./static/vue.js"></script>
        <script>
            // 定义组件
            let App = {
                data(){
                    return {
                        msg:'我是组件'
                    }
                },
                template:`
                    <div class="App_component">
                        <h2>{{ msg }}</h2>
                    </div>
                `
            }
            var vm = new Vue({
                el:"#app",
                data(){
                    return {
                        msg:"hello"
                    }
                },
                // 2.挂载组件
                components:{
                    App  // 这里相当于App:App,key个value一致的话,在ES6中可以只写一个
                },
                // 3.使用组件
                template:`
                    <div class="app">
                        <App></App>
                    </div>
                `
            })
        </script>
    </body>
    </html>

    5.嵌套组件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Component</title>
        <style>
            .App_component{
                background-color: darkseagreen;
                height: 400px;
                width: 500px;
            }
            .Vheader{
                background-color: cornflowerblue;
                height: 100px;
                width: 500px;
            }
        </style>
    </head>
    <body>
    <div id="app">
    
    </div>
    <script src="./static/vue.js"></script>
    <script>
        // 定义VHeader
        let Vheader ={
            data() {
                return {
                    msg: '我是App中的Vheader'
                }
            },
            template: `
                <div class="Vheader">
                    <h3>{{ msg }}</h3>
                </div>
            `
        }
        // 定义App组件
        let App ={
            data() {
                return {
                    msg: '我是App组件'
                }
            },
            template: `
                <div class="App_component">
                    <Vheader></Vheader>
                </div>
            `,
            components:{
                Vheader
            }
        }
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: "hello"
                }
            },
            // 2.挂载组件
            components: {
                App // 这里相当于App:App,key个value一致的话,在ES6中可以只写一个
            },
            //3.使用组件
            template: `
                <div class="app">
                    <App></App>
                </div>
            `
        })
    </script>
    </body>
    </html>

    上述代码中,App组件挂载在vue实例中,在vue实例的template中使用。而Vheader组件挂载在App组件中,在App组件的template中使用。

    三、全局组件

    1.全局组件的定义和使用

    无需挂载,vue实例或者局部组件都可以直接使用的组件。

    <body>
    <div id="app">
    </div>
    <script src="./static/vue.js"></script>
    <script>
        // 定义一个全局组件,一个按钮
        Vue.component('VBtn', {
            data() {
                return {
                    name: '全局组件中的按钮'
                }
            },
            template: `
                    <button>{{ name }}</button>
                `
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: 'hello world'
                }
            },
            // VBtn为全局组件,可以在这里直接使用,而无需挂载
            template: `
                <div>
                    <VBtn></VBtn>
                </div>
            `
        })
    </script>
    </body>

    使用Vue.component()来定义,定义出的组件可以不用挂载直接使用。

    实现效果:

    2.slot分发数据

    在1.的全局组件VBtn中,按钮的名称是根据自己data中的name属性来指定的,如果想在被使用的地方来指定,则需要使用slot分发。

    <body>
    <div id="app">
    </div>
    <script src="./static/vue.js"></script>
    <script>
        // 定义一个全局组件,一个按钮
        Vue.component('VBtn', {
            data() {
                return {
                    name: '全局组件中的按钮'
                }
            },
            template: `
                    <button><slot></slot></button>
                `
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: 'hello world',
                    btn_name:'vue实例中的VBtn按钮'
                }
            },
            // VBtn为全局组件,可以在这里直接使用,而无需挂载
            template: `
                <div>
                    <VBtn>{{btn_name}}</VBtn>
                </div>
            `
        })
    </script>
    </body>

    使用<slot>标签,就可以将使用时指定的数据分发到组件中。这里相当于将vue实例中的btn_name属性的数据分发到了VBtn组件中,并放在<slot>标签的位置。

    实现效果:

    四、父组件传值给子组件

    1.父组件传递值出去

    var vm = new Vue({
        el: "#app",
        data() {
            return {
                msg: 'hello world',
                btn_name:'vue实例中的VBtn按钮'
            }
        },
        // 在VBtn中我们绑定一个属性trans_data,通过这个属性将msg的值传递给子组件VBtn
        template: `
            <div>
                <VBtn :trans_data="msg">{{btn_name}}</VBtn>  
            </div>
        `
    })

    在父组件中,绑定一个自定义的属性,将msg属性的值传递出去。

    如果只是传递静态值,则直接使用自定义属性就可以传递了:

    <VBtn trans_data="hello world">{{btn_name}}</VBtn>  

    2.子组件接收父组件传递的值

    // 定义一个全局组件,一个按钮
    Vue.component('VBtn', {
        data() {
            return {
                name: '全局组件中的按钮'
            }
        },
        // 子组件中接收trans_data中的值(就是父组件中msg的值)
        props:['trans_data'],
        template: `
                <div>
                    <button><slot></slot></button>
                    <button>{{trans_data}}</button>
                </div>
            `
    })

    使用props列表来接收父组件传递的值,注意,props列表中的字符串代表着接收属性的名称,必须与父组件中绑定属性的名称一致。

    3.父组件传值给子组件完整代码

    <body>
    <div id="app">
    </div>
    <script src="./static/vue.js"></script>
    <script>
        // 定义一个全局组件,一个按钮
        Vue.component('VBtn', {
            data() {
                return {
                    name: '全局组件中的按钮'
                }
            },
            // 子组件中接收trans_data中的值(就是父组件中msg的值)
            props:['trans_data'],
            template: `
                    <div>
                        <button><slot></slot></button>
                        <button>{{trans_data}}</button>
                    </div>
                `
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: 'hello world',
                    btn_name:'vue实例中的VBtn按钮'
                }
            },
            // 在VBtn中我们绑定一个属性trans_data,通过这个属性将msg的值传递给子组件VBtn
            template: `
                <div>
                    <VBtn :trans_data="msg">{{btn_name}}</VBtn>
                </div>
            `
        })
    </script>
    </body>

    值传递流程:父组件data中的msg--->父组件template中VBtn中的trans_data属性--->子组件props中的'trans_data'--->子组件template中的{{trans_data}}。

    四、子组件传值给父组件

    1.子组件向父组件传出值

    // 定义一个全局组件,一个按钮
    Vue.component('VBtn', {
        data() {
            return {
                name: '全局组件中的按钮'
            }
        },
        // 子组件中接收trans_data中的值(就是父组件中msg的值)
        props:['trans_data'],
        // 1.在<button>中绑定click时间,触发kidClickHandler事件函数
        template: `
                <div>
                    <button @click="kidClickHandler">msg翻转</button>
                </div>
            `,
        methods:{
            // 2.kidClickHandler事件函数使用$emit触发父组件实例上的事件,事件名是parentClickHandler,传递参数是翻转后的trans_data
            kidClickHandler(){
                this.$emit('parentClickHandler',this.trans_data.split('').reverse().join(''));
            }
        }
    })

    1)子组键实现onclick事件绑定,触发kidClickHandler事件函数

    2)在kidClickHandler事件函数中使用$emit触发父组件的parentClickHandler事件,并传递参数(翻转后的trans_data)

    2.父组件接收子组件传递的值

    var vm = new Vue({
        el: "#app",
        data() {
            return {
                msg: 'hello world',
            }
        },
        // 在VBtn中我们绑定一个属性trans_data,通过这个属性将msg的值传递给子组件VBtn
        template: `
            <div>
                <VBtn :trans_data="msg" @parentClickHandler="processClickHandler"></VBtn>
                <h2>{{msg}}</h2>
            </div>
        `,
        // 3.当子组件的kidClickHandler被触发时,$emit会触发parentClickHandler事件,即会执行processClickHandler事件函数
        methods:{
            processClickHandler(inversed_msg){
                // 4.将从子组件传递过来的翻转后的trans_data赋值给msg。完成msg的翻转,并更新到<h2>{{msg}}</h2>中
                this.msg = inversed_msg;
            }
        }
    })

    1)父组件的parentClickHandler被触发,调用processClickHandler事件函数

    2)processClickHandler事件函数接收子组件传递的参数(翻转后的trans_data)

    3)processClickHandler事件函数,将接收的参数赋值给msg属性

    4)由于<h2>中使用msg属性,则msg会自动更新

    3.组件传值给父组件完整代码

    <body>
    <div id="app">
    </div>
    <script src="./static/vue.js"></script>
    <script>
        // 定义一个全局组件,一个按钮
        Vue.component('VBtn', {
            data() {
                return {
                    name: '全局组件中的按钮'
                }
            },
            // 子组件中接收trans_data中的值(就是父组件中msg的值)
            props:['trans_data'],
            // 1.在<button>中绑定click时间,触发kidClickHandler事件函数
            template: `
                    <div>
                        <button @click="kidClickHandler">msg翻转</button>
                    </div>
                `,
            methods:{
                // 2.kidClickHandler事件函数使用$emit触发父组件实例上的事件,事件名是parentClickHandler,传递参数是翻转后的trans_data
                kidClickHandler(){
                    this.$emit('parentClickHandler',this.trans_data.split('').reverse().join(''));
                }
            }
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
                    msg: 'hello world',
                }
            },
            // 在VBtn中我们绑定一个属性trans_data,通过这个属性将msg的值传递给子组件VBtn
            template: `
                <div>
                    <VBtn :trans_data="msg" @parentClickHandler="processClickHandler"></VBtn>
                    <h2>{{msg}}</h2>
                </div>
            `,
            // 3.当子组件的kidClickHandler被触发时,$emit会触发parentClickHandler事件,即会执行processClickHandler事件函数
            methods:{
                processClickHandler(inversed_msg){
                    // 4.将从子组件传递过来的翻转后的trans_data赋值给msg。完成msg的翻转,并更新到<h2>{{msg}}</h2>中
                    this.msg = inversed_msg;
                }
            }
        })
    </script>
    </body>

    实现效果:

    五、平行组件传值

    平行组件传值,能够代替父子传值,用得比较多一些。

    1.定义一个公交车对象

    该公交车对象主要用来承载平行组件之间的参数传递。他是一个vue对象。

    // 1.定义一个bus对象(vue对象,用来承载平行组件的数据传递)
    let bus = new Vue();

    2.Test1数据发送者

    // 定义Test1全局组件
    Vue.component('Test1', {
        data() {
            return {
                msg: 'Test1中的值'
            }
        },
        // 2.在<button>中绑定click时间,触发trans_data_to_Test2事件函数
        template: `
                <div>
                    <button @click="trans_data_to_Test2">传递值给Test2</button>
                </div>
            `,
        methods:{
            // 3.trans_data_to_Test2事件函数,触发bus中的testData事件函数(这个名字需要和接收方Test2中$on中的事件函数名一致)
            trans_data_to_Test2(){
                bus.$emit('testData',this.msg);
            }
        }

    1)button绑定click事件,触发trans_data_to_Test2事件函数

    2)trans_data_to_Test2调用bus的testData事件函数(该函数不需要单独去定义,理解为一个名字标识即可),并将要传递的数据作为参数传入

    3.Test2数据接收者

    // 定义Test2全局组件
    Vue.component('Test2', {
        data() {
            return {
                msg: ''
            }
        },
        template: `
                <div>
                     这里是Test2组件,msg的值为:{{msg}}
                </div>
            `,
        // 4.给bus绑定$on函数,即可获取Test1使用$emit传递到testData事件函数中的参数
        created(){
            // 5.这里必须使用箭头函数,否则this指向的是bus,而我们是要给Test2的msg赋值。使用箭头函数,this指向的是调用bus的实例,也就是Test2
            bus.$on('testData',(val)=>{
                this.msg = val;
            })
        },
        methods:{}
    })

    1)在created函数中,给bus绑定$on,在这里从testData中获取val参数中的数据

    2)将获取的数据复制给自己的msg属性,这里注意,在$on中的回调函数,必须使用箭头函数,让this指向Test2组件,而不是bus

    4.平行组件传值完整代码(Test1-->Test2)

    <body>
    <div id="app">
    </div>
    <script src="./static/vue.js"></script>
    <script>
        // 1.定义一个bus对象(vue对象,用来承载平行组件的数据传递)
        let bus = new Vue();
        // 定义Test1全局组件
        Vue.component('Test1', {
            data() {
                return {
                    msg: 'Test1中的值'
                }
            },
            // 2.在<button>中绑定click时间,触发trans_data_to_Test2事件函数
            template: `
                    <div>
                        <button @click="trans_data_to_Test2">传递值给Test2</button>
                    </div>
                `,
            methods:{
                // 3.trans_data_to_Test2事件函数,触发bus中的testData事件函数(这个名字需要和接收方Test2中$on中的事件函数名一致)
                trans_data_to_Test2(){
                    bus.$emit('testData',this.msg);
                }
            }
        })
        // 定义Test2全局组件
        Vue.component('Test2', {
            data() {
                return {
                    msg: ''
                }
            },
            template: `
                    <div>
                         这里是Test2组件,msg的值为:{{msg}}
                    </div>
                `,
            // 4.给bus绑定$on函数,即可获取Test1使用$emit传递到testData事件函数中的参数
            created(){
                // 5.这里必须使用箭头函数,否则this指向的是bus,而我们是要给Test2的msg赋值。使用箭头函数,this指向的是调用bus的实例,也就是Test2
                bus.$on('testData',(val)=>{
                    this.msg = val;
                })
            },
            methods:{}
        })
        var vm = new Vue({
            el: "#app",
            data() {
                return {
    
                }
            },
            // 在VBtn中我们绑定一个属性trans_data,通过这个属性将msg的值传递给子组件VBtn
            template: `
                <div>
                    <Test1></Test1>
                    <Test2></Test2>
                </div>
            `
        })
    </script>
    </body>

    Test1(数据发送者)和Test2(数据接受者)都是全局组件,并且都在vue根实例中使用,他们两是平行关系(父子关系也可以使用平行传值)。

    平行传值的核心就是利用一个中间vue实例(bus公交车实例)来实现数据的传递。不同于父子传值。父子之间有特定的关系,所以传递值不需要借助中间实例。

    66

  • 相关阅读:
    力扣236题、235(二叉树最近公共祖先)
    力扣222题(完全二叉树的节点个数)
    力扣130题、990题(并查集算法)
    力扣855题(考场就座)
    力扣659题(分割数组为连续子数组)
    力扣435题(区间调度问题)
    【转】编程之:EXPORT_SYMBOL使用
    【转】shell工具之:常用shell脚本命令详细
    【转】vim工具命令之:添加行号和删除行号
    BUG之【虚拟机报错】:Centos出现 rm: cannot remove x: Read-only file system 的总结
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/12270950.html
Copyright © 2011-2022 走看看