Vue基础(七):常用特性(三)

  • A+
所属分类:Web前端
摘要

组件(Component)是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。


1. 组件

组件(Component)是Vue.js最强大的功能之一。

组件可以扩展HTML元素,封装可重用的代码。

1.1 组件注册

1.1.1 全局注册

Vue.component('组件名称', { }) 第1个参数是标签名称,第2个参数是一个选项对象
全局组件注册后,任何vue实例都可以用

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <button-counter></button-counter>     <button-counter></button-counter>     <button-counter></button-counter>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       组件注册     */     Vue.component('button-counter', {       data: function(){         return {           count: 0         }       },       template: '<button @click="handle">点击了{{count}}次</button>',       methods: {         handle: function(){           this.count += 2;         }       }     })     var vm = new Vue({       el: '#app',       data: {       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

 

可以看到组件可以重复使用。

1.1.2 组件注册注意事项

组件参数的data值必须是函数同时这个函数要求返回一个对象

组件模板必须是单个根元素

组件模板的内容可以是模板字符串 

如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <button-counter></button-counter>     <button-counter></button-counter>     <button-counter></button-counter>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       组件注册注意事项       1、组件参数的data值必须是函数       2、组件模板必须是单个跟元素       3、组件模板的内容可以是模板字符串      */     // Vue.component('button-counter', {     //   data: function(){     //     return {     //       count: 0     //     }     //   },     //   template: '<div><button @click="handle">点击了{{count}}次</button><button>测试</button></div>',     //   methods: {     //     handle: function(){     //       this.count += 2;     //     }     //   }     // })     // -----------------------------------     Vue.component('button-counter', {       data: function(){         return {           count: 0         }       },       template: `         <div>           <button @click="handle">点击了{{count}}次</button>           <button>测试123</button>         </div>       `,       methods: {         handle: function(){           this.count += 2;         }       }     })     var vm = new Vue({       el: '#app',       data: {       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

 

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <button-counter></button-counter>     <hello-world></hello-world>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       组件注册注意事项       如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是       在普通的标签模板中,必须使用短横线的方式使用组件     */     Vue.component('HelloWorld', {       data: function(){         return {           msg: 'HelloWorld'         }       },       template: '<div>{{msg}}</div>'     });     Vue.component('button-counter', {       data: function(){         return {           count: 0         }       },       template: `         <div>           <button @click="handle">点击了{{count}}次</button>           <button>测试123</button>           <HelloWorld></HelloWorld>         </div>       `,       methods: {         handle: function(){           this.count += 2;         }       }     })     var vm = new Vue({       el: '#app',       data: {        }     });   </script> </body> </html>

执行结果:

Vue基础(七):常用特性(三)

1.1.3 局部注册 

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <hello-world></hello-world>     <hello-tom></hello-tom>     <hello-jerry></hello-jerry>     <test-com></test-com>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       局部组件注册       局部组件只能在注册他的父组件中使用     */     Vue.component('test-com',{       template: '<div>Test<hello-world></hello-world></div>'     });     var HelloWorld = {       data: function(){         return {           msg: 'HelloWorld'         }       },       template: '<div>{{msg}}</div>'     };     var HelloTom = {       data: function(){         return {           msg: 'HelloTom'         }       },       template: '<div>{{msg}}</div>'     };     var HelloJerry = {       data: function(){         return {           msg: 'HelloJerry'         }       },       template: '<div>{{msg}}</div>'     };     var vm = new Vue({       el: '#app',       data: {                },       components: {         'hello-world': HelloWorld,         'hello-tom': HelloTom,         'hello-jerry': HelloJerry       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

为什么报错呢?因为我们在全局组件中使用了局部组件,它是访问不到的。局部组件只能在当前注册它的vue实例中使用。

Vue基础(七):常用特性(三)

1.2 Vue组件之间的传值 

1.2.1 父组件向子组件传值

父组件发送的形式是以属性的形式绑定值到子组件身上。
然后子组件用属性props接收

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <div>{{pmsg}}</div>     <!--1、menu-item 在 APP中嵌套着 故 menu-item 为 子组件 -->         <!-- 给子组件传入一个静态的值 -->     <menu-item title='来自父组件的值'></menu-item>     <!-- 2、 需要动态的数据的时候 需要属性绑定的形式设置 此时 ptitle 来自父组件data 中的数据 .         传的值可以是数字、对象、数组等等         -->     <menu-item :title='ptitle' content='hello'></menu-item>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       父组件向子组件传值     */     Vue.component('menu-item', {       props: ['title', 'content'],       data: function() {         return {           msg: '子组件本身的数据'         }       },       template: '<div>{{msg + "----" + title + "-----" + content}}</div>'     });     var vm = new Vue({       el: '#app',       data: {         pmsg: '父组件中内容',         ptitle: '动态绑定属性'       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

然后就是注意命名,在props中使用驼峰形式,模板中需要使用短横线的形式字符串形式的模板中没有这个限制。 

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <div>{{pmsg}}</div>     <menu-item :menu-title='ptitle'></menu-item>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       父组件向子组件传值-props属性名规则     */     Vue.component('third-com', {       props: ['testTile'],       template: '<div>{{testTile}}</div>'     });     Vue.component('menu-item', {       props: ['menuTitle'],       template: '<div>{{menuTitle}}<third-com testTile="hello"></third-com></div>'     });     var vm = new Vue({       el: '#app',       data: {         pmsg: '父组件中内容',         ptitle: '动态绑定属性'       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

最后再了解一下父组件向子组件传值时,props属性值的类型。

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <div>{{pmsg}}</div>     <menu-item :pstr='pstr' :pnum='12' pboo='true' :parr='parr' :pobj='pobj'></menu-item>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       父组件向子组件传值-props属性值类型     */     Vue.component('menu-item', {       props: ['pstr','pnum','pboo','parr','pobj'],       template: `         <div>           <div>{{pstr}}</div>           <div>{{12 + pnum}}</div>           <div>{{typeof pboo}}</div>           <ul>             <li :key='index' v-for='(item,index) in parr'>{{item}}</li>           </ul>             <span>{{pobj.name}}</span>             <span>{{pobj.age}}</span>           </div>         </div>       `     });     var vm = new Vue({       el: '#app',       data: {         pmsg: '父组件中内容',         pstr: 'hello',         parr: ['apple','orange','banana'],         pobj: {           name: 'lisi',           age: 12         }       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

1.2.2 子组件向父组件传值 

子组件用 $emit()触发事件
$emit()第一个参数为自定义的事件名称,第二个参数为需要传递的数据
父组件用v-on监听子组件的事件

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>     <menu-item :parr='parr' @enlarge-text='handle'></menu-item>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       子组件向父组件传值-基本用法       props传递数据原则:单向数据流     */     Vue.component('menu-item', {       props: ['parr'],       template: `         <div>           <ul>             <li :key='index' v-for='(item,index) in parr'>{{item}}</li>           </ul>           <button @click='$emit("enlarge-text")'>扩大父组件中字体大小</button>         </div>       `     });     var vm = new Vue({       el: '#app',       data: {         pmsg: '父组件中内容',         parr: ['apple','orange','banana'],         fontSize: 10       },       methods: {         handle: function(){           // 扩大字体大小           this.fontSize += 5;         }       }     });   </script> </body> </html>

执行结果:

Vue基础(七):常用特性(三)

 

点击按钮:

Vue基础(七):常用特性(三)

然后再来一个携带参数的案例:

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>     <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       子组件向父组件传值-携带参数     */          Vue.component('menu-item', {       props: ['parr'],       template: `         <div>           <ul>             <li :key='index' v-for='(item,index) in parr'>{{item}}</li>           </ul>           <button @click='$emit("enlarge-text", 5)'>扩大父组件中字体大小</button>           <button @click='$emit("enlarge-text", 10)'>扩大父组件中字体大小</button>         </div>       `     });     var vm = new Vue({       el: '#app',       data: {         pmsg: '父组件中内容',         parr: ['apple','orange','banana'],         fontSize: 10       },       methods: {         handle: function(val){           // 扩大字体大小           this.fontSize += val;         }       }     });   </script> </body> </html>

执行结果:

Vue基础(七):常用特性(三)

左边按钮一次加5px,右边按钮一次加10px。 

1.2.3 兄弟组件之间数据交互

兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据

  提供事件中心 var hub = new Vue()

传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)

接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名

销毁事件,通过hub.$off()方法名销毁之后无法进行传递数据

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <div>父组件</div>     <div>       <button @click='handle'>销毁事件</button>     </div>     <test-tom></test-tom>     <test-jerry></test-jerry>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       兄弟组件之间数据传递     */     // 提供事件中心     var hub = new Vue();      Vue.component('test-tom', {       data: function(){         return {           num: 0         }       },       template: `         <div>           <div>TOM:{{num}}</div>           <div>             <button @click='handle'>点击</button>           </div>         </div>       `,       methods: {         handle: function(){             //2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)           hub.$emit('jerry-event', 2);         }       },       mounted: function() {         // 监听事件         // 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名         hub.$on('tom-event', (val) => {           this.num += val;         });       }     });     Vue.component('test-jerry', {       data: function(){         return {           num: 0         }       },       template: `         <div>           <div>JERRY:{{num}}</div>           <div>             <button @click='handle'>点击</button>           </div>         </div>       `,       methods: {         handle: function(){           // 触发兄弟组件的事件           //2、传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据)            hub.$emit('tom-event', 1);         }       },       mounted: function() {         // 监听事件         // 3、接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名         hub.$on('jerry-event', (val) => {           this.num += val;         });       }     });     var vm = new Vue({       el: '#app',       data: {                },       methods: {         handle: function(){             //4、销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据           hub.$off('tom-event');           hub.$off('jerry-event');         }       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

 

 

点击TOM按钮,JERRY增加2,点击JERRY按钮,TOM增加1,点击销毁事件后,点击按钮无反应。 

1.3 组件插槽 

组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力。

1.3.1 匿名插槽

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">       <!-- 这里的所有组件标签中嵌套的内容会替换掉slot 如果不传值 则使用 slot 中的默认值 -->     <alert-box>有bug发生</alert-box>     <alert-box>有一个警告</alert-box>     <alert-box></alert-box>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       组件插槽:父组件向子组件传递内容     */     Vue.component('alert-box', {         // 当组件渲染的时候,这个 <slot> 元素将会被替换为“组件标签中嵌套的内容”。       // 插槽内可以包含任何模板代码,包括 HTML       template: `         <div>           <strong>ERROR:</strong>           <slot>默认内容</slot>         </div>       `     });     var vm = new Vue({       el: '#app',       data: {                }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

1.3.2 具名插槽

具有名字的插槽

使用slot中的 "name" 属性绑定元素  

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <body>   <div id="app">     <base-layout>         <!-- 2、 通过slot属性来指定, 这个slot的值必须和下面slot组件得name值对应上如果没有匹配到 则放到匿名的插槽中 -->       <p slot='header'>标题信息</p>       <p>主要内容1</p>       <p>主要内容2</p>       <p slot='footer'>底部信息信息</p>     </base-layout>      <base-layout>         <!-- 注意点:template临时的包裹标签最终不会渲染到页面上 -->       <template slot='header'>         <p>标题信息1</p>         <p>标题信息2</p>       </template>       <p>主要内容1</p>       <p>主要内容2</p>       <template slot='footer'>         <p>底部信息信息1</p>         <p>底部信息信息2</p>       </template>     </base-layout>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       具名插槽     */     Vue.component('base-layout', { //        1、 使用 <slot> 中的 "name" 属性绑定元素 指定当前插槽的名字 //        注意点: //         具名插槽的渲染顺序,完全取决于模板,而不是取决于父组件中元素的顺序       template: `         <div>           <header>             <slot name='header'></slot>           </header>           <main>             <slot></slot>           </main>           <footer>             <slot name='footer'></slot>           </footer>         </div>       `     });     var vm = new Vue({       el: '#app',       data: {                }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

为什么飘红了,因为Vue是不允许有重名的slot,如果你没有代码洁癖的话,完全不用理会。

1.3.3 作用域插槽

父组件对子组件加工处理

既可以复用子组件的slot,又可以使slot内容不一致 

<!DOCTYPE html> <html> <head>   <meta charset="UTF-8">   <title>Vue的基本使用</title> </head> <style type="text/css">   .current {     color: orange;   } </style> <body>   <div id="app">       <!--         1、当我们希望li 的样式由外部使用组件的地方定义,因为可能有多种地方要使用该组件,         但样式希望不一样 这个时候我们需要使用作用域插槽         -->     <fruit-list :list='list'>         <!-- 2、 父组件中使用了<template>元素,而且包含scope="slotProps",             slotProps在这里只是临时变量             --->        <template slot-scope='slotProps'>         <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>         <span v-else>{{slotProps.info.name}}</span>       </template>     </fruit-list>   </div>   <script type="text/javascript" src="js/vue.js"></script>   <script type="text/javascript">     /*       作用域插槽     */     Vue.component('fruit-list', {       props: ['list'], //        3、 在子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法msg="xxx", //            插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供了内容,会显示默认的内容。 //            如果父组件为这个插槽提供了内容,则默认的内容会被替换掉       template: `         <div>                    <li :key='item.id' v-for='item in list'>              <slot :info='item'>{{item.name}}</slot>           </li>         </div>       `     });     var vm = new Vue({       el: '#app',       data: {         list: [{           id: 1,           name: 'apple'         },{           id: 2,           name: 'orange'         },{           id: 3,           name: 'banana'         }]       }     });   </script> </body> </html>

执行效果:

Vue基础(七):常用特性(三)

 

 

第三个数据高亮显示了。