Fork me on GitHub

-Vue- 组件

一:组件化开发基础

1 组件是什么?有什么用?

组件就是:扩展 HTML 元素,封装可重用的代码,目的是复用,例如:有一个轮播图,可以在很多页面中使用,一个轮播有js,css,html放到一起,有逻辑,有样式,有html。

组件的分类:

  • 全局组件:可以放在根中
  • 局部组件:

工程化开发之后:

  • 一个组件就是一个 xx.vue

2 组件的注册方式

2.1 定义全局组件,绑定时间,编写样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>全局组件</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <div @click="handleClick">我是根部组件</div>
    <global v-if="isShow"></global>    <!--归vue实例管-->
    <ul>
        <li v-for="i in 4">
            <global></global>   <!--复用-->
        </li>
    </ul>

</div>
</body>
<script>
    // 创建1个组件对象(全局组件)
    Vue.component('global', {
        template: `
            <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px;" @click="handleClick">我是头部组件</div>
                <div v-if="isShow">显示消失</div>
            </div>
        `,
        methods: {
            handleClick() {
                console.log('我被点击了')
                this.isShow = !this.isShow
            }
        },
        data() {
            return {
                isShow: true
            }
        }
    })

    // 组件与Vue实例是隔离的,所以点击事件绑定的是同一个变量名是没有关系的
  	// 一个是对根部部件的点击事件,一个是对头部部件的点击事件
    var vm = new Vue({
        el: '#box',
        data: {
            isShow: true
        },
        methods: {
            handleClick() {
                console.log('我被点击了 我是根组件')   
            }
        }
    })
</script>
</html>

2 定义局部组件

2.1 局部组件 放在Vue实例(根组件)中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>

<div id="box" style="max-width: 300px">
    <local></local>
    <global></global>
</div>


</body>

<script>
    // 创建1个组件对象 (全局组件)
    Vue.component('global',{
        template:`
         <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px 10px; border-radius: 5px;margin: 5px 0;">
                    我是全局组件
                </div>
            </div>
                `,
    })

    var vm = new Vue({
        el: '#box',
        data: {},
        // 创建1个组件对象 (局部组件)
        components: {
            local: {   // local 组件名
                template:`
                     <div>
                        <div style="background: rgba(104,255,104,0.7); padding: 5px 10px; border-radius: 5px; margin: 3px 50px 3px 0;"
                             @click="handleClick">我是局部组件
                        </div>
                    </div>
                `, // 局部组件模板
                methods: {
                    handleClick() {
                        console.log('我被点击了')
                    }
                }
            }
        }
    })

</script>
</html>

2.2 局部组件 放在全局组件中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box" style="max-width: 300px">
    <ul>
        <li v-for="i in 3">
            <global>

            </global>

        </li>
    </ul>
</div>

<script>
    // 创建全局组件
    Vue.component('global',{
        template:`
        <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px 10px; border-radius: 5px;margin: 5px 0;">
                    我是全局的组件
                </div>
                <local></local>
                <local></local>
                <br>
            </div>`,
        // 创建局部组件,<local> 为该局部组件的标识
        components: {
            local: {
                template: `
            <div>
                <div style="background: rgba(104,255,104,0.7); padding: 5px 10px; border-radius: 5px; margin: 3px 50px 3px 0;">我是局部组件</div>
            </div>
        `,
            }
        }
    })

    let vm = new Vue({    // 全局组件依赖于Vue实例
        el: '#box',
    })
</script>

</body>
</html>

2.3 注意点

  • 定义的组件(body中的位置)必须要放在Vue实例(这也是一个组件 根组件)中。
  • 局部组件必须放在全局组件/根组件中,无法单独使用。

三:组件编写方式 与 Vue实例的区别

Vue实例

  • 其实,它也是一个组件,是一个根组件,并且可以在其中布置局部组件。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>

<div id="box">
    <ul>
        <li>字符串:{{name}}</li>
        <li>数值:{{age}}</li>
        <li><button @click="handleClick()">Click Here</button></li>
        <li><local></local></li>
    </ul>
</div>

</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            name: 'Darker',
            age: 18,
        },
        methods: {
            handleClick() {
                alert('按钮被点击')
            }
        },
        components: {
            local: {
                template: `
                <div @click="handleClick" v-if="isShow"> 我是根组件下的局部组件 </div>`,
                methods: {
                    handleClick() {
                        alert('局部组件被点击了')
                        this.isShow=!this.isShow
                    }
                },
                data() {
                    return {
                        isShow: true
                    }
                }

            }
        }
    })
