一:组件的基本用法
组件和创建Vue实例类似,需要先注册后才能使用,Vue组件注册方式分为全局注册和局部注册,全局注册的组件在任何使用Vue的地方均可使用,局部注册的组件只能在实例作用于范围内使用。
全局注册:
Vue.component('my-component', { template : '<div>这是组件中的内容</div>' });
或者使用局部注册:
var myTemplateContent = { template : '<div>这是组件中的内容</div>' }; new Vue({ el : '#app', components : { 'my-component' : this.myTemplateContent } });
使用组件:
<div id="app"> <my-component></my-component> </div>
<div id="app"> <table> <tbody is='my-component'></tbody> </table> </div>
<body> <div id="app"> <my-component></my-component> </div> <script> var myTemplateContent = { template : '<div>{{message}}</div>', data : function(){ return {message : '这是组件中的内容'} } }; new Vue({ el : '#app', components : { 'my-component' : this.myTemplateContent } }) </script> </body>
三:使用props在组件之间传递数据
组件可以进行层级嵌套,父组件的data是不能直接被子组件访问的,需要通过props参数来传递数据,传递的值可以是一个字符串数组或者对象。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <my-component message="来自父组件的数据"></my-component> 11 </div> 12 13 <script> 14 15 var myTemplateContent = { 16 props : ['message'], 17 template : '<div>{{message}}</div>' 18 }; 19 20 new Vue({ 21 el : '#app', 22 components : { 23 'my-component' : this.myTemplateContent 24 } 25 }) 26 </script> 27 </body> 28 </html>
在上述示例中,<my-component message="来自父组件的数据"></my-component>的message属性值可以是v-bind动态绑定的数据,当绑定的数据更新时,模板内容也会动态更新:
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>Test page</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input type="text" v-model='inputValue'> <my-component :message='inputValue'></my-component> </div> <script> var myTemplateContent = { props : ['message'], template : '<div>{{message}}</div>' }; new Vue({ el : '#app', components : { 'my-component' : this.myTemplateContent }, data : { inputValue : '' } }) </script> </body> </html>
上述代码示例中,当在input输入框中输入值时,会更新data中的inputValue,因此my-component组件的message属性值也会动态更新,组件的内容也随之动态更新。
组件之间除了可以进行数据通信,还可以进行组件之间的事件调用,从而完成消息发送和接收。
四:使用自定义事件从子组件向父组件传递数据
类似观察者模式,在Vue中,子组件使用$emit()来触发事件,父组件使用$on()来监听子组件的事件。
在下面的例子中,子组件my-component有两个按钮,handleIncrease和handleReduce分别用于增加和减少模板的data组件的counter值,然后使用$emit()方法通知父组件的increase和reduce方法。$emit()方法的第一个参数是自定义事件的名称,后续参数是要传递的数据。父组件使用handleGetTotal方法将接收到的参数赋值给自身的total值上。
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>Test page</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>总数:{{total}}</p> <my-component @increase="handleGetTotal" @reduce="handleGetTotal"></my-component> </div> <script> var myTemplateContent = { template : ' <div> <button @click="handleIncrease">+1</button> <button @click="handleReduce">-1</button> </div>', data : function (){ return {counter : 0} }, methods : { handleIncrease : function(){ this.counter++; this.$emit('increase', this.counter); }, handleReduce : function(){ this.counter--; this.$emit('reduce', this.counter); } } }; new Vue({ el : '#app', components : { 'my-component' : this.myTemplateContent }, data : {total : 0}, methods : { handleGetTotal : function(total){ this.total = total; } } }) </script> </body> </html>
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <p>{{message}}</p> 11 <my-component ></my-component> 12 </div> 13 14 <script> 15 //空的Vue对象作为"中介" 16 var bus = new Vue(); 17 18 var myTemplateContent = { 19 template : '<button @click="handleEvent">传递事件</button>', 20 methods : { 21 handleEvent : function(){ 22 bus.$emit('on-message', '来自my-component组件中的内容') 23 } 24 } 25 }; 26 27 new Vue({ 28 el : '#app', 29 components : { 30 'my-component' : this.myTemplateContent 31 }, 32 data : {message : ''}, 33 mounted : function(){ 34 var that = this; 35 //Vue实例初始化时,监听来自bus的事件 36 bus.$on('on-message', function(msg){ 37 that.message = msg; 38 }) 39 } 40 }) 41 </script> 42 </body> 43 </html>
六:组件之间的通信——父链和子组件索引
在子组件中,可以使用this.$parent来直接访问组件的父组件,父组件也可以用this.$children来访问它的所有子组件,且可以向上/向下无线递归访问,直到实例根元素或者最内层元素。实例代码:
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset=utf-8>
5 <title>Test page</title>
6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
7 </head>
8 <body>
9 <div id="app">
10 <p>{{message}}</p>
11 <my-component ></my-component>
12 </div>
13
14 <script>
15 //空的Vue对象作为"中介"
16 var bus = new Vue();
17
18 var myTemplateContent = {
19 template : '<button @click="handleEvent">通过父链直接修改数据</button>',
20 methods : {
21 handleEvent : function(){
22 this.$parent.message = '来自组件my-component的内容';
23 }
24 }
25 };
26
27 new Vue({
28 el : '#app',
29 components : {
30 'my-component' : this.myTemplateContent
31 },
32 data : {message : ''}
33 })
34 </script>
35 </body>
36 </html>
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset=utf-8>
5 <title>Test page</title>
6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
7 </head>
8 <body>
9 <div id="app">
10 <button @click="handleRef">通过ref获取子组件实例</button>
11 <my-component ref="comMy"></my-component>
12 </div>
13
14 <script>
15 var myTemplateContent = {
16 template : '<div>子组件</div>',
17 data : function(){
18 return {
19 message : '子组件内容'
20 }
21 }
22 };
23
24 new Vue({
25 el : '#app',
26 components : {
27 'my-component' : this.myTemplateContent
28 },
29 methods : {
30 handleRef : function(){
31 //通过refs访问指定的子组件实例
32 var msg = this.$refs.comMy.message;
33 console.log(msg)
34 }
35 }
36 })
37 </script>
38 </body>
39 </html>
$refs只在组件渲染完成后填充,并不是响应式的,应避免在模板和计算属性中使用$refs。
七:使用slot插槽分发内容1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <!--这里的showChild绑定的是父组件的数据--> 11 <my-component v-show="showChild"></my-component> 12 </div> 13 <script> 14 var myTemplateContent = { 15 template : '<div>子组件</div>' 16 }; 17 new Vue({ 18 el : '#app', 19 components : { 20 'my-component' : this.myTemplateContent 21 }, 22 data : { 23 showChild : true 24 } 25 }) 26 </script> 27 28 <div id="app2"> 29 <my-component ></my-component> 30 </div> 31 <script> 32 var myTemplateContent = { 33 //这里的showChild绑定的是子组件的数据 34 template : '<div v-show="showChild">子组件</div>', 35 data : function(){ 36 return { 37 showChild : true 38 } 39 } 40 }; 41 new Vue({ 42 el : '#app2', 43 components : { 44 'my-component' : this.myTemplateContent 45 } 46 }) 47 </script> 48 </body> 49 </html>
(一)单个Slot插槽的用法
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <child-component> 11 <p>分发的内容</p> 12 <p>更多分发的内容</p> 13 </child-component> 14 </div> 15 <script> 16 var myTemplateContent = { 17 template : ' 18 <div> 19 <slot> 20 <p>如果父组件没有插入内容,此行文字将作为默认内容</p> 21 </slot> 22 </div>' 23 }; 24 new Vue({ 25 el : '#app', 26 components : { 27 'child-component' : this.myTemplateContent 28 } 29 }) 30 </script> 31 </body> 32 </html>
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <child-component> 11 <h2 slot="header">标题</h2> 12 <p>分发的内容</p> 13 <p>更多分发的内容</p> 14 <div slot="footer">底部信息</div> 15 </child-component> 16 </div> 17 <script> 18 var myTemplateContent = { 19 template : ' 20 <div class="container"> 21 <div class="header"> 22 <slot name="header"></slot> 23 </div> 24 <div class="main"> 25 <slot></slot> 26 </div> 27 <div class="footer"> 28 <slot name="footer"></slot> 29 </div> 30 </div>' 31 }; 32 new Vue({ 33 el : '#app', 34 components : { 35 'child-component' : this.myTemplateContent 36 } 37 }) 38 </script> 39 </body> 40 </html>
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <child-component> 11 <template scope="props"> 12 <p>来自父组件的内容</p> 13 <p>{{props.msg}}</p> 14 </template> 15 </child-component> 16 </div> 17 <script> 18 var myTemplateContent = { 19 template : ' 20 <div class="container"> 21 <slot msg="来自子组件的内容"></slot> 22 </div>' 23 }; 24 new Vue({ 25 el : '#app', 26 components : { 27 'child-component' : this.myTemplateContent 28 } 29 }) 30 </script> 31 </body> 32 </html>
子组件的模板中,<slot msg="来自子组件的内容"></slot>向插槽传递了一个msg,父组件使用了template元素,且有一个scope="props"的属性,然后在template内部就可以使用props.msg来访问子组件传递过来的数据了。(注意到这里的scope="props"中的props只是一个临时变量,可以为任意名字)
(四)访问slot1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset=utf-8> 5 <title>Test page</title> 6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 7 </head> 8 <body> 9 <div id="app"> 10 <child-component> 11 <h2 slot="header">标题</h2> 12 <p>分发的内容</p> 13 <p>更多分发的内容</p> 14 <div slot="footer">底部信息</div> 15 </child-component> 16 </div> 17 <script> 18 var myTemplateContent = { 19 template : ' 20 <div class="container"> 21 <div class="header"> 22 <slot name="header"></slot> 23 </div> 24 <div class="main"> 25 <slot></slot> 26 </div> 27 <div class="footer"> 28 <slot name="footer"></slot> 29 </div> 30 </div>', 31 mounted : function(){ 32 var header = this.$slots.header; 33 var main = this.$slots.default; 34 var footer = this.$slots.footer; 35 console.log({ 36 header, 37 main, 38 footer 39 }) 40 } 41 }; 42 new Vue({ 43 el : '#app', 44 components : { 45 'child-component' : this.myTemplateContent 46 } 47 }) 48 </script> 49 </body> 50 </html>