组件化开发思想:
组件化规范: Web Components 规范草案
1.我们希望尽可能多的重用代码
2.自定义组件的方式不太容易(html、css、js)
3.多次使用组件可能导师冲突
Web Components 通过创建封装好的功能定制元素解决上述问题。(目前并没有广泛的被浏览器所支持。)
官网: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
不同的功能封装到不同的组件当中,不同的组件通过组合完成特定的需求。
Vue实现了部分上述规范。
组件注册:
全局组件注册语法:
Vue.component(组件名称, {
data: 组件数据,
template: 组件模版内容
})
用法:
<div id='app'>
<btn></btn>
</div>
注意事项:
1.data 必须是一个函数。
2.组件模版,必须为单个根元素。
3.组件内容可以是模版字符串。(模版字符串需要浏览提供支持,ES6语法)。
4.组件的命名方式:如果使用驼峰命名,那么在使用组件的时候,只能在字符串模版中使用驼峰的方式使用组件,但是在普通的标签模版中,必须转换成带短横线的标签。
案例:
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>Document</title>
9 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
10 </head>
11
12 <body>
13
14 <div id='app'>
15 <btn></btn>
16 </div>
17
18 </body>
19
20 <script>
21 // 组件注册
22 // 定义一个名为btn的组件
23 Vue.component('btn', {
24 data: function () {
25 return {
26 count: 0
27 }
28 },
29 template: '<button v-on:click="count++">点击了{{count}}次</button>'
30 })
31
32 var vm = new Vue({
33 el: '#app',
34 data: {
35
36 }
37 })
38 </script>
39
40 </html>
局部组件注册:
局部组件只能在注册他的父组件中使用
案例:
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>Document</title>
9 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
10 </head>
11
12 <body>
13
14 <div id='app'>
15 <component-a></component-a>
16 <component-b></component-b>
17 <component-c></component-c>
18 </div>
19
20 </body>
21
22 <script>
23 // 局部组件注册
24 var ComponentA = {
25 data: function () {
26 return {
27 msg: 'hello A'
28 }
29 },
30 template: '<button>{{msg}}</button>'
31 }
32 var ComponentB = {
33 data: function () {
34 return {
35 msg: 'hello B'
36 }
37 },
38 template: '<button>{{msg}}</button>'
39 }
40 var ComponentC = {
41 data: function () {
42 return {
43 msg: 'hello C'
44 }
45 },
46 template: '<button>{{msg}}</button>'
47 }
48
49 var vm = new Vue({
50 el: '#app',
51 data: {
52
53 },
54 components: {
55 'component-a': ComponentA,
56 'component-b': ComponentB,
57 'component-c': ComponentC
58 }
59 })
60 </script>
61
62 </html>
组件之间数据交互
1.组件内部通过props接受传递过来的值。
父组件向子组件传值:
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>Document</title>
9 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
10 </head>
11
12 <body>
13 <div id='app'>
14 <div>{{pmsg}}</div>
15 <menu-item title='(parent-component)'></menu-item>
16 <menu-item :title='pmsg'></menu-item>
17 </div>
18 </body>
19 <script>
20 Vue.component('menu-item',{
21 data: function () {
22 return {
23 msg:'(children-component)'
24 }
25 },
26 props:['title'],
27 template:'<div>{{msg + title}}</div>'
28 })
29
30
31 var vm = new Vue({
32 el: '#app',
33 data: {
34 pmsg:'hello word'
35 }
36 })
37 </script>
38
39 </html>
props属性名规则:
1.在props中使用驼峰形式,模版中需要使用短横线的形式。
2.字符串形式的模版中没有这个限制。
props属性值类型:
1.字符串类型 string
2.数值类型 number
3.布尔值 boolean
4.数组 array
5.对象 object
子组件向父组件传值:
props 传递数据原则是单向数据流。
1.子组件通过自定义事件向父组件传递信息。
2.父组件监听子组件的事件
<menu-item v-on:hello ='count+1'></menu-item>
案例:子组件向父组件传值,携带参数
<button v-on:click='$emit("hello","abc123")'>button</button>
<menu-item v-on:hello ='$event'></menu-item>
组件之间数据交互:
1.单独的事件中心管理组件间的通讯。
new 一个新的Vue实例对象作为事件中心
var eventHub = new Vue()
2.监听事件与销毁事件
$on 事件监听,第一参数是自定义事件的名称,第二个参数是事件函数。
eventHub.$on('add-todo',addTodo)
$off 事件销毁
eventHub.$off('add-todo')
3.触发事件
$emit 第一个参数为自定义事件名称,第二个参数为事件函数
eventHub.$emit('add-todo', id)
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>Document</title>
9 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
10 </head>
11
12 <body>
13
14 <div id='app'>
15 <test-tom></test-tom>
16 <test-jerry></test-jerry>
17 </div>
18
19 </body>
20
21 <script>
22 // 事件中心
23 var hub = new Vue()
24
25 Vue.component('test-tom', {
26 data: function () {
27 return {
28 num: 0
29 }
30 },
31 template: `
32 <div>
33 <div>Tom:{{num}}</div>
34 <div>
35 <button @click='handle'>点击</button>
36 </div>
37 </div>
38 `,
39 methods: {
40 handle: function () {
41 // 触发兄弟组件的事件
42 hub.$emit('jerry-event', 2)
43 }
44 }
45 })
46
47 Vue.component('test-jerry', {
48 data: function () {
49 return {
50 num: 5
51 }
52 },
53 template: `
54 <div>
55 <div>Jerry:{{num}}</div>
56 <div>
57 <button @click='handle'>点击</button>
58 </div>
59 </div>
60 `,
61 methods: {
62 handle: function () {
63 // 触发兄弟组件的事件
64 hub.$emit('tom-event', 5)
65 }
66 }
67 })
68
69 var vm = new Vue({
70 el: '#app',
71 mounted: function () {
72 hub.$on('tom-event', value => {
73 this.num += value
74 })
75
76 hub.$on('jerry-event', value => {
77 this.num += value
78 })
79 }
80 })
81 </script>
82
83 </html>
组件插槽
组件插槽的作用:
父组件向子组件传递内容(类似于React 中的 this.props.children)
1.插槽位置
1 Vue.component('alert-box',{
2 template:`
3 <div>
4 hello
5 <slot></slot>
6 </div>
7 `
8 })
2.插槽内容
1 <alert-box>hello</alert-box>
案例:
1 <!DOCTYPE html>
2 <html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>Document</title>
9 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
10 </head>
11
12 <body>
13 <div id='app'>
14 <alert-box>
15 <alert-item></alert-item>
16 </alert-box>
17 </div>
18 </body>
19 <script>
20 // 插槽位置
21 Vue.component('alert-box', {
22 template: `
23 <div>
24 <div>box</div>
25 <slot></slot>
26 <div>box</div>
27 </div>
28 `
29 })
30
31 Vue.component('alert-item', {
32 template: `
33 <div>
34 <div>item</div>
35 </div>
36 `
37 })
38
39 var vm = new Vue({
40 el: '#app',
41 data: {
42
43 }
44 })
45 </script>
46
47 </html>
具名插槽:自 2.6.0 起被废弃 不推荐使用
1.插槽定义
1 <div> 2 <header> 3 <slot name='header'></slot> 4 </header> 5 <main> 6 <slot></slot> 7 </main> 8 <footer> 9 <slot name='footer'></slot> 10 </footer> 11 </div>
2.插槽内容
1 <div> 2 <h1 slot='header'>标题</h1> 3 4 <p>内容</p> 5 <p>内容</p> 6 <p>内容</p> 7 8 <p slot='footer'>底部</p> 9 </div>