</script>
</html>

组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>局部组件</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
    
<div id="box" style="max-width: 300px">
    <ul>
        <li v-for="i in 3">
            <global></global>
        </li>
    </ul>
</div>
    
</body>
<script>
    // 创建1个组件对象(全局组件)
    Vue.component('global', {
        template: `
            <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px 10px; border-radius: 5px;margin: 5px 0;">
                    我是全局组件
                </div>
                <local></local>
                <br>
            </div>
        `,
        // 创建1个组件对象(局部组件)
        components: {
            local: {
                template: `
            <div>
                <div style="background: rgba(104,255,104,0.7); padding: 5px 10px; border-radius: 5px; margin: 3px 50px 3px 0;">我是局部组件</div>
            </div>
        `,
            }
        }
    })
    let vm = new Vue({
        el: '#box',
    })
</script>
</html>

区别

  • 自定义组件需要有1个 root element,一般包裹在 1个div

  • 父子组件的data是无法共享的

    • 这一点就像Docker的容器一样,是相互隔离的,只要是组件就隔离,不论是子组件与父组件,还是子组件与子组件,或父组件与父组件,即只要是组件就是隔离的。

    • 就算父子的data数据相同,拥有相同的方法,也是互不影响的。

就像是在不同名称空间下的函数,并不会互相影响。
  • 组件可以有data、methods、computed....,但是 data 必须是一个函数

    • Vue实例:data是1个键值对,用来存放属性的。
    var vm = new Vue({
        el: '#box',
        data: {
            isShow: true
        }
    })
    
    • 组件:data是1个函数,需要有返回值(return)
    Vue.component('global', {
        template: `
            <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px;" @click="handleClick">我是头部组件</div>
                <div v-if="isShow">显示消失</div>
            </div>
    `,
        methods: {
            handleClick() {
                console.log('我被点击了')
                this.isShow = !this.isShow
            }
        },
        data() {
            return {
                isShow: true
            }
        }
    })
    

四:组件通信

1 父传子

  • 在全局组件中自定义属性:<global :myname="name" :myage="19"></global>
  • 在组件中获取:{{myname}} ——props: ['myname', 'myage']
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>

<div id="box">
    <global myname="name" myage="18"></global> <!-- 这里 myname="name" 定义的name为字符串-->
    <global :myname="name" :myage="19"></global> <!-- 这里是js语法,也就是js变量-->
    <global :myname="'Ben'" :myage="20"></global><!-- 这里是js语法,不过定义的是字符串-->

</div>

</body>
<script>
    Vue.component('global', {
        template: `
            <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div>
                {{myname}}
                {{myage}}
            </div>
        `,
        props: ['myname', 'myage']   // 写入父传子的数据的key值
    })

    let vm = new Vue({
        el: '#box',
        data :{
            name: 'darker'
        }
    })
</script>



</html>

属性验证

  • 限制父传子的变量类型

    props: {
        myname: String,
        isshow: Boolean
    }
    
  • 父传子的时候注意以下区别

     <global myname="name" myage="18"></global> 
    <!-- 这里 myname="name" myage="18"定义的name为html的字符串-->
    <global :myname="name" :myage="19"></global> 
    <!-- 这里是js语法,也就是myname是js变量,myage是整形-->
    <global :myname="'Ben'" :myage="20"></global>
    <!-- 这里是js语法,不过myname定义的是字符串,myage定义的是整形-->
    
    
    
  • 实例

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="../Vue/vue.js"></script>
    </head>
    <body>
    
    <div id="box">
    
        <global :myname="name" :myage="19" :myshow="isShow"></global> <!-- 这里是js语法,也就是js变量-->
        <global :myname="'Ben'" :myage="20" :myshow="false"></global><!-- 这里是js语法,不过定义的是字符串-->
    
    </div>
    
    </body>
    <script>
        Vue.component('global', {
            template: `
                <div>
                    <div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div>
                    {{myname}}
                    {{myshow}}
                </div>
            `,
            props: {
                    myname: String,
                    myshow: Boolean,
    
            }
        })
    
        let vm = new Vue({
            el: '#box',
            data :{
                name: 'darker',
                isShow: true
            }
        })
    </script>
    
    </html>
    

2 子传父 (控制子组件的显示和隐藏)

点击子组件,就会触发父组件的某个函数执行

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>

<div id="box">

    <global @myevent="handleClick"></global>

</div>

