Vue组件化练习-简单购物车案例(附代码)

2022-10-01 14:05:14

定义Vue组件

什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

全局组件定义的三种方式

  1. 使用 Vue.extend 配合 Vue.component 方法:
var login = Vue.extend({
      template: '<h1>登录</h1>'
    });
    Vue.component('login', login);
  1. 直接使用 Vue.component 方法:
Vue.component('register', {
      template: '<h1>注册</h1>'
    });
  1. 将模板字符串,定义到script标签种:
<script id="tmpl" type="x-template">
      <div><a href="#">登录</a> | <a href="#">注册</a></div>
    </script>

同时,需要使用 Vue.component 来定义组件:

Vue.component('account', {
      template: '#tmpl'
    });

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

组件中展示数据和响应事件

  1. 在组件中,data需要被定义为一个方法,例如:
Vue.component('account', {
      template: '#tmpl',
      data() {
        return {
          msg: '大家好!'
        }
      },
      methods:{
        login(){
          alert('点击了登录按钮');
        }
      }
    });
  1. 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问;

【重点】为什么组件中的data属性必须定义为一个方法并返回一个对象

  1. 通过计数器案例演示

使用components属性定义局部子组件

  1. 组件实例定义方式:
<script>// 创建 Vue 实例,得到 ViewModelvar vm=newVue({
      el:'#app',
      data:{},
      methods:{},
      components:{// 定义子组件
        account:{// account 组件
          template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>',// 在这里使用定义的子组件
          components:{// 定义子组件的子组件
            login:{// login 组件
              template:"<h3>这是登录组件</h3>"}}}}});</script>
  1. 引用组件:
<div id="app"><account></account></div>

使用flag标识符结合v-ifv-else切换组件

  1. 页面结构:
<div id="app"><input type="button" value="toggle"@click="flag=!flag"><my-com1 v-if="flag"></my-com1><my-com2 v-else="flag"></my-com2></div>
  1. Vue实例定义:
<script>Vue.component('myCom1',{
      template: '<h3>奔波霸</h3>'})Vue.component('myCom2',{
      template: '<h3>霸波奔</h3>'})// 创建 Vue 实例,得到 ViewModelvar vm=newVue({
      el:'#app',
      data:{
        flag:true},
      methods:{}});</script>

使用:is属性来切换不同的子组件,并添加切换动画

  1. 组件实例定义方式:
// 登录组件const login=Vue.extend({
      template: `<div><h3>登录组件</h3></div>`});Vue.component('login', login);// 注册组件const register=Vue.extend({
      template: `<div><h3>注册组件</h3></div>`});Vue.component('register', register);// 创建 Vue 实例,得到 ViewModelvar vm=newVue({
      el:'#app',
      data:{ comName:'login'},
      methods:{}});
  1. 使用component标签,来引用组件,并通过:is属性来指定要加载的组件:
<div id="app"><a href="#"@click.prevent="comName='login'">登录</a><a href="#"@click.prevent="comName='register'">注册</a><hr><transition mode="out-in"><component:is="comName"></component></transition></div>
  1. 添加切换样式:
<style>.v-enter,.v-leave-to{
      opacity:0;
      transform:translateX(30px);}.v-enter-active,.v-leave-active{
      position: absolute;
      transition: all0.3s ease;}

    h3{
      margin:0;}</style>

父组件向子组件传值

  1. 组件实例定义方式,注意:一定要使用props属性来定义父组件传递过来的数据
<script>// 创建 Vue 实例,得到 ViewModelvar vm=newVue({
      el:'#app',
      data:{
        msg: '这是父组件中的消息'},
      components:{
        son:{
          template: '<h1>这是子组件---{{finfo}}</h1>',
          props:['finfo']}}});</script>
  1. 使用v-bind或简化指令,将数据传递到子组件中:
<div id="app"><son:finfo="msg"></son></div>

子组件向父组件传值

  1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
  2. 父组件将方法的引用传递给子组件,其中,getMsg是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称
<son@func="getMsg"></son>
  1. 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用
