Vue基础第三天知识点以及知识点代码:Vue组件化开发

目标

  • 能够知道组件化开发思想
  • 能够知道组件的注册方式
  • 能够说出组件间的数据交互方式
  • 能够说出组件插槽的用法
  • 能够说出Vue调试工具的用法
  • 能够基于组件的方式实现业务功能

1. 组件化开发思想

1.1 现实中的组件化思想体现

在这里插入图片描述

1.2 编程中的组件化思想体现

在这里插入图片描述

1.3 组件化规范: Web Components

2. 组件注册

2.1 全局组件注册语法

在这里插入图片描述

Vue.component(组件名称, {
	data: 组件数据,
	template: 组件模板内容
})
// 注册一个名为 button-counter 的新组件
Vue.component('button-counter', {
	data: function () {
		return {
			count: 0
		}
	},
	template: '<button v-on:click="count++">点击了{{ count }}次.</button>'
})

实现代码:

<body>
    <div id="app">
        <button-counter></button-counter>
    </div>

    <script src="js/vue.js"></script>

    <script>
        Vue.component('button-counter', {
            data: function() {
                return {
                    count: 0
                }
            },
            template: '<button @click="count++">点击了 {{count}} 次</button>'
        })

        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

☆ 还可以通过 methods 内部调用的方法来操作:

    <div id="app">
        <!-- 子组件数据相互独立,互相不影响 -->
        <button-counter></button-counter>
        <button-counter></button-counter>
        <button-counter></button-counter>
    </div>

    <script src="js/vue.js"></script>

    <script>
        Vue.component('button-counter', {
            data: function() {
                return {
                    count: 0
                }
            },
            template: '<button @click="handle">点击了 {{count}} 次</button>',
            methods: {
                handle: function() {
                    this.count += 2      //必须要使用 this 来调用
                }
            }
        })

        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

2.2 组件用法

<div id="app">
	<button-counter></button-counter>
</div>
<div id="app">
	<button-counter></button-counter>
	<button-counter></button-counter>
	<button-counter></button-counter>
</div>

2.3 组件注册注意事项

1. data必须是一个函数

  • 分析函数与普通对象的对比

2. 组件模板内容必须是单个跟元素

  • 分析演示实际的效果

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

  • 模板字符串需要浏览器提供支持(ES6语法)

实现代码:

<body>
    <div id="app">
        <!-- 子组件数据相互独立,互相不影响 -->
        <button-counter></button-counter>
        <button-counter></button-counter>
        <button-counter></button-counter>
    </div>

    <script src="js/vue.js"></script>

    <script>
        // 组件注册注意事项
        //     1. data必须是一个函数
        //     2. 组件模板内容必须是单个跟元素
        //     3. 组件模板内容可以是模板字符串
        Vue.component('button-counter', {
            data: function() {
                return {
                    count: 0
                }
            },

            // 3. 组件模板内容可以是模板字符串
            
            // 必须要用反引符号 `` 。
            template: `   
                <div>
                    <button @click="count++">点击了{{count}}次</button>
                    <button>测试</button>
                </div>
            `
        })

        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

4. 组件命名方式

  • 短横线方式:
Vue.component('my-component', { /* ... */ })
  • 驼峰方式:
Vue.component('MyComponent', { /* ... */ })

驼峰方式实现代码:

