Vue — 第四天(components组件)

问题导入

下面的代码是一个折叠面板的效果。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- <script src="./vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
        body{
            background-color: #ccc;
        }
        #app{
            width: 400px;
            margin: 20px auto;
            background-color: #fff;
            border:4px solid blueviolet;
            border-radius: 1em;
            box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
            padding:1em 2em 2em;
        }
        h3{
            text-align: center;
        }
        .title{
            display: flex;
            justify-content: space-between;
            align-items: center;
            border:1px solid #ccc;
            padding: 0 1em;
        }
        .title h4{line-height: 2;margin:0;}
        .container{
            border:1px solid #ccc;
            padding: 0 1em;
        }
        .btn {
            /* 鼠标改成手的形状 */
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="app">
        <h3>案例:折叠面板</h3>
        <div>
            <div class="title">
                <h4>芙蓉楼送辛渐</h4>
                <span class="btn"
                v-on:click="isVisiable= !isVisiable">
                {{isVisiable ? '收起' : '展开'}}
                </span>
            </div>
            <div class="container" v-show="isVisiable">
                寒雨连江夜入吴,
                <br>
                平明送客楚山孤。
                <br>
                洛阳亲友如相问,
                <br>
                一片冰心在玉壶。
                <br>
            </div>
        </div>
    </div>
    <script>
        const vm = new Vue({
            el: "#app",
            data: {
                isVisiable: true // 表示当前 内容是否显示
            }
        })
       
    </script>

</body>
</html>

如何实现多个折叠面板

解决方案:

  • 复制代码
    • 代码重复 冗余
    • 不利于维护
  • 封装组件

体验组件的使用

代码如下:

<div id="app">
      <h2>案例:折叠面板</h2>
        <pannel></pannel>
        <pannel></pannel>
      </div>
    </div>
    <script src="./vue.js"></script>
    <script>
        var vm = new Vue({
            el: '#app',

            components: {
              pannel: {
                data(){
                  return {isOpen: false}
                },
                template: `<div class="box">
                  <div class="title" >
                    <h4>我是标题12</h4><span class="btn" @click="isOpen=!isOpen">{{isOpen ? '收起' : '展开'}} </span>  
                  </div>
                  <div class="container" v-show="isOpen">
                    我是内容
                    <br>
                    我是内容
                  </div>
                </div>`
              }
            }
        })
    </script>

vue-devtools

在使用组件的时候,不好观察效果,vue官方提供了一个针对vue项目的调试工具。

使用效果

20200514010129016.png

安装

方法一:chrome浏览器应用商店下载

  • https://chrome.google.com/webstore/category/extensions?hl=zh-CN 安装。

方法二:本地安装步骤

1、解压今天资料中的 vue-devtools.zip 文件,注意:解压到某个不会改动的目录。

在这里插入图片描述

2、进入chrome浏览的扩展程序

在这里插入图片描述

3、打开开发者模式

在这里插入图片描述

4、加载已解压的扩展程序

在这里插入图片描述

用vue-devtools 来观察上的组件

通过浏览器打开使用了vue技术的网页

在这里插入图片描述

在这里插入图片描述

学习组件的使用

理解组件

vue组件是可复用的 Vue 实例,它可以包含独立的HTML结构,CSS样式,JS代码。它可以把一个完整的页面,根据界面功能拆分成若干组件。

js模块,独立的一个js文件,提供js逻辑,函数。

组件底层是什么?

  • 组件本质上是一个vue实例,但是写法和vue实例不同,new Vue({//配置对象})
  • 在Vue实例中 new Vue({components:{//组件}})
  • 组件有配置对象:
    • 不包含 el 选项,el是指定vue实例(或者称为根组件)管理的视图容器。
    • 通过 template选项,指定html结构容器,每个组件模板有且只有一个根元素。
    • data数据项必须是一个函数,通过它的返回值来设置数据。
  • 配置选项:data methods watcher filters directives computed created …(创建组件,创建vue实例)

定义格式

new Vue({
    el: '#app'
    data: {},
    components: {
        // 组件本质上是一个vue实例,但是写法和vue实例不同
        组件名1: { 
            data () { return {数据项}}, 	// 必须是一个函数
            template: `<div>模板</div>`,	// 约定模板
            props:[], // 从父组件中获取数据
            methods:{},
            watcher:{},
            filters:{},
            directives:{},
            computed:{},
            created(){},
            components:{} // 继续定义子组件
            ...

        },
        组件名2: {
            data () { return {数据项}},
            template: `<div>模板</div>`,
            props:[], // 从父组件中获取数据
            methods:{}
            ...
        }
    }
})

组件本质上是一个vue实例,但是在写法上和vue实例稍有不同:

  • 不包含 el 选项,el是指定vue实例(或者称为根组件)管理的视图容器。
  • 通过 template选项,指定html结构容器,每个组件模板有且只有一个根元素。
  • data数据项必须是一个函数,这个函数必须返回一个对象,在这个对象中设置数据项。

使用格式

在模板中使用,就像使用dom标签一样。

<子组件1></子组件1>

基本示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
         <h3>学习组件的使用</h3>
         <city-qj></city-qj>
         <br>
         <comp1></comp1>
         <comp1></comp1>
         <br>

    </div>
    <script src="./vue.js"></script>
    <script>
        // 1. 组件是什么
        //  组件是可复用的Vue实例。
        
        // 2. 如何定义
        // new Vue ({
        //     el:,//
        //     components: {
        //         // 定义组件
        //         // 组件名1 : 对组件的配置项,它是一个对象.
        //         //          在new Vue({ }) 可以写的配置项,在组件中基本上都可以用
        //         comp1: {
        //             // 组件的data与vue实例有一点区别:它必须是一个函数,通过这个函数来返回对象,
        //             // 在对象中定义自己的数据项
        //             data: function() { 
        //                 return { 
        //                     // 组件自己的数据项
        //                 }
        //             },
        //             // 用来约定组件的模板。它等价于vue实例的 el
        //             // 它只能一个根元素
        //             // template: `<div></div><h1></h1>` // 不ok
        //             template: `<div></div>` // ok
        //             watch:,
        //             computed:,
        //             filters,
        //             directives,
        //             components,

        //         }
        //     }
        // })
        
        // 3. 如何使用
        //    像使用dom标签一样使用,标签就是组件名


        // 要点:
        // 1.  数据。
        //     组件是自成体系,它的数据只能自己用,在其它组件不能使用;它不能直接使用vue实例中的数据
        //     格式:data(){ return {} }
        // 2. 视图。由template决定。
        // 3. methods
        //    与vue实例中的一样,定义,去调用。
        //    它只能调用自己的定义的methods

        const vm = new Vue({
            el: '#app',
            data: {
                name: 'js'
            },
            methods: {
                f1 () {

                }
            },
            components: {
                "city-qj": {
                    // 定义数据:只能在当前组件中使用
                    data() {
                        return {
                            teC: '小龙虾'
                        }
                    },
                    // 设置视图
                    template: `<div>
                                <h4>城市:潜江</h4>
                                <p>{{teC}}</p>
                                <button @click="hClick">点我</button> 
                            </div>`,
                    // 定义方法:只能在当前组件中使用
                    methods: {
                        hClick () {
                            // 它的this是组件自己
                            console.log(this.teC)
                        }
                    }
                },
                comp1: {
                    template: '<div><h1>comp1组件</h1></div>'
                }
            }
        })
    </script>
</body>
</html>

组件名和父子组件

<div id="app">
    <MyComp1></MyComp1>
    <myComp1></myComp1>
    <my-comp1></my-comp1>
</div>
<script src="./vue.js"></script>
<script>
    new Vue({
        el: '#app',
        components: {
            MyComp1:{template:`<div>子组件:MyComp1 大驼峰</div>`},
            myComp1:{template:`<div>子组件:myComp1 小驼峰</div>`},
            mycomp1:{template:`<div>子组件:mycomp1 纯小写</div>`}
        }
    });
</script>
  • 父子组件

    • 如果组件A在组件B中被调用,则称组件B是组件A的父组件,而组件A是组件B的子组件
  • 组件名:

    • 在定义组件时,组件名可以是小驼峰(myCom1),大驼峰的写法(MyCom1)
    • 在模板中使用组件时,全采用全小写的烤串写法(my-com1)

组件的嵌套

一个组件内部,还可以继续定义,使用组件。

<div id="app">
    <my-comp1></my-comp1>
</div>
<script src="./vue.js"></script>
<script>
    new Vue({
        el: '#app',
        components: {
            myComp1:{
                template:`<div>子组件 <abcd></abcd></div>`,
                components:{
                    abcd:{template:`<h5>ABCD</h5>`}
                }
            }
        }
    });
</script>

小结

vue中的组件系统让我们可以像搭积木一样,通过不同的组件来建立整个页面。

在这里插入图片描述

父子组件传值-从数据从父组件传给子组件

在这里插入图片描述

基本步骤

  • 在父组件中使用子组件时,给子组件传入自定义属性;

  • 子组件内部声明props来接收属性。

原则:你情我愿

在这里插入图片描述

示例

<div>
	<com1 abc="vue" :myMsg="info"></com1>
</div>

components:{
 com1:{
  template: `<div>子组件:{{myMsg}}</div>`,
  // props 用来接收使用组件所绑定的属性数据,属性的名字就是数据的字段名称
  // props: ['abc'] 接收到了使用组件绑定的abc属性属性,使用vue实例即可访问,this.abc 
  // 此时的 this.myMsg 其实就是父组件传递给子组件的数据
  props: ['abc','myMsg']
})
  • 只读不能修改
  • 父组件中修改了值,也会影响子组件中的Props值

案例-折叠面板

<style>
        body{
            background-color: #eee;
        }
        #app{
            background-color:#fff;
            width: 500px;
            margin: 50px auto;
            text-align: center;
            box-shadow: 3px 3px 3px rgba(0 , 0, 0, 0.5);
            padding:1em 2em;
        }
        .title {
            line-height: 1;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .btn{ cursor: pointer;}
        .container {
            border: 1px solid #ccc;
            padding: 1em;  
        }
    </style>
</head>
<body>
    <div id="app">
      <h2>案例:折叠面板</h2>
        <pannel title="标题1" content="内容1"></pannel>
        <pannel title="标题2" content="内容2"></pannel>
      </div>
    </div>
    <script src="./vue.js"></script>
    <script>
        const pannel = {
          props: ['title','content'],
          data(){
            return {isOpen: false}
          },
          template: `<div class="box">
            <div class="title" >
              <h4>{{title}}</h4><span class="btn" @click="isOpen=!isOpen">{{isOpen ? '收起' : '展开'}} </span>  
            </div>
            <div class="container" v-show="isOpen">
              {{content}}
            </div>
          </div>`
        }
        var vm = new Vue({
            el: '#app',
            components: {
              pannel: pannel
            }
        })
    </script>

组件中的插槽

对于前面的折叠面板组件,如果使用组件时,希望传入一些比较复杂的内容,让每个折叠面板的内容区域都不一样(不仅是数据不同,而是整体结构都不同),则可以使用插槽技术。

image-20200513224752503

定义格式

在子组件的模板中通过slot预留出来区域,充当占位符的功能。

template: `
    其它元素....
    <slot></slot>
	其它元素....
	<slot name="插槽1"></slot>
	其它元素...
	<slot name="插槽2"></slot>
	`

如果slot有name属性,则它是叫具名插槽,否则叫默认插槽

技巧: 如果只有一个插槽,则不需要写插槽名。

使用格式

在使用子组件时,传入自定义的内容即可。

单个插槽

如果子组件上只有一个默认插槽。

<子组件><div>任意内容....</div></子组件>

多个插槽

如果子组件上定义了多个插槽,则要对应标记出slot的名字。

<子组件>
   <div>任意内容.将会填充默认插槽的位置...</div>
   <div slot='插槽名1'>任意内容.将会填充插槽1的位置...</div>
   <div slot='插槽名2'>任意内容.将会填充插槽2的位置...</div>
</子组件>

默认插槽使用示例

<div id="app">
    <com1></com1>
    <com1>
        <h1>我是父组件中来填充默认插槽的内容</h1>
    </com1>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        components: {
            com1: {
                template: `<div>我是子组件com1,<slot>这里默认插件的默认内容</slot> </div>`
            }
        }
    })
</script>

具名插槽使用示例

<div id="app">
    <com1></com1>
    <com1>
        <h1 slot="slot1">我是父组件中来填充具名插槽的内容</h1>
    </com1>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        components: {
            com1: {
                template: 
                `<div>我是子组件com1,
<slot>这里默认插件的默认内容</slot>
<slot name="slot1"></slot>
    </div>`
            }
        }
    })
</script>

案例:用插槽来改进折叠面板

允许用户自行定义折叠面板中content区域的内容。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
    <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
    <style>
        body{
            background-color: #ccc;
        }
        #app{
            width: 400px;
            margin: 20px auto;
            background-color: #fff;
            border:4px solid blueviolet;
            border-radius: 1em;
            box-shadow: 3px 3px 3px rgba(0, 0, 0, .5);
            padding:1em 2em 2em;
        }
        h3{
            text-align: center;
        }
        .title{
            display: flex;
            justify-content: space-between;
            align-items: center;
            border:1px solid #ccc;
            padding: 0 1em;
        }
        .title h4{line-height: 2;margin:0;}
        .container{
            border:1px solid #ccc;
            padding: 0 1em;
        }
        .btn {
            /* 鼠标改成手的形状 */
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="app">
        <h3>案例:折叠面板-插槽</h3>
       
        <my-pannel tit="标题1">
            <table>
                <tr>
                    <td>1</td>
                    <td>2</td>
                    <td>3</td>
                </tr>
            </table>
        </my-pannel>
        <my-pannel tit="标题2">
            <img src="http://img3m9.ddimg.cn/32/4/28530149-1_w_7.jpg">
            <!-- 图片 -->
        </my-pannel>
        <my-pannel tit="标题3">
            <div>
                寒雨连江夜入吴,
                <br>
                平明送客楚山孤。
                <br>
                洛阳亲友如相问,
                <br>
                一片冰心在玉壶。
                <br>
            </div>
        </my-pannel>
    </div>
    <script>
        // 对于折叠面板:
        // 特点:
        //   - 标题一般会比较简单,所以使用props传值
        //   - 内容可能比较复杂,通过插槽技术允许用户传入任意内容
        //     由于只需要一个插槽,所以就直接使用默认插槽就行。

        // 下面是用组件来解决的
        const vm = new Vue({
            el: "#app",
            components: {
                // 定义的组件的名字
                MyPannel: {
                    data () {
                        return {
                            isVisiable: true, // 表示当前 内容是否显示
                        }
                    },
                    props:['tit'],
                    template: `
                        <div>
                            <div class="title">
                                <h4>{{tit}}</h4>
                                <span class="btn"
                                v-on:click="isVisiable= !isVisiable">
                                {{isVisiable ? '收起' : '展开'}}
                                </span>
                            </div>
                            <div class="container" v-show="isVisiable">
                                <slot></slot>
                            </div>
                        </div>
                    `
                }
            }
        })
       
    </script>

</body>
</html>

子组件传值给父组件

背景:

在子组件中需要向父组件传值

在这里插入图片描述

原理

  • 自定义事件 + emit

步骤

  • 在父组件中:使用子组件时,在子组件上添加自定义事件监听

    <子组件 
         @自定义事件名1="处理事件的函数1"
      	 @自定义事件名2="处理事件的函数2"></子组件>
    
  • 在子组件中:在某个时刻,通过$emit向父组件传递事件,并附加参数

    this.$emit(自定义事件名1,附加参数)
    

示例

<div id="app">
    <comp1 @gameover1="hGameover1" @over="hover"></comp1>
</div>
methods: {
	hover (param) {
		console.log(param)
	}
},
components:{
	comp1: 
		{
            template:`<div> 
                <button @click="$emit('gameover')"> 发消息 </button>
                <button @click="hClick"> 发消息 </button>
                </button></div>`,
            methods: {
                hclick () {
                    this.$emit('over', {a:1,b:2})
                }
            }
		}
}

案例-测试题

在这里插入图片描述
在这里插入图片描述
参考代码:

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.js"></script>
    <style>
        body {
            background-color: #eee;
        }

        #app {
            background-color: #fff;
            width: 500px;
            margin: 50px auto;
            box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
            padding: 2em;
        }

        .box {
            padding: 1em;
            border: 1px solid #ccc;
            margin: 1em;
        }
        .subject{
          margin:5px;
          padding:5px;
          font-size: 20px;
        }
        .subject span {
          display:inline-block;
          text-align: center;
          width: 20px;
        }
        .subject input {
          width: 50px;
          height: 20px;
        }
        .right{color:green}
        .error{
          color:red;
        }
        .undo{color:#ccc;}
    </style>
</head>

<body>
    <div id="app">
      <h2>测试题</h2>

        <subject v-for="idx in count" :idx="idx" @submit='hGetAns'></subject>
        <div>
          <flag 
            v-for="(item,idx) in result"
            :idx="item.idx" :ans="item.ans"></flag>
      
        </div>
    </div>
    <script>
        
        var vm = new Vue({
           
            data: {
              count:5,
              result:[]
            },
            created () {
              for(var i = 1; i<= this.count;i++) {
                this.result.push({idx:i,ans:'未完成'})
              }
            },
            components: {
                flag: {
                  props:["idx","ans"],
                  computed: {
                    cCla () {
                      return this.ans ==='正确' ? 'right': (this.ans==='错误'?'error':'undo')
                    }
                  },
                  template: `<span :class="cCla">{{idx}}: {{ans}}</span>`
                },
                subject: {
                  props:["idx"],
                    template: `<div class="subject">
                        <span>{{num1}}</span>+<span>{{num2}}</span> = <input v-model.trim.number="result" :disabled="isSubmit"/>
                        <button :disabled="isSubmit" @click="hSubmit">提交</button>
                    </div>`,
                    data:function() { 
                      return {
                        num1: Math.ceil(Math.random()*10),
                        num2: Math.ceil(Math.random()*10),
                        result: '',
                        isSubmit:false
                      }
                    },
                    methods: {
                        hSubmit () {
                            let rs =  this.result == (this.num1 + this.num2)
                            this.isSubmit = true
                            this.$emit('submit', {ans:rs,idx:this.idx})
                        }
                    }
                }
            },
            methods: {
                hGetAns (result) {
                  // console.log(result)
                  const rs = this.result.find(it => it.idx === result.idx)
                  rs.ans = result.ans ? '正确' : '错误'
                }
            }
        }).$mount('#app')
    </script>
</body>

</html>

全局组件

在vue实例中定义的组件称为局部组件,它只能当前vue实例中使用;

在vue实例之外的定义的组件称为全局组件,它可以在多个vue实例中使用,方便封装组件共享给他人使用。

定义格式

Vue.component('组件名称','组件配置对象')

这里的组件配置对象与上述局部组件的方法一样。

使用格式

与局部组件一致。

示例

  <div id="app">
    <!-- 注册组件时候使用的组件名称,在视图中使用就是自定义标签的名称 -->
    <!-- 注意:组件的名称不能和原生html标签重名 -->
    <com-article></com-article>
  </div>
  <script src="./vue.js"></script>
  <script>
    // 全局注册组件
    Vue.component('com-article', {
      // 组件配置对象,和vue实例的配置对象基本一致,没有el选项
      // template选项必须指定,当前组件管理的视图,需要有一个根标签。
      // 在模板字符中,可以使用插值表达式,和任意指令,但是只能使用当前组件数据和函数。
      template: '<div>{{count}} <button @click="add()">自增1</button></div>',
      // 声明数据,使用的还是data,但是是一个函数,函数的返回值才是组件的数据,必须是对象。
      data () {
        return {
          count: 100
        }
      },
      // 可以使用任意配置选项
      methods: {
        add () {
          this.count ++ 
        }
      }
    })

    // 根实例
    const vm = new Vue({
      el: '#app'
    })
  </script>

件称为局部组件,它只能当前vue实例中使用;

在vue实例之外的定义的组件称为全局组件,它可以在多个vue实例中使用,方便封装组件共享给他人使用。

定义格式

Vue.component('组件名称','组件配置对象')

这里的组件配置对象与上述局部组件的方法一样。

使用格式

与局部组件一致。

示例

  <div id="app">
    <!-- 注册组件时候使用的组件名称,在视图中使用就是自定义标签的名称 -->
    <!-- 注意:组件的名称不能和原生html标签重名 -->
    <com-article></com-article>
  </div>
  <script src="./vue.js"></script>
  <script>
    // 全局注册组件
    Vue.component('com-article', {
      // 组件配置对象,和vue实例的配置对象基本一致,没有el选项
      // template选项必须指定,当前组件管理的视图,需要有一个根标签。
      // 在模板字符中,可以使用插值表达式,和任意指令,但是只能使用当前组件数据和函数。
      template: '<div>{{count}} <button @click="add()">自增1</button></div>',
      // 声明数据,使用的还是data,但是是一个函数,函数的返回值才是组件的数据,必须是对象。
      data () {
        return {
          count: 100
        }
      },
      // 可以使用任意配置选项
      methods: {
        add () {
          this.count ++ 
        }
      }
    })

    // 根实例
    const vm = new Vue({
      el: '#app'
    })
  </script>

猜你喜欢

转载自blog.csdn.net/weixin_44694682/article/details/106149443