<div id="app"><!-- 引用父组件--><son@func="getMsg"></son><!-- 组件模板定义--><script type="x-template" id="son"><div><input type="button" value="向父组件传值"@click="sendMsg"/></div></script></div><script>// 子组件的定义方式Vue.component('son',{
      template:'#son',// 组件模板Id
      methods:{sendMsg(){// 按钮的点击事件this.$emit('func','OK');// 调用父组件传递过来的方法,同时把数据传递出去}}});// 创建 Vue 实例,得到 ViewModelvar vm=newVue({
      el:'#app',
      data:{},
      methods:{getMsg(val){// 子组件中,通过 this.$emit() 实际调用的方法,在此进行定义alert(val);}}});</script>

简单购物车案例

在这里插入图片描述

代码实现:

<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"/><metahttp-equiv="X-UA-Compatible"content="IE=edge"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>购物车案例</title><style>.cart{width: 300px;margin: auto;}.cart .title{background-color:rgb(252, 11, 240);height: 40px;line-height: 40px;color: white;text-align: center;}.cart .item{/* 子绝对位置父相对位置 */position: relative;height: 55px;line-height: 55px;border-top: 1px dashedrgb(116, 21, 103);}.cart .item:first-child{border-top: none;}.cart .item img{width: 45px;height: 45px;margin: 5px;}.cart .item .name{position: absolute;left: 55px;top: 0;font-size: 16px;}.cart .item .change{position: absolute;top: 0;right: 50px;width: 100px;}.cart .item .change a{font-size: 20px;text-decoration: none;display: inline-block;height: 25px;width: 20px;line-height: 25px;background-color: lightgray;text-align: center;vertical-align: middle;}.cart .item .change .num{width: 40px;height: 25px;}.cart .item .del{position: absolute;top: 0;right: 0;width: 50px;font-size: 28px;text-align: center;color: red;}.cart .total{background-color:rgb(116, 21, 103);height: 50px;line-height: 50px;text-align: right;color: white;}.cart .total .account{margin: 0 10px;}</style></head><body><divid="app"><!--将组件渲染到页面上,定义组件名时,如果是用的驼峰命名,页面这里的组件名用短横线连接两个小写单词。--><my-cart></my-cart></div><scriptsrc="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script>const cartTitle={//props:["uname"],//注意:template创建的模板有且只有一个根元素。template:`
            <div class="title">{{uname}}的商品</div>`,};const cartList={props:["list"],//click.prevent阻止默认行为template:`
            <div>
                <div class="item" v-for="ii in list" :key="ii.id">
                    <img :src="ii.img">
                    <div class="name">{{ii.name}}</div>
                    <div class="change">
                      
                        <a href="" @click.prevent="$emit('change-num',{id:ii.id,type:'sub'})">-</a>
                        <input type="text" class="num" v-model="ii.num" @blur="$emit('change-num',{id:ii.id,type:'edit',num:$event.target.value})">
                        <a href="" @click.prevent="$emit('change-num',{id:ii.id,type:'add'})">+</a>
                    </div>
                    <div class="del" @click="$emit('del',ii.id)">X</div>
                </div>
            </div>`,};const cartTotal={props:["list"],template:`
            <div class="total">
              <span>总价:{{total}}</span>
              <input type="button" value="结算" class="account">
            </div>`,//计算属性,进行累加和computed:{total(){returnthis.list.reduce((sum, item)=>{return item.num* item.price+ sum;},0);},},};//定义全局组件
      Vue.component("myCart",{template:`
            <div class="cart">
                <cart-title :uname="uname"></cart-title>
                <cart-list :list="goods" @change-num='changeNum' @del="delItem"></cart-list>
                <cart-total :list="goods"></cart-total>
            </div>`,components:{
          cartTitle,
          cartList,
          cartTotal,},methods:{delItem(id){
            console.log("del");this.goods=this.goods.filter(item=>{return item.id!==id;})},//先找到id,changeNum(arg){
            console.log(arg);let id=arg.id;let type=arg.type;if(type==='add'){this.goods.some(item=>{if(item.id===id){
                  item.num++;returntrue;}})}elseif(type==='sub'){this.goods.some(item=>{if(item.id===id){if(item.num>=2){
                    item.num--;}else{//删除this.delItem(id);}returntrue;}})}elseif(type==='edit'){this.goods.some(item=>{if(item.id===id){
                  item.num=arg.num;returntrue;}})}}},data(){return{uname:"张三",goods:[{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",},],};},});const vm=newVue({el:"#app",});</script>
  • 作者:学习中!
  • 原文链接:https://blog.csdn.net/m0_63845261/article/details/123977268
    更新时间:2022-10-01 14:05:14