</body>
<script>
    Vue.component('global', {
        template: `
            <div>
                <div style="background: rgba(255,104,104,0.7); padding: 5px;">全局组件/子组件</div>
                <button @click="handleNav">点我</button>
            </div>
        `,
        data() {
            return {
                name: 'Darker'
            }
        },
        methods: {
            handleNav() {
                console.log('我是子组件函数')
                this.$emit('myevent',this.name,666,777)
            }
        }
    })

    let vm = new Vue({
        el: '#box',
        data: {},
        methods: {
            handleClick(a, b, c) {
                console.log('我是父组件函数')
                console.log(a)
                console.log(b)
                console.log(c)
            }
        }
    })
</script>


</html>

小案例

  • 子组件有一个按钮和一个输框,子组件输入完内容后,数据在父组件中展示。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>

<div id="box">

    <global @myevent="handleClick"></global>
    <br>
    <div>父组件接收到:{{ myText }}</div>

</div>

</body>
<script>
    Vue.component('global', {
        template: `
            <div>
                <input type="text" v-model="myText">
                <button @click="handleClick">点我传数据</button>
                
            </div>
        `,
        data() {
            return {
                myText: ''
            }
        },
        methods: {
            handleClick() {
                console.log('我是子组件函数')
                this.$emit('myevent', this.myText)
            }
        }
    })

    let vm = new Vue({
        el: '#box',
        data: {
            myText: ''
        },
        methods: {
            handleClick(a) {
                console.log('我是父组件函数')
                console.log(a)
                this.myText = a
            }
        }
    })
</script>
</html>

因为是在两个不同的组件取同样的Js变量名,所以并不会冲突。

3 ref属性 (也可以实现组件间通信:子和父都可以实现通信)

  • ref放在 标签 上,拿到的是 原生的DOM节点。
  • ref放在 组件 上,拿到的是 组件对象。
  • 通过这种方式实现子传父 (this.$refs.mychild.text)
  • 通过这种方式实现父传子(调用子组件方法传参数)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
    <!--    通过ref,获取input的值-->
    <input type="text" ref="mytext">
    <button @click="handleClick">点我</button>
    <child ref="mychild"></child>
    <hr>
    <h3>根部件获取子部件数据</h3>
    <p>子部件的数据:{{ myText }}</p>
    <hr>
    <h3>ref 放在标签上获得的数据</h3>
    <p>{{ sign }}</p>



</div>
</body>
<script>
    Vue.component('child', {
        template: `
        <div>
        <h3>子部件获取根部件数据</h3>
        <p>根部件的数据:{{ myText }}</p>  </div>`,
        data() {
            return {
                text: '子组件数据',
                myText: ''
            }
        },
        methods: {
            add(a) {
                console.log(a)   // 得到父组件传来的数据
                this.myText = a
            }
        }
    })
    var vm = new Vue({
        el: '#box',
        data: {
            text: '父组件数据',
            myText: '',
            sign:''
        },
        methods: {
            handleClick() {
                console.log(this)
                //this.$refs.mytext 获取到input控件,取出value值
                // 该节点的数据值
                this.sign = this.$refs.mytext.value
                // 通过点击事件直接获得该组件对象的text
                this.myText = this.$refs.mychild.text
                // this.$refs.mychild.add()
                this.$refs.mychild.add(this.text) // 通过该函数给该组件传参,传递数据

            }
        }

    })
</script>
</html>

4 事件总线 (不同层级的不同组件通信)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>

<div id="box">
    <global1></global1>
    <hr>
    <global2></global2>
</div>

</body>
<script>
    // 定义1个时间总线
    let bus = new Vue({})

    // 组件1
    Vue.component('global1', {
        template: `
            <div>
                <h3>组件1</h3>
                <input type="text" v-model="myText">
                <button @click="handleClick1">点我传递数据到另一个组件</button>
            </div>
        `,
        data() {
            return {
                myText: ''
            }
        },
        methods: {
            handleClick1() {
                console.log(this.myText)
                bus.$emit('any', this.myText)  // 通过事件总线发送
            }
        }
    })
    // 组件2
    Vue.component('global2', {
        template: `
            <div>
                <h3>组件2</h3>
                收到的消息是:{{recvText}}
            </div>
        `,
        data() {
            return {
                recvText: ''
            }
        },
        mounted() { // 组件的挂载(生命周期钩子函数中的1个),开始监听时间总线上的:any
            bus.$on('any', (item) => {
                console.log('收到了', item,)
                this.recvText = item
            })
        },
        methods: {}
    })
    // 父组件
    let vm = new Vue({
        el: '#box',
        data: {},
    })
</script>
</html>

五:动态组件

1 基本使用

1 <component> 元素,动态地绑定多个组件到它的 is 属性
2 <keep-alive> 保留状态,避免重新渲染
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>
<div id="box">
    <ul>
        <li><a @click="who='child1'">首页</a></li>  <!--已经在此绑定了点击事件的函数-->
        <li><a @click="who='child2'">商品</a></li>
        <li><a @click="who='child3'">购物车</a></li>
        <hr>
         <component :is="who"></component>
    </ul>

</div>
</body>
<script>
    var bus = new Vue() //new一个vue的实例,就是中央事件总线
    Vue.component('child1', {
        template: `<div>
          首页
					<input type="text">
        </div>`,

    })
    Vue.component('child2', {
        template: `<div>
                   商品
										<input type="text">
                    </div>`,
    })
    Vue.component('child3', {
        template: `<div>
                   购物车
										<input type="text">
                    </div>`,
    })
    var vm = new Vue({
        el: '#box',
        data: {
            who: 'child1'
        },


    })
</script>
</html>


2 keep-alive 的使用

keep-alive可以让输入框内有的内容一致保持,不会因为切换而重置。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../Vue/vue.js"></script>
</head>
<body>

<div id="box">
    <ul>
        <li>
            <button @click="who='child1'">首页</button>
        </li>
        <li>
            <button @click="who='child2'">订单</button>
        </li>
        <li>
            <button @click="who='child3'">商品</button>
        </li>
    </ul>
    <keep-alive>
        <component :is="who"></component>
    </keep-alive>
</div>

</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            who: 'child1'
        },
        components: {
            child1: {
                template: `
                    <div>
                        <span style="border-bottom: 5px solid rgba(255,104,104,0.7)">我是首页</span>
                        <input type="text">
                    </div>
                `,
            },
            child2: {
                template: `
                    <div>
                        <span style="border-bottom: 5px solid rgba(255,104,255,0.7)">我是订单</span>
                        <input type="text">
                    </div>
                `,
            },
            child3: {
                template: `
                    <div>
                        <span style="border-bottom: 5px solid rgba(104,255,104,0.7)">我是商品</span>
                        <input type="text">
                    </div>
                `,
            }
        }
    })
</script>
</html>

六:slot 插槽

1 基本使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>slot 插槽</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>

<div id="box">
    <child>
        <h6>Hello World</h6>
    </child>
</div>

</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            who: 'child1'
        },
        components: {
            child: {
                template: `
                    <div>
                        <slot></slot>
                        <span style="border-bottom: 5px solid rgba(255,104,104,0.7)">我是组件的原内容</span>
                        <slot></slot>
                    </div>
                `,
            },
        }
    })
</script>
</html>

<h6>Hello World</h6> 自动找到了 部件中 <slot></slot> 然后插入

2 小案例(通过插槽实现在1个组件中控制另1个组件的显示隐藏)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>slot 插槽</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>

<div id="box">
    <!--通过插槽实现在一个组件中控制另一个组件的显示隐藏-->
    <child1>
        <button @click="isShow=!isShow">显示/隐藏组件2</button>
    </child1>

    <child2 v-if="isShow"></child2>
</div>
</body>
<script>
    Vue.component('child1', {
        template: `<div>
          组件1
          <slot></slot>
        </div>`,

    })
    Vue.component('child2', {
        template: `<div>
          <h3>组件2</h3>
        </div>`,

    })
    var vm = new Vue({
        el: '#box',
        data: {
            isShow: true
        }

    })
</script>
</html>

3 具名插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>具名插槽</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>

<div id="box">
    <!-- 具名插槽,把p标签给a插槽,div标签给b插槽-->
    <child>
        <p slot="a">我是具名插槽a插入的内容</p>
        <div slot="b">我是具名插槽b插入的内容</div>
    </child>
</div>
</body>
<script>
    Vue.component('child', {
        template: `<div>
            <slot name="a"></slot>
            <hr>
            <span style="border-bottom: 5px solid rgba(255,104,104,0.7)">我是组件的原内容</span>
            <hr>
            <slot name="b"></slot>
        </div>`,

    })
    var vm = new Vue({
        el: '#box',
        data: {}

    })
</script>
</html>
posted @ 2020-12-21 21:49  artherwan  阅读(93)  评论(0编辑  收藏  举报