<body>
    <div id="app">
        <button-counter></button-counter>
        <hello-world></hello-world>
    </div>

    <script src="js/vue.js"></script>

    <script>
    //   组件注册注意事项
    //     如果使用驼峰式命名组件,那么在使用组件的时候,只能在字符串模板中用驼峰的方式使用组件,但是
    //     在普通的标签模板中,必须使用短横线的方式使用组件
        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="count++">点击了{{count}}次</button>
                    <button>测试</button>
                    <HelloWorld></HelloWorld>
                </div>
            `
        })

        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

2.4 局部组件注册

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
	el: '#app',
	// 在 components 里面可以注册更多的局部组件
	components: {
		//推荐用短横线的方式写:
		'component-a': ComponentA,
		'component-b': ComponentB,
		'component-c': ComponentC,
	}
})

实现代码:

<body>
    <div id="app">
        <hello-world></hello-world>
        <hello-jerry></hello-jerry>
        <hello-tom></hello-tom>
    </div>

    <script src="js/vue.js"></script>

    <script>
    //   局部组件注册
    //   局部组件只能在注册他的父组件中使用
        Vue.component('test-com',{
          template: '<div>Test<hello-world></hello-world></div>'
        });
    
        var HelloWorld = {
            data: function() {
                return {
                    msg: 'HelloWorld'
                }
            },
            template: '<div>{{msg}}</div>'
        }
        var HelloJerry = {
            data: function() {
                return {
                    msg: 'HelloJerry'
                }
            },
            template: '<div>{{msg}}</div>'
        }
        var HelloTom = {
            data: function() {
                return {
                    msg: 'HelloTom'
                }
            },
            template: '<div>{{msg}}</div>'
        }

        var vm = new Vue({
            el: '#app',
            data: {

            },
            components: {
                'hello-world': HelloWorld,
                'hello-jerry': HelloJerry,
                'hello-tom': HelloTom
            }
        })
    </script>
</body>

3. Vue调试工具

3.1 调试工具安装

① 克隆仓库
② 安装依赖包
③ 构建
④ 打开Chrome扩展页面
⑤ 选中开发者模式
⑥ 加载已解压的扩展,选择shells/chrome
在这里插入图片描述

3.2 调试工具用法

在这里插入图片描述
测试代码:

  <style type="text/css">
    .root {
      background-color: orange;
    }
    .second {
      background-color: lightgreen;
    }
    .third {
      background-color: lightblue;
    }
  </style>
</head>
<body>
  <div id="app" class="root">
    <div>{{root}}</div>
    <second-com></second-com>
    <second-com></second-com>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    /*
      Vue调试工具安装与基本使用
    */
    Vue.component('second-com',{
      data: function(){
        return {
          second: '二级组件'
        }
      },
      template: `<div class='second'>
        <div>{{second}}</div>
        <third-com></third-com>
        <third-com></third-com>
        <third-com></third-com>
      </div>`
    });
    Vue.component('third-com',{
      data: function(){
        return {
          third: '三级组件'
        }
      },
      template: '<div class="third"><div>{{third}}</div></div>'
    });
    
    var vm = new Vue({
      el: '#app',
      data: {
        root: '顶层组件'
      }
    });
  </script>
</body>

4. 组件间数据交互

4.1 父组件向子组件传值

1. 组件内部通过props接收传递过来的值(接收父组件传过来的数据)

Vue.component(‘menu-item', {
	props: ['title'],   // 它的值是一个 数组
	template: '<div>{{ title }}</div>'
})

2. 父组件通过属性将值传递给子组件

<menu-item title="来自父组件的数据"></menu-item>
<menu-item :title="title"></menu-item>

实现代码:

<body>
    <div id="app">
        <div>{{pmsg}}</div>
        <menu-item title="来自父组件的值"></menu-item>
        <!-- 动态绑定值: -->
        <menu-item :title="ptitle"></menu-item>
        <!-- 动态绑定多个值: -->
        <menu-item :title="ptitle" content="hello"></menu-item>
    </div>

    <script src="js/vue.js"></script>

    <script>
        //定义子组件:
        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>

3. props属性名规则

  • 在props中使用驼峰形式,模板中需要使用短横线的形式
Vue.component(‘menu-item', {
	// 在 JavaScript 中是驼峰式的
	props: [‘menuTitle'],
	template: '<div>{{ menuTitle }}</div>'
})

<!– 在html中是短横线方式的 -->
<menu-item menu-title=“nihao"></menu-item>

实现代码:

<body>
    <div id="app">
        <div>{{pmsg}}</div>
        <menu-item :menu-title="ptitle"></menu-item>
    </div>

    <script src="js/vue.js"></script>

    <script>
        //定义子组件:
        Vue.component('menu-item', {
            props: ['MenuTitle'],
            template: '<div>{{MenuTitle}}</div>'
        })

        var vm = new Vue({
            el:'#app',
            data: {
                pmsg: '父组件中内容',
                ptitle: '动态绑定属性'
            }
        })
    </script>
</body>
  • 字符串形式的模板中没有这个限制

实现代码:

<body>
    <div id="app">
        <div>{{pmsg}}</div>
        <menu-item :menu-title="ptitle"></menu-item>
    </div>

    <script src="js/vue.js"></script>

    <script>
        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>

4. props属性值类型

  • 字符串 String
  • 数值 Number
  • 布尔值 Boolean
  • 数组 Array
  • 对象 Object

实现代码:

<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>

4.2 子组件向父组件传值

1. 子组件通过自定义事件向父组件传递信息

<button v-on:click='$emit("enlarge-text") '>扩大字体</button>

2. 父组件监听子组件的事件

<menu-item v-on:enlarge-text='fontSize += 0.1'></menu-item>

第1 第2 实现代码:

<body>
    <div id="app">
        <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
        <menu-item :parr='parr' @enlarge-text='handle'></menu-item>
    </div>

    <script src="js/vue.js"></script>

    <script>
        // 子组件向父组件传值-基本用法
        // props传递数据原则:单向数据流

        Vue.component('menu-item', {
            props: ['parr'],
            template: `
                <div>
                    <ul>
                        <li :key='index' v-for='(item, index) in parr'>{{item}}</li>
                        <button @click='parr.push("lemo")'>点击</button>
                        <button @click='$emit("enlarge-text")'>扩大父组件字体大小</button>
                    </ul>
                </div>
            `
        });

        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件内容',
                parr: ['apple', 'orange', 'banana'],
                fontSize: 10
            },
            methods: {
                handle: function() {
                    // 扩大字体大小
                    this.fontSize += 5;
                }
            }
        })
    </script>
</body>

3. 子组件通过自定义事件向父组件传递信息

<button v-on:click='$emit("enlarge-text", 0.1) '>扩大字体</button>

4. 父组件监听子组件的事件

<menu-item v-on:enlarge-text='fontSize += $event'></menu-item>

第3 第4 实现代码:

<body>
    <div id="app">
        <div :style='{fontSize: fontSize + "px"}'>{{pmsg}}</div>
        <menu-item :parr='parr' @enlarge-text='handle($event)'></menu-item>
    </div>

    <script src="js/vue.js"></script>

    <script>
        // 子组件向父组件传值-携带参数

        Vue.component('menu-item', {
            props: ['parr'],
            template: `
                <div>
                    <ul>
                        <li :key='index' v-for='(item, index) in parr'>{{item}}</li>
                        <button @click='$emit("enlarge-text", 5)'>扩大父组件字体大小5</button>
                        <button @click='$emit("enlarge-text", 10)'>扩大父组件字体大小10</button>
                    </ul>
                </div>
            `
        });

        var vm = new Vue({
            el: '#app',
            data: {
                pmsg: '父组件内容',
                parr: ['apple', 'orange', 'banana'],
                fontSize: 10
            },
            methods: {
                handle: function(val) {
                    // 扩大字体大小
                    this.fontSize += val;
                }
            }
        })
    </script>
</body>

4.3 非父子组件间传值

在这里插入图片描述

1. 单独的事件中心管理组件间的通信

var eventHub = new Vue()

2. 监听事件与销毁事件

eventHub.$on('add-todo', addTodo)
eventHub.$off('add-todo')

3. 触发事件

eventHub.$emit(‘add-todo', id)

第1 2 3 实现代码:

<body>
    <div id="app">
        <div>父组件</div>
        <div>
            <button @click="handle">销毁</button>
        </div>
        <text-tom></text-tom>
        <text-jerry></text-jerry>
    </div>
    <script src="js/vue.js"></script>

    <script>
        // 提供事件中心
        var hub = new Vue();



        // Tom
        Vue.component('text-tom', {
            data: function() {
                return {
                    num: 0
                }
            },
            template: `
                <div>
                    <div>Tom: {{num}}</div>
                    <div>
                        <button @click="handle">点击</button>
                    </div>
                </div>
            `,
            methods: {
                handle: function() {
                    // 触发监听兄弟组件的事件
                    hub.$emit('jerry-event', 2);
                }
            },
            mounted: function() {
                // 在这里监听事件
                hub.$on('tom-event', (val)=> {
                    this.num += val;
                })
            }
        });

        // jerry
        Vue.component('text-jerry', {
            data: function() {
                return {
                    num: 0
                }
            },
            template: `
                <div>
                    <div>Jerry: {{num}}</div>
                    <div>
                        <button @click="handle">点击</button>
                    </div>
                </div>
            `,
            methods: {
                handle: function() {
                    // 触发监听兄弟组件的事件
                    hub.$emit('tom-event', 1);
                }
            },
            mounted: function() {
                // 在这里监听事件
                hub.$on('jerry-event', (val)=> {
                    this.num += val;
                })
            }
        });

        var vm = new Vue({
            el: '#app',
            data: {

            },
            methods: {
                handle: function() {
                    hub.$off('tom-event');
                    hub.$off('jerry-event');
                }
            }
        });
    </script>
</body>

组件插槽

5.1 组件插槽的作用

  • 父组件向子组件传递内容
    在这里插入图片描述

5.2 组件插槽基本用法

1. 插槽位置

Vue.component('alert-box', {
	template: `
		<div class="demo-alert-box">
		<strong>Error!</strong>
		<slot></slot>
		</div>
	`
})

2. 插槽内容

<alert-box>Something bad happened.</alert-box>

第1 2 实现代码:

<body>
    <div id="app">
        <alert-box>有bug发生</alert-box>
        <alert-box>有一个警告</alert-box>
        <alert-box></alert-box>
    </div>
    <script src="js/vue.js"></script>

    <script>
        Vue.component('alert-box', {
            template: `
                <div>
                    <strong>ERROR:</strong>
                    <slot>默认内容</slot>
                </div>
            `
        });

        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

5.3 具名插槽用法

1. 插槽定义

<div id="app">
	<header>
		<slot name="header"></slot>
	</header>
	
	<main>
		<slot></slot>
	</main>
	
	<footer>
		<slot name="footer"></slot>
	</footer>
</div>

2. 插槽内容

<base-layout>
	<h1 slot="header">标题内容</h1>
	
	<p>主要内容1</p>
	<p>主要内容2</p>
	
	<p slot="footer">底部内容</p>
</base-layout>

第 1 2 实现代码:简单操作:

<body>
    <div id="app">
        <base-layout>
            <p slot="header">标题信息</p>

            <p>主要内容1</p>
            <p>主要内容2</p>

            <p slot="footer">底部标题信息</p>
        </base-layout>
    </div>
    <script src="js/vue.js"></script>

    <script>
        Vue.component('base-layout', {
            template: `
                <div>
                    <hender>
                        <slot name='header'></slot>
                    </hender>

                    <main>
                        <slot></slot>
                    </main>

                    <footer>
                        <slot name='footer'></slot>
                    </footer>
                </div>
            `
        });
        
        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

复杂用法:

<body>
    <div id="app">
        <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 src="js/vue.js"></script>

    <script>
        Vue.component('base-layout', {
            template: `
                <div>
                    <hender>
                        <slot name='header'></slot>
                    </hender>

                    <main>
                        <slot></slot>
                    </main>

                    <footer>
                        <slot name='footer'></slot>
                    </footer>
                </div>
            `
        })

        var vm = new Vue({
            el: '#app',
            data: {

            }
        });
    </script>
</body>

5.4 作用域插槽

在这里插入图片描述

1. 插槽定义

<ul>
	<li v-for= "item in list" v-bind:key= "item.id" >
		<slot v-bind:item="item">
			{{item.name}}
		</slot>
	</li>
</ul>

2. 插槽内容

<fruit-list v-bind:list= "list">
	<template slot-scope="slotProps">
		<strong v-if="slotProps.item.current">
			{{ slotProps.item.text }}
		</strong>
	</template>
</fruit-list>

第1 2 实现代码:

    <style>
        .current {
            color: orange;
        }
    </style>
</head>
<body>
    <div id="app">
        <fruit-list :list="list">
            <template slot-scope="slotProps">
                <strong v-if="slotProps.info.id == 2" class="current">
                    {{slotProps.info.name}}
                </strong>
                <span v-else>{{slotProps.info.name}}</span>
            </template>
        </fruit-list>
    </div>
    <script src="js/vue.js"></script>

    <script>
        Vue.component('fruit-list', {
            props: ['list'],
            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>

6.基于组件的案例

案例:购物车

在这里插入图片描述

需求分析:

1. 按照组件化方式实现业务需求

  • 根据业务功能进行组件化划分
    ① 标题组件(展示文本)
    ② 列表组件(列表展示、商品数量变更、商品删除)
    ③ 结算组件(计算商品总额)

2. 功能实现步骤

  • 实现整体布局和样式效果
  • 划分独立的功能组件
  • 组合所有的子组件形成整体结构
  • 逐个实现各个组件功能
    • 标题组件
    • 列表组件
    • 结算组件

实现代码

1.组件化布局

  <style type="text/css">
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      template: `
        <div class="title">我的商品</div>
      `
    }
    var CartList = {
      template: `
        <div>
          <div class="item">
            <img src="img/a.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/b.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/c.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/d.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/e.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
        </div>
      `
    }
    var CartTotal = {
      template: `
        <div class="total">
          <span>总价:123</span>
          <button>结算</button>
        </div>
      `
    }
    Vue.component('my-cart',{
      template: `
        <div class='cart'>
          <cart-title></cart-title>
          <cart-list></cart-list>
          <cart-total></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });

  </script>
</body>

2.完成标题和结算组件功能

  <style type="text/css">
    .container {
    }
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      template: `
        <div>
          <div class="item">
            <img src="img/a.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/b.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/c.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/d.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/e.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
        </div>
      `
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>总价:{{total}}</span>
          <button>结算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 计算商品的总价
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '张三',
          list: [{
            id: 1,
            name: 'TCL彩电',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '机顶盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海尔冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手机',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV电视',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });

  </script>
</body>

3.完成列表组件删除商品功能

  <style type="text/css">
    .container {
    }
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              <a href=""></a>
              <input type="text" class="num" />
              <a href=""></a>
            </div>
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        del: function(id){
          // 把id传递给父组件
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>总价:{{total}}</span>
          <button>结算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 计算商品的总价
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '张三',
          list: [{
            id: 1,
            name: 'TCL彩电',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '机顶盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海尔冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手机',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV电视',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list :list='list' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        delCart: function(id) {
          // 根据id删除list中对应的数据
          // 1、找到id所对应数据的索引
          var index = this.list.findIndex(item=>{
            return item.id == id;
          });
          // 2、根据索引删除对应数据
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });

  </script>
</body>

4.完成列表组件更新商品数量(1)

  <style type="text/css">
    .container {
    }
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
              <a href="">+</a>
            </div>
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        changeNum: function(id, event){
          this.$emit('change-num', {
            id: id,
            num: event.target.value
          });
        },
        del: function(id){
          // 把id传递给父组件
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>总价:{{total}}</span>
          <button>结算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 计算商品的总价
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '张三',
          list: [{
            id: 1,
            name: 'TCL彩电',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '机顶盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海尔冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手机',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV电视',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        changeNum: function(val) {
          // 根据子组件传递过来的数据,跟新list中对应的数据
          this.list.some(item=>{
            if(item.id == val.id) {
              item.num = val.num;
              // 终止遍历
              return true;
            }
          });
        },
        delCart: function(id) {
          // 根据id删除list中对应的数据
          // 1、找到id所对应数据的索引
          var index = this.list.findIndex(item=>{
            return item.id == id;
          });
          // 2、根据索引删除对应数据
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });
  </script>
</body>

完成列表组件更新商品数量(2)

  <style type="text/css">
    .container {
    }
    .container .cart {
      width: 300px;
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var CartTitle = {
      props: ['uname'],
      template: `
        <div class="title">{{uname}}的商品</div>
      `
    }
    var CartList = {
      props: ['list'],
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">
              <a href="" @click.prevent='sub(item.id)'>-</a>
              <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
              <a href="" @click.prevent='add(item.id)'>+</a>
            </div>
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        changeNum: function(id, event){
          this.$emit('change-num', {
            id: id,
            type: 'change',
            num: event.target.value
          });
        },
        sub: function(id){
          this.$emit('change-num', {
            id: id,
            type: 'sub'
          });
        },
        add: function(id){
          this.$emit('change-num', {
            id: id,
            type: 'add'
          });
        },
        del: function(id){
          // 把id传递给父组件
          this.$emit('cart-del', id);
        }
      }
    }
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>总价:{{total}}</span>
          <button>结算</button>
        </div>
      `,
      computed: {
        total: function() {
          // 计算商品的总价
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart',{
      data: function() {
        return {
          uname: '张三',
          list: [{
            id: 1,
            name: 'TCL彩电',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          },{
            id: 2,
            name: '机顶盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          },{
            id: 3,
            name: '海尔冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          },{
            id: 4,
            name: '小米手机',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          },{
            id: 5,
            name: 'PPTV电视',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        changeNum: function(val) {
          // 分为三种情况:输入域变更、加号变更、减号变更
          if(val.type=='change') {
            // 根据子组件传递过来的数据,跟新list中对应的数据
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num = val.num;
                // 终止遍历
                return true;
              }
            });
          }else if(val.type=='sub'){
            // 减一操作
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num -= 1;
                // 终止遍历
                return true;
              }
            });
          }else if(val.type=='add'){
            // 加一操作
            this.list.some(item=>{
              if(item.id == val.id) {
                item.num += 1;
                // 终止遍历
                return true;
              }
            });
          }
        },
        delCart: function(id) {
          // 根据id删除list中对应的数据
          // 1、找到id所对应数据的索引
          var index = this.list.findIndex(item=>{
            return item.id == id;
          });
          // 2、根据索引删除对应数据
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });

  </script>
</body>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值