• Vue组件化开发


    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                  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                  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                  45px;
     52                 height: 45px;
     53                 margin: 5px;
     54             }
     55 
     56             .container .item .name {
     57                 position: absolute;
     58                  90px;
     59                 top: 0;
     60                 left: 55px;
     61                 font-size: 16px;
     62             }
     63 
     64             .container .item .change {
     65                  100px;
     66                 position: absolute;
     67                 top: 0;
     68                 right: 50px;
     69             }
     70 
     71             .container .item .change a {
     72                 font-size: 20px;
     73                  30px;
     74                 text-decoration: none;
     75                 background-color: lightgray;
     76                 vertical-align: middle;
     77             }
     78 
     79             .container .item .change .num {
     80                  40px;
     81                 height: 25px;
     82             }
     83 
     84             .container .item .del {
     85                 position: absolute;
     86                 top: 0;
     87                 right: 0px;
     88                  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>

    效果,如下所示:

  • 相关阅读:
    中间件格式
    python3 bytes与str数据类型相互转换
    python 连接mongodb 使用
    md5 简单加密
    django 使用https协议运行runserver
    工厂模式
    C++字符串
    C++字符
    C++数学函数
    MATLAB函数总结——数值运算和符号运算
  • 原文地址:https://www.cnblogs.com/biehongli/p/12726157.html
Copyright © 2020-2023  润新知