1、Vue组件化开发思想。
引述:组件化规范Web Components。
1)、我们希望尽可能多的重用代码。 2)、自定义组件的方式不太容易(html、css、js)。 3)、多次使用组件可能导致冲突。 4)、Web Components通过创建封装好功能的定制元素解决上述问题。 5)Vue部分实现了上述Web Components规范。
2、Vue组件注册。Vue组件注册注意事项。
1)、data必须是一个函数。分析函数与普通对象的对比,Vue的data是一个对象,区别于组件的data是一个函数。组件的data是函数,可以形成一个闭包的环境,这可以保证每一个组件都可以拥有一份独立的数据。 2)、组件模板内容必须是单个根元素,分析演示实际的效果,比如多个div包了多个button标签。类比Vue实例的el容器中。 3)、组件模板内容可以是模板字符串。模板字符串需要浏览器提供支持(ES6语法规则)。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <div v-text="msg"></div>
10
11 <!-- Vue组件注册成功之后,就可以使用了 -->
12 <!-- 这个组件是一个子组件,因为Vue也是一个组件,子组件写到Vue父组件当中,形成父子关系。 -->
13 <button-counter></button-counter>
14
15 <!-- 组件的重用,可以拷贝多份,每个组件相互独立,不互相影响的。 -->
16 <button-counter></button-counter>
17 <button-counter></button-counter>
18 </div>
19
20 <script src="vue.js" type="text/javascript"></script>
21 <script type="text/javascript">
22 // Vue的组件化开发,Vue的注册。参数一是组件的名称,参数二是组件的内容,是一个对象。
23 // Vue.component('', {
24 // data: '', //data表示组件数据。
25 // template: '', // template表示组件模板内容。可以做数据的绑定、分支、事件的操作、循环的结构都可以在这里面使用。
26 // });
27 // Vue的组件化开发,Vue的注册,下面的语法就将组件注册成功了。
28 // Vue.component('button-counter', {
29 // data: function() {
30 // // 提供一个具体的对象,对象当中存放的具体的数据
31 // return {
32 // count: 0,
33 // }
34 // },
35 // // template: '<button @click="count++">点击了{{count}}次</button>',
36 // // 组件模板内容必须是单个根元素,分析演示实际的效果
37 // template: '<div><button @click="counts">点击了{{count}}次</button><button @click="counts">点击了{{count}}次</button></div>',
38 // methods: {
39 // counts: function() {
40 // this.count++;
41 // }
42 // }
43 // })
44 //---------------------------------------------------------------------------------------
45 Vue.component('button-counter', {
46 data: function() {
47 // 提供一个具体的对象,对象当中存放的具体的数据
48 return {
49 count: 0,
50 }
51 },
52 // template: '<button @click="count++">点击了{{count}}次</button>',
53 // 组件模板内容可以是模板字符串。模板字符串需要浏览器提供支持(ES6语法规则)
54 template: `
55 <div>
56 <button @click="counts">点击了{{count}}次</button>
57 <button @click="counts">点击了{{count}}次</button>
58 </div>
59 `,
60 methods: {
61 counts: function() {
62 this.count++;
63 }
64 }
65 })
66
67 // 创建Vue对象
68 var vm = new Vue({
69 el: '#app',
70 data: { // 对象,区别于组件的data是一个函数。
71 msg: 'hello world!',
72 },
73 methods: {}
74 });
75 </script>
76 </body>
77 </html>
3、Vue组件注册,组件名称方法。
1)、短横线方式。Vue.component('button-counter', {/** ... */}); 2)、驼峰方式,驼峰方式要用到根组件里面,需要换成短横线方式。如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是在普通的标签模板中,必须使用短横线的方式使用组件。Vue.component('buttonCounter', {/** ... */});
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <Hello-world></Hello-world>
10 <div v-text="msg"></div>
11
12 <!-- Vue组件注册成功之后,就可以使用了 -->
13 <!-- 这个组件是一个子组件,因为Vue也是一个组件,子组件写到Vue父组件当中,形成父子关系。 -->
14 <button-counter></button-counter>
15
16 <!-- 组件的重用,可以拷贝多份,每个组件相互独立,不互相影响的。 -->
17 <button-counter></button-counter>
18 <button-counter></button-counter>
19 </div>
20
21 <script src="vue.js" type="text/javascript"></script>
22 <script type="text/javascript">
23 // Vue的组件化开发,Vue的注册。参数一是组件的名称,参数二是组件的内容,是一个对象。
24 // Vue.component('', {
25 // data: '', //data表示组件数据。
26 // template: '', // template表示组件模板内容。可以做数据的绑定、分支、事件的操作、循环的结构都可以在这里面使用。
27 // });
28 // Vue的组件化开发,Vue的注册,下面的语法就将组件注册成功了。
29 // Vue.component('button-counter', {
30 // data: function() {
31 // // 提供一个具体的对象,对象当中存放的具体的数据
32 // return {
33 // count: 0,
34 // }
35 // },
36 // // template: '<button @click="count++">点击了{{count}}次</button>',
37 // // 组件模板内容必须是单个根元素,分析演示实际的效果
38 // template: '<div><button @click="counts">点击了{{count}}次</button><button @click="counts">点击了{{count}}次</button></div>',
39 // methods: {
40 // counts: function() {
41 // this.count++;
42 // }
43 // }
44 // })
45
46 Vue.component('HelloWorld', {
47 data: function() {
48 return {
49 msg: '您好,Vue!',
50 }
51 },
52 template: '<div>{{msg}}</div>',
53 });
54
55 //---------------------------------------------------------------------------------------
56 Vue.component('buttonCounter', {
57 data: function() {
58 // 提供一个具体的对象,对象当中存放的具体的数据
59 return {
60 count: 0,
61 }
62 },
63 // template: '<button @click="count++">点击了{{count}}次</button>',
64 // 组件模板内容可以是模板字符串。模板字符串需要浏览器提供支持(ES6语法规则)
65 template: `
66 <div>
67 <button @click="counts">点击了{{count}}次</button>
68 <button @click="counts">点击了{{count}}次</button>
69 <HelloWorld></HelloWorld>
70 </div>
71 `,
72 methods: {
73 counts: function() {
74 this.count++;
75 }
76 }
77 })
78
79 // 创建Vue对象
80 var vm = new Vue({
81 el: '#app',
82 data: { // 对象,区别于组件的data是一个函数。
83 msg: 'hello world!',
84 },
85 methods: {}
86 });
87 </script>
88 </body>
89 </html>
4、Vue组件注册,局部组件注册。
1)、局部组件,只能在注册他的父组件当中使用,在别的组件中是不可以使用的。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <!-- 使用局部组件 -->
10 <component-a></component-a>
11 <component-b></component-b>
12 <component-c></component-c>
13 </div>
14
15 <script src="vue.js" type="text/javascript"></script>
16 <script type="text/javascript">
17 /* 通过这种方式注册的组件只能在父组件中使用,在别的组件中是使用不了的。 */
18 var componentA = {
19 data: function() {
20 return {
21 msg: 'hello world componentA.',
22 }
23 },
24 template: '<div>{{msg}}</div>',
25 };
26 var componentB = {
27 data: function() {
28 return {
29 msg: 'hello world componentB.',
30 }
31 },
32 template: '<div>{{msg}}</div>',
33 };
34 var componentC = {
35 data: function() {
36 return {
37 msg: 'hello world componentC.',
38 }
39 },
40 template: '<div>{{msg}}</div>',
41 };
42
43 // 创建Vue对象
44 var vm = new Vue({
45 el: '#app',
46 data: { // 对象,区别于组件的data是一个函数。
47
48 },
49 methods: {
50
51 },
52 // Vue的局部组件注册。
53 components: {
54 // 将局部组件通过components注册进来
55 'componentA': componentA,
56 'componentB': componentB,
57 'componentC': componentC
58 }
59 });
60 </script>
61 </body>
62 </html>
5、Vue组件注册,Vue调式工具vue-devtools用法。
1)、克隆地址,https://github.com/vuejs/vue-devtools.git 2)、安装依赖包npm install。 3)、构建npm run build。生成一个Chrome扩展的包。 4)、打开Chrome扩展页面。加载构建的包。 5)、选中开发者模式。 6)、加载已解压的扩展,选择shells/chrome。
6、Vue组件,组件之间的关系,包含父子关系(祖孙关系)和兄弟关系。组件间数据交互。
1)、父组件向子组件传值。
a、组件内部通过props接收传递过来的值,它的值是一个数组,数组中可以包含很多的属性,这些属性都是从父组件传输过来的。 b、父组件通过属性将值传递给子组件。通过静态传递和动态绑定传递属性。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <!-- 父组件打印 -->
10 <div>{{pmsg}}</div>
11 <!-- 子组件的使用,父组件以属性的方式将值传递给子组件 -->
12 <menu-item title="我是来自父组件的标题" concent="我是父组件的内容!"></menu-item>
13 <!-- 父组件给子组件动态传值 -->
14 <menu-item v-bind:title="title" concent="我是父组件的内容!"></menu-item>
15 <menu-item :title="title" concent="我是父组件的内容!"></menu-item>
16 </div>
17
18 <script src="vue.js" type="text/javascript"></script>
19 <script type="text/javascript">
20 // 创建一个组件,父组件向子组件传值。
21 Vue.component('menu-item', {
22 props: ['title',
23 'concent'
24 ], // 子组件接收父组件传递的值
25 data: function() {
26 return {
27 msg: '子组件本身的数据'
28 }
29 },
30 template: '<div>{{msg + " : " + title + " " + concent}}</div>',
31 });
32
33 // 创建Vue对象
34 var vm = new Vue({
35 el: '#app',
36 data: { // 对象,区别于组件的data是一个函数。
37 pmsg: '父组件中的内容',
38 title: '我是来自父组件的标题!',
39 },
40 methods: {
41
42 },
43
44 });
45 </script>
46 </body>
47 </html>
2)、props属性名规则。
a、在props中使用驼峰形式,在html模板中需要使用短横线的形式。因为dom元素的属性不区分大小的,如果传递驼峰形式就出现问题了。 b、字符串形式的模板中没有这个限制。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <!-- 父组件打印 -->
10 <div>{{pmsg}}</div>
11 <!-- 子组件的使用,父组件以属性的方式将值传递给子组件 -->
12 <menu-item menu-title="我是来自父组件的标题" concent="我是父组件的内容!"></menu-item>
13
14 <!-- 在html模板中需要使用短横线的形式 -->
15 <!-- 父组件给子组件动态传值,title值是动态传值,是父组件里面定义的变量 -->
16 <menu-item v-bind:menu-title="title" concent="我是父组件的内容!"></menu-item>
17 <menu-item :menu-title="title" concent="我是父组件的内容!"></menu-item>
18 </div>
19
20 <script src="vue.js" type="text/javascript"></script>
21 <script type="text/javascript">
22 // 创建一个组件,父组件向子组件传值。
23 Vue.component('third-component', {
24 // 子组件接收父组件传递的值
25 props: ['thirdTitle'],
26 // 子组件接收父组件传递的值menuTitle、concent就可以使用了。
27 // 这里面指定的就是字符串形式的模板。这里面可以使用驼峰形式的数据是没有问题的。
28 template: '<div>{{thirdTitle}}</div>',
29 });
30 Vue.component('menu-item', {
31 // 子组件接收父组件传递的值
32 props: ['menuTitle',
33 'concent'
34 ],
35 data: function() {
36 return {
37 msg: '子组件本身的数据'
38 }
39 },
40 // 子组件接收父组件传递的值menuTitle、concent就可以使用了。
41 // 这里面指定的就是字符串形式的模板。这里面可以使用驼峰形式的数据是没有问题的。
42 template: '<div>{{msg + " : " + menuTitle + " " + concent}}<third-component thirdTitle="hello"></third-component></div>',
43 });
44
45 // 创建Vue对象
46 var vm = new Vue({
47 el: '#app',
48 data: { // 对象,区别于组件的data是一个函数。
49 pmsg: '父组件中的内容',
50 title: '我是来自父组件的标题!',
51 },
52 methods: {
53
54 },
55
56 });
57 </script>
58 </body>
59 </html>
3)、props属性值规则。
a、字符串String。 b、数值Number。 c、布尔值Boolean。 d、数组Array。 e、对象Object。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <!-- 第二步、在父组件进行打印输出 -->
10 <div>{{pmsg}}</div>
11
12 <!-- 第二步、在子组件进行引用父组件的数据pstr -->
13 <menu-item v-bind:pstr2='pstr' v-bind:pnum2='pnum' v-bind:pboo2='pboo' v-bind:parr2='parr' v-bind:pobj2='pobj'>
14 </menu-item>
15
16 <br />
17 <menu-item :pstr2='pstr' :pnum2='pnum'></menu-item>
18 <br />
19
20 <!-- pnum2前面不加冒号就是字符串类型的,加上冒号就是数值number类型 -->
21 <menu-item v-bind:pstr2='pstr' pnum2='pnum'></menu-item>
22 </div>
23
24 <script src="vue.js" type="text/javascript"></script>
25 <script type="text/javascript">
26 // 父组件向子组件传值props属性名类型
27 Vue.component('menu-item', {
28 /* 第三步、在子组件进行引用父组件传来的数据 */
29 props: ['pstr2', 'pnum2', 'pboo2', 'parr2',
30 'pobj2'
31 ],
32 template: `
33 <div>
34 <div>{{pstr2}}</div>
35 <div>{{typeof pnum2}}</div>
36 <div>{{pboo2}}</div>
37 <div>{{parr2}}</div>
38 <ul>
39 <li v-bind:key='index' v-for='(item,index) in parr2'>{{item}}</>
40 </ul>
41 <div>
42 <span>{{pobj2.name}}</span>
43 <span>{{pobj2.age}}</span>
44 </div>
45 </div>
46 `
47 });
48
49 // 创建Vue对象
50 var vm = new Vue({
51 el: '#app',
52 data: { // 对象,区别于组件的data是一个函数。
53 pmsg: '父组件中内容', // 第一步、在父组件中进行打印显示
54 pstr: 'hello', // 第一步、在子组件进行打印显示,字符串类型
55 pnum: 12, //数组类型
56 pboo: true, //布尔类型
57 parr: ['apple', 'orange', 'banana'], //数组类型
58 pobj: {
59 name: '张飒飒',
60 age: 25,
61 },
62 },
63 methods: {
64
65 },
66 });
67 </script>
68 </body>
69 </html>
7、Vue组件,子组件向父组件传值。
1)、props传值数据原则,单向数据流,意思就是只允许父组件向子组件传递数据,而不允许子组件直接操作props中的数据。如果子组件直接操作props中的数据,数据的控制逻辑就比较复杂,不容易进行控制,单向数据流处理逻辑比较清晰,所以推荐使用单向数据流。 2)、Vue子组件向父组件传值的方式,是通过子组件通过自定义事件向父组件传递信息。$emit方法名称携带一个参数,这个参数名称就是自定义事件,这个事件就可以传递给父组件,父组件需要监听这个事件,父组件通过v-on:事件名称,直接绑定处理事件的名称,后面跟着事件处理逻辑。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <!-- 第二步、在父组件进行打印输出 -->
10 <!-- v-bind:style绑定样式字体大小,可以通过子组件向父组件传递信息,通过自定义事件 -->
11 <div v-bind:style='{fontSize:fontSize2 + "px"}'>{{pmsg}}</div>
12
13 <!-- 第二步、在子组件进行引用父组件的数据pstr -->
14 <menu-item v-bind:parr2='parr' v-on:enlarge-text='handle'></menu-item>
15 <br />
16 <menu-item v-bind:parr2='parr' @enlarge-text='handle'></menu-item>
17 </div>
18
19 <script src="vue.js" type="text/javascript"></script>
20 <script type="text/javascript">
21 // 父组件向子组件传值props属性名类型
22 Vue.component('menu-item', {
23 /* 第三步、在子组件进行引用父组件传来的数据 */
24 props: ['parr2'],
25 template: `
26 <div>
27 <ul>
28 <li v-bind:key='index' v-for='(item,index) in parr2'>{{item}}</li>
29 </ul>
30 <button @click='parr2.push("lemon")'>点击</button>
31
32 <button @click='$emit("enlarge-text")'>扩大父组件中字体大小</button>
33 </div>
34 `
35 /* <button @click='$emit("")'>扩大父组件中字体大小</button>中的$emit("")固定方法名称,参数是自定义事件的名称 */
36 /* 然后在父组件中进行自定义事件的监听, */
37 });
38
39 // 创建Vue对象
40 var vm = new Vue({
41 el: '#app',
42 data: { // 对象,区别于组件的data是一个函数。
43 pmsg: '父组件中内容', // 第一步、在父组件中进行打印显示
44 parr: ['apple', 'orange', 'banana'], //数组类型
45 fontSize2: 10,
46 },
47 methods: {
48 // Vue子组件向父组件传值的方式
49 handle: function() {
50 // 扩大字体大小
51 this.fontSize2 += 1;
52 }
53 },
54 });
55 </script>
56 </body>
57 </html>
3)、子组件通过自定义事件向父组件传值。子组件通过自定义事件向父组件传递信息,$emit方法名称可以携带两个参数,第二个参数可以是传递给父组件的参数。在父组件中通过$event接收到子组件传输的数据,$event是固定写法。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <!-- 第二步、在父组件进行打印输出 -->
10 <!-- v-bind:style绑定样式字体大小,可以通过子组件向父组件传递信息,通过自定义事件 -->
11 <div v-bind:style='{fontSize:fontSize2 + "px"}'>{{pmsg}}</div>
12
13 <!-- 第二步、在子组件进行引用父组件的数据pstr -->
14 <menu-item v-bind:parr2='parr' v-on:enlarge-text='handle($event)'></menu-item>
15 <br />
16 <!-- $event是固定写法,接收子组件传递的参数值 -->
17 <menu-item v-bind:parr2='parr' @enlarge-text='handle($event)'></menu-item>
18 </div>
19
20 <script src="vue.js" type="text/javascript"></script>
21 <script type="text/javascript">
22 // 父组件向子组件传值props属性名类型
23 Vue.component('menu-item', {
24 /* 第三步、在子组件进行引用父组件传来的数据 */
25 props: ['parr2'],
26 template: `
27 <div>
28 <ul>
29 <li v-bind:key='index' v-for='(item,index) in parr2'>{{item}}</li>
30 </ul>
31 <button @click='parr2.push("lemon")'>点击</button>
32
33 <button @click='$emit("enlarge-text",100)'>扩大父组件中字体大小</button>
34 </div>
35 `
36 /* <button @click='$emit("")'>扩大父组件中字体大小</button>中的$emit("")固定方法名称,参数是自定义事件的名称 */
37 /* 然后在父组件中进行自定义事件的监听, */
38 /* $emit固定方法,第二个参数可以是子组件向父组件传递的参数值 */
39 });
40
41 // 创建Vue对象
42 var vm = new Vue({
43 el: '#app',
44 data: { // 对象,区别于组件的data是一个函数。
45 pmsg: '父组件中内容', // 第一步、在父组件中进行打印显示
46 parr: ['apple', 'orange', 'banana'], //数组类型
47 fontSize2: 10,
48 },
49 methods: {
50 // Vue子组件向父组件传值的方式
51 handle: function(val) {
52 // 扩大字体大小
53 this.fontSize2 += val;
54 }
55 },
56 });
57 </script>
58 </body>
59 </html>
8、Vue组件,组件间数据交互,非父子组件间传值。
1)、单独的事件中心管理组件间的通信。比如兄弟组件之间的数据交互,此时使用props和自定义事件就不好使了,此时就要使用事件中心的管理模式。比如组件A和组件B需要通过事件中心,组件A触发事件,组件B监听事件中心的事件,反之亦然。 2)、监听事件与销毁事件。事件中心通过var eventHub = new Vue()即可。eventHub.$on('add-todo',addTodo)用于监听事件,参数1是自定义事件的名称,参数2是事件函数。eventHub.$off('add-todo')用于销毁时间事件,参数1是事件的名称。 3)、触发事件。eventHub.$emit('add-todo',id);参数一,触发指定的事件名称,参数二是携带的参数。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <div>
10 <span>事件中心的事件销毁</span>
11 <button @click="handle">销毁</button>
12 </div>
13
14 <br />
15 <menu-tom></menu-tom>
16
17 <br />
18 <!-- 兄弟组件,相互传递数据 -->
19 <menu-jack></menu-jack>
20 </div>
21
22 <script src="vue.js" type="text/javascript"></script>
23 <script type="text/javascript">
24 // 提供Vue的事件中心,在事件中心可以进行事件的监听
25 var hub = new Vue();
26
27 // 兄弟之间组件数据传递
28 Vue.component('menu-tom', {
29 data: function() {
30 return {
31 num: 0,
32 }
33 },
34 template: `
35 <div>
36 <div>Tom:{{num}}</div>
37 <div>
38 <button @click='handle'>点击</button>
39 </div>
40 </div>
41 `,
42 /* 点击按钮触发事件 */
43 methods: {
44 handle: function() {
45 // 触发兄弟组件的事件
46 hub.$emit('jack-event', 5);
47 }
48 },
49 // Vue的钩子函数,Vue声明周期里面的,mounted钩子函数一旦被触发,模板就就绪了,即可以对模板进行操作了。
50 mounted: function() {
51 // 监听事件,箭头函数,接收对方传递过来的数据
52 hub.$on('tom-event', (val) => {
53 // 拿到这对方的数据之后,进行累加操作,val是兄弟组件传递过来的
54 this.num += val;
55 });
56 }
57 });
58
59 // 兄弟组件
60 Vue.component('menu-jack', {
61 data: function() {
62 return {
63 num: 0,
64 }
65 },
66 template: `
67 <div>
68 <div>Jack:{{num}}</div>
69 <div>
70 <button @click='handle'>点击</button>
71 </div>
72 </div>
73 `,
74 /* 点击按钮触发事件 */
75 methods: {
76 handle: function() {
77 // 触发兄弟组件的事件
78 hub.$emit('tom-event', 10);
79 }
80 },
81 // Vue的钩子函数,Vue声明周期里面的,mounted钩子函数一旦被触发,模板就就绪了,即可以对模板进行操作了。
82 mounted: function() {
83 // 监听事件,箭头函数,接收对方传递过来的数据
84 hub.$on('jack-event', (val) => {
85 // 拿到这对方的数据之后,进行累加操作,val是兄弟组件传递过来的
86 this.num += val;
87 });
88 }
89 });
90
91 // 创建Vue对象
92 var vm = new Vue({
93 el: '#app',
94 data: { // 对象,区别于组件的data是一个函数。
95
96 },
97 methods: {
98 handle: function() {
99 // 事件中心的事件销毁
100 hub.$off('tom-event');
101 hub.$off('jack-event');
102 }
103 },
104 });
105 </script>
106 </body>
107 </html>
9、Vue组件,组件插槽。组件插槽的作用。
1)、父组件向子组件传递内容,这个内容是模板的内容。上面分析的是数据的交互。 2)、子组件使用<slot></slot>预留插槽,可以将父组件的中标签之间的内容展示出来。 3)、插槽的位置位于子组件的模板中,使用<slot></slot>表示,语法固定。 4)、使用的时候,使用这个组件的时候通过标签中的内容传递给<slot></slot>表示。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <menu-alert>此处有Bug!!!</menu-alert>
10 <menu-alert>此处有Warn!!!</menu-alert>
11 </div>
12
13 <script src="vue.js" type="text/javascript"></script>
14 <script type="text/javascript">
15 // 提供Vue的事件中心,在事件中心可以进行事件的监听
16 var hub = new Vue();
17
18 // 兄弟之间组件数据传递
19 Vue.component('menu-alert', {
20 /* 插槽的内容是在组件标签的中间传递过来的,如果不传递内容,这里有默认显示 */
21 template: `
22 <div>
23 <strong>Error:</strong>
24 <slot>默认内容!</slot>
25 </div>
26 `,
27 });
28
29 // 创建Vue对象
30 var vm = new Vue({
31 el: '#app',
32 data: { // 对象,区别于组件的data是一个函数。
33
34 },
35 methods: {
36
37
38 },
39 });
40 </script>
41 </body>
42 </html>
10、Vue组件,组件插槽。具名插槽用法。
1)、在子组件模板定义的时候,使用<slot name="插槽名称"></slot>来定义,也可以省略name的属性定义,就是默认插槽。 2)、在父组件中使用的时候,在标签上面使用slot="插槽名称"就可以使用该插槽了。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 </head>
7 <body>
8 <div id="app">
9 <menu-alert>
10 <p slot='header'>标题信息</p>
11 </menu-alert>
12 <menu-alert>
13 <p>详细内容1</p>
14 <p>详细内容2</p>
15 </menu-alert>
16 <menu-alert>
17 <p slot='footer'>底部信息</p>
18 </menu-alert>
19
20 <br />
21
22 <menu-alert>
23 <!-- template临时包裹内容,并不会渲染到界面上,可以同时把多条内容填充到文本中 -->
24 <template slot='header'></template>
25 <p>标题信息1</p>
26 <p>标题信息2</p>
27 </menu-alert>
28 <menu-alert>
29 <p>详细内容1</p>
30 <p>详细内容2</p>
31 </menu-alert>
32 <menu-alert>
33 <template slot='footer'>
34 <p>底部信息1</p>
35 <p>底部信息2</p>
36 </template>
37
38 </menu-alert>
39 </div>
40
41 <script src="vue.js" type="text/javascript"></script>
42 <script type="text/javascript">
43 // 提供Vue的事件中心,在事件中心可以进行事件的监听
44 var hub = new Vue();
45
46 // 兄弟之间组件数据传递
47 Vue.component('menu-alert', {
48 /* 插槽的内容是在组件标签的中间传递过来的,如果不传递内容,这里有默认显示 */
49 template: `
50 <div>
51 <header>
52 <slot name='header'></slot>
53 </header>
54 <main>
55 <slot></slot>
56 </main>
57 <footer>
58 <slot name='footer'></slot>
59 </footer>
60 </div>
61 `,
62 });
63
64 // 创建Vue对象
65 var vm = new Vue({
66 el: '#app',
67 data: { // 对象,区别于组件的data是一个函数。
68
69 },
70 methods: {
71
72
73 },
74 });
75 </script>
76 </body>
77 </html>
11、Vue组件,组件插槽。作用域插槽。
1)、应用场景,父组件对子组件的内容进行加工处理。
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <title></title>
6 <style type="text/css">
7 .orange {
8 color: orange;
9 }
10 </style>
11 </head>
12 <body>
13 <div id="app">
14 <!-- 应用场景,父组件对子组件的内容进行加工处理。 -->
15 <fruit-list v-bind:list2='list'>
16 <!-- <template slot-scope='slotProps'> -->
17 <!-- 父组件提供template标签,来进行插槽的填充,slot-scopek属性可以得到子组件中绑定的那个属性,即子组件绑定的v-bind:info='item'的属性 -->
18 <template slot-scope='slotProps'>
19 <!-- 就得到了子组件的数据了,就可以开始对子组件的数据进行加功处理了。 -->
20 <strong v-bind:class="orange2" v-if='slotProps.info.id==2'>
21 {{slotProps.info.name}}
22 </strong>
23
24 </template>
25
26 </fruit-list>
27 <br />
28 </div>
29
30 <script src="vue.js" type="text/javascript"></script>
31 <script type="text/javascript">
32 // 提供Vue的事件中心,在事件中心可以进行事件的监听
33 var hub = new Vue();
34
35 // 兄弟之间组件数据传递
36 Vue.component('fruit-list', {
37 props: ['list2'],
38 /* 插槽的内容是在组件标签的中间传递过来的,如果不传递内容,这里有默认显示 */
39 template: `
40 <div>
41 <li v-bind:key='item.id' v-for='(item,id) in list2'>
42 <slot v-bind:info='item'>{{item.name}}</slot>
43 </li>
44 </div>
45 `,
46 });
47
48 // 创建Vue对象
49 var vm = new Vue({
50 el: '#app',
51 data: { // 对象,区别于组件的data是一个函数。
52 orange2: 'orange',
53 list: [{
54 id: 1,
55 name: 'apple',
56 }, {
57 id: 2,
58 name: 'orange',
59 }, {
60 id: 3,
61 name: 'banana',
62 }, ]
63 },
64 methods: {
65
66
67 },
68 });
69 </script>
70 </body>
71 </html>
12、基于组件的案例,按照组件化方式实现业务需求。
1)、根据业务功能进行组件化划分。
a、标题组件,展示文本。 b、列表组件,列表展示,商品数量变更,商品删除。 c、结算组件,计算商品金额。 2)、功能实现步骤。 a、实现整体布局和样式效果。 b、划分独立的功能组件。 c、组合所有的子组件形成整体结构。 d、逐个实现各个组件的功能,包含标题组件,列表组件,结算组件。
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>Document</title>
6 <style type="text/css">
7 .container {}
8
9 .container .cart {
10 width: 300px;
11 /*background-color: lightgreen;*/
12 margin: auto;
13 }
14
15 .container .title {
16 background-color: lightblue;
17 height: 40px;
18 line-height: 40px;
19 text-align: center;
20 /*color: #fff;*/
21 }
22
23 .container .total {
24 background-color: #FFCE46;
25 height: 50px;
26 line-height: 50px;
27 text-align: right;
28 }
29
30 .container .total button {
31 margin: 0 10px;
32 background-color: #DC4C40;
33 height: 35px;
34 width: 80px;
35 border: 0;
36 }
37
38 .container .total span {
39 color: red;
40 font-weight: bold;
41 }
42
43 .container .item {
44 height: 55px;
45 line-height: 55px;
46 position: relative;
47 border-top: 1px solid #ADD8E6;
48 }
49
50 .container .item img {
51 width: 45px;
52 height: 45px;
53 margin: 5px;
54 }
55
56 .container .item .name {
57 position: absolute;
58 width: 90px;
59 top: 0;
60 left: 55px;
61 font-size: 16px;
62 }
63
64 .container .item .change {
65 width: 100px;
66 position: absolute;
67 top: 0;
68 right: 50px;
69 }
70
71 .container .item .change a {
72 font-size: 20px;
73 width: 30px;
74 text-decoration: none;
75 background-color: lightgray;
76 vertical-align: middle;
77 }
78
79 .container .item .change .num {
80 width: 40px;
81 height: 25px;
82 }
83
84 .container .item .del {
85 position: absolute;
86 top: 0;
87 right: 0px;
88 width: 40px;
89 text-align: center;
90 font-size: 40px;
91 cursor: pointer;
92 color: red;
93 }
94
95 .container .item .del:hover {
96 background-color: orange;
97 }
98 </style>
99 </head>
100 <body>
101 <div id="app">
102 <div class="container">
103 <my-cart></my-cart>
104 </div>
105 </div>
106 <script type="text/javascript" src="js/vue.js"></script>
107 <script type="text/javascript">
108 var CartTitle = {
109 props: ['username'],
110 template: `
111 <div class="title">{{username }} 的商品</div>
112 `
113 };
114 var CartList = {
115 props: ['lists'],
116 template: `
117 <div>
118 <div v-bind:key='id' v-for='(item,id) in lists' class="item">
119 <img v-bind:src="item.img" />
120 <div class="name">{{item.name}}</div>
121 <div class="change">
122 <a href="" @click.prevent='sub(item.id)'>-</a>
123 <input type="text" class="num" v-bind:value='item.num' @blur='changeNum(item.id,$event)' />
124 <a href="" @click.prevent='add(item.id)'>+</a>
125 </div>
126 <div class="del" @click='del(item.id)'>×</div>
127 </div>
128 </div>
129 `,
130 methods: {
131 del: function(id) {
132 // 不建议直接在子组件中操作props: ['lists'],的数据。将id传给父组件,让父组件进行删除。
133 console.log(id);
134 // 将id传递给父组件,通过自定义事件的方式向父组件传递信息
135 this.$emit('cart-del', id);
136 },
137 /* 通过事件对象$event可以将参数传递过来 */
138 changeNum: function(id, event) {
139 // 在父组件进行处理商品数量的变更,告诉父组件改的数量也要传递过去。
140 console.log(id, event.target.value);
141 // 触发自定义事件
142 this.$emit('change-num', {
143 id: id,
144 num: event.target.value,
145 type: 'change',
146 });
147 },
148 sub: function(id) {
149 this.$emit('change-num', {
150 id: id,
151 type: 'sub',
152 });
153 },
154 add: function(id) {
155 this.$emit('change-num', {
156 id: id,
157 type: 'add',
158 })
159 },
160 }
161 };
162 var CartTotal = {
163 props: ['lists'],
164 template: `
165 <div class="total">
166 <span>总价:{{totalPrice}}</span>
167 <button>结算</button>
168 </div>
169 `,
170 // 计算属性
171 computed: {
172 totalPrice: function() {
173 // 计算商品的总价,遍历数组,让单价乘以数量再累加。
174 var total = 0;
175 this.lists.forEach(item => {
176 // 计算出总价
177 total += item.price * item.num;
178 });
179 // 将计算的总价返回。
180 return total;
181 }
182 }
183 };
184
185 // 全局组件,这个全局组件里面包含三个局部子组件。
186 Vue.component('my-cart', {
187 data: function() {
188 return {
189 uname: '张飒飒',
190 list: [{
191 id: 1,
192 name: 'TCL彩电',
193 price: 1000,
194 num: 1,
195 img: 'img/a.jpg'
196 }, {
197 id: 2,
198 name: '机顶盒',
199 price: 1000,
200 num: 1,
201 img: 'img/b.jpg'
202 }, {
203 id: 3,
204 name: '海尔冰箱',
205 price: 1000,
206 num: 1,
207 img: 'img/c.jpg'
208 }, {
209 id: 4,
210 name: '小米手机',
211 price: 1000,
212 num: 3,
213 img: 'img/d.jpg'
214 }, {
215 id: 5,
216 name: 'PPTV电视',
217 price: 1000,
218 num: 2,
219 img: 'img/e.jpg'
220 }]
221 }
222 },
223 /* 通过父组件监听子组件的删除事件,将id传输给delCart方法的$event参数 */
224 template: `
225 <div class="cart">
226 <cart-title v-bind:username='uname'></cart-title>
227 <cart-list v-bind:lists='list' v-on:cart-del='delCart($event)' @change-num='changeNum($event)'></cart-list>
228 <cart-total v-bind:lists='list'></cart-total>
229 </div>
230 `,
231 components: {
232 'cart-title': CartTitle,
233 'cart-list': CartList,
234 'cart-total': CartTotal,
235 },
236 methods: {
237 delCart: function(id) {
238 console.log(id);
239 // 通过id删除list集合中的数据,第一步,找到id所对应数据的索引,第二步,根据索引删除对应的数据。
240 // 第一步,找到id所对应数据的索引。
241 var index = this.list.findIndex(item => {
242 return item.id == id;
243 });
244
245 // 第二步,根据索引删除对应的数据
246 this.list.splice(index, 1);
247 },
248 changeNum: function(val) {
249 console.log(val);
250 // 分为三种情况,输入域变更,加好变更,减号变更。
251 if (val.type == 'change') {
252 // 将子组件传递过来的数据num更新list中对应的数据
253 this.list.some(item => {
254 // 如果集合里面的和子组件传递过来的匹配相等就进行更新即可。
255 if (item.id == val.id) {
256 // 此时就完成了更新
257 item.num = val.num;
258 // 终止遍历
259 return true; // 返回true表示终止遍历
260 }
261 });
262 } else if (val.type == 'add') {
263 this.list.some(item => {
264 // 如果集合里面的和子组件传递过来的匹配相等就进行更新即可。
265 if (item.id == val.id) {
266 // 此时就完成了更新
267 item.num += 1;
268 // 终止遍历
269 return true; // 返回true表示终止遍历
270 }
271 });
272 } else if (val.type == 'sub') {
273 this.list.some(item => {
274 // 如果集合里面的和子组件传递过来的匹配相等就进行更新即可。
275 if (item.id == val.id) {
276 // 此时就完成了更新
277 item.num -= 1;
278 // 终止遍历
279 return true; // 返回true表示终止遍历
280 }
281 });
282 }
283 }
284 }
285 });
286
287 var vm = new Vue({
288 el: '#app',
289 data: {
290
291 }
292 });
293 </script>
294 </body>
295 </html>
效果,如下所示: