Vue2:组件及组件通信

Vue 组件

组件是 可复用的 Vue 实例,封装标签,样式和 JS 代码。

  • 组件化 :封装的思想,把页面上 可重用的部分 封装为 组件,从而方便项目的 开发 和 维护。

简单理解:一个页面, 可以拆分成一个个组件,一个组件就是一个整体,每个组件可以有自己独立的 结构 样式 和 行为 (html, css和js)

组件的创建使用

创建组件:创建.vue文件 – 标签 – 样式 – JS进去

注册组件:(全局 / 局部)

使用组件:(组件名用作标签)

执行结果:把组件标签最终替换成封装组件内的标签。

  1. 创建组件 (components 下的 Pannel.vue)

    <template>
      <div>
        <div>
          <div class="title">
            <h4>芙蓉楼送辛渐</h4>
            <span class="btn" @click="isShow = !isShow">
              {{ isShow ? '收起' : '展开' }}
            </span>
          </div>
          <div class="container" v-show="isShow">
            <p>寒雨连江夜入吴, </p>
            <p>平明送客楚山孤。</p>
            <p>洛阳亲友如相问,</p>
            <p>一片冰心在玉壶。</p>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      // 数据
      data () {
        return {
          isShow: false
        }
      }
    }
    </script>
    
    <style lang="less" scoped>
      .title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        border: 1px solid #ccc;
        padding: 0 1em;
        }
        .title h4 {
        line-height: 2;
        margin: 0;
        }
        .container {
        border: 1px solid #ccc;
        padding: 0 1em;
        }
        .btn {
        /* 鼠标改成手的形状 */
        cursor: pointer;
        }
    </style>
    
  2. 局部注册组件,并使用 (App.vue)

    <template>
      <div id="app">
        <h3>案例:折叠面板</h3>
        <!-- 3.使用组件 -->
        <pannel></pannel>
        <pannel></pannel>
        <pannel></pannel>
      </div>
    </template>
    
    <script>
    // 2. 注册组件
    import Pannel from './components/Pannel'
    export default {
      components: {
        Pannel
      },
    }
    </script>
    
    <style lang="less" scoped>
      body {
        background-color: #ccc;
        #app {
          width: 400px;
          margin: 20px auto;
          background-color: #fff;
          border: 4px solid blueviolet;
          border-radius: 1em;
          box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
          padding: 1em 2em 2em;
          h3 {
            text-align: center;
          }
        }
      }
    </style>
    

Vue 组件通信

App.vue

<template>
	<div>
    <!-- 传入数据 -->
    <MyProduct title="俺是标题" price="100" info="俺是描述,这玩意儿真不错"></MyProduct>
  </div>
</template>

<script>
// 引入子组件
import MyProduct from './components/MyProduct.vue'

export default {
  // 注册组件
  components: {
    MyProduct,
  },
};
</script>

MyProduct.vue

<template>
	<div class="my-product">
		<h3>标题: {{ title }}</h3>
		<p>价格: {{ price }}元</p>
		<p>{{ info }}</p>
	</div>
</template>

<script>
export default {
	props: ["title", "price", "info"],
};
</script>

<style scoped>
.my-product {
	width: 400px;
	padding: 20px;
	border: 2px solid #000;
	border-radius: 5px;
	margin: 10px;
}
</style>

父向子 - props

指定类型,是否必传

props: {
	title: {
    	typeof: String,
    	require: true
    },
}

父向子 - 组件循环

App.vue

<template>
  <div>
    <MyProduct v-for="v in list"
              :title="v.proname"
              :price="v.proprice"
              :info="v.info"
              :key="v.id">
    </MyProduct>
  </div>
</template>

<script>
import MyProduct from './components/MyProduct.vue'

export default {
  // 数据
  data () {
    return {
      list: [
          { id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: '开业大酬宾, 全场8折' },
          { id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: '好吃不腻, 快来买啊' },
          { id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: '炎热的夏天, 来个冰激凌了' },
      ],
    }
  },
  // 注册组件
  components: {
    MyProduct,
  }
}
</script>

MyProduct.vue

<template>
	<div class="my-product">
		<h3>标题: {{ title }}</h3>
		<p>价格: {{ price }}元</p>
		<p>{{ info }}</p>
	</div>
</template>

<script>
export default {
	props: ["title", "price", "info"],
};
</script>

<style scoped>
.my-product {
	width: 400px;
	padding: 20px;
	border: 2px solid #000;
	border-radius: 5px;
	margin: 10px;
}
</style>
子向父

单项数据流

简单理解:父组件向子组件发送事件,子组件触发。

触发父组件的自定义方法

this.$emit("", 参数1, 参数2);

EventBus(兄弟通信)

跨组件通信使用。

在监听方(List)接收,发送方(MyProduct)发送。

  1. App.vue 父组件中写主体代码

    <template>
      <div>
        <div style="float: left;">
          <Product v-for="(v, i) in list" :key="v.id"
            :title="v.proname"
            :price="v.proprice"
            :info="v.info"
            :index="i">
          </Product>
        </div>
    
        <div style="float: left;">
          <List :arr="list"></List>
        </div>
    
      </div>
    </template>
    
    <script>
    // 1.导入组件
    import Product from './components/MyProduct.vue'
    import List from './components/List.vue'
    
    export default {
      // 2.注册组件
      components: {
        Product,
        List
      },
      // 数据
      data () {
        return {
          price: 1,
          list: [
            {id: 1, proname: "超级好吃的棒棒糖", proprice: 18.8, info: "开业大酬宾, 全场8折",},
            {id: 2, proname: "超级好吃的大鸡腿", proprice: 34.2, info: "好吃不腻, 快来买啊",},
            {id: 3, proname: "超级无敌的冰激凌", proprice: 14.2, info: "炎热的夏天, 来个冰激凌了",},
          ],
        }
      },
    }
    </script>
    
  2. MyProduct.vue 外部组件中使用

    <template>
    	<div class="my-product">
    		<h3>标题: {{ title }}</h3>
    		<p>价格: {{ price }}元</p>
    		<p>{{ info }}</p>
        	<button @click="subFn">宝刀-砍1元</button>
    	</div>
    </template>
    
    <script>
    import eventBus from '../EventBus/index.js'
    
    export default {
    	props: ["title", "price", "info", "index"],
    	methods: {
    		subFn () {
    			// 发送
    			eventBus.$emit('subPrice', this.index, 1);
    		}
    	}
    };
    </script>
    
    <style scoped>
    .my-product {
    	width: 400px;
    	padding: 20px;
    	border: 2px solid #000;
    	border-radius: 5px;
    	margin: 10px;
    }
    </style>
    
  3. 在 src 下创建 EventBus 文件夹,下新建 index.js,并创建空白 Vue 对象并导出 (只负责监听和触发)

    import Vue from 'vue'
    export default new Vue();
    
  4. 在 components 文件夹下新建 List.vue

    箭头函数没有 this,可以直接指向

    <template>
    	<div>
    		<ul class="my-product">
    			<li v-for="(item, index) in arr" :key="index">
    				<span>{{ item.proname }}</span>
    				<span>{{ item.proprice }}</span>
    			</li>
    		</ul>
    	</div>
    </template>
    
    <script>
    import eventBus from "../EventBus/index.js";
    
    export default {
    	props: ["arr"],
    	// 生命周期 函数  vue创建完毕以后调用created
    	// 	created :处于loading结束后,还做一些初始化,实现函数自执行(data数据已经初始化,但是DOM结构渲染完成,组件没有加载)
    	// 	mounted :处于发起后端请求,获取数据,配合路由钩子执行操作(DOM渲染完成,组件挂载完成 )
    	created() {
    		// 接收(监听)
    		eventBus.$on("subPrice", (index, price) => {
    			if (this.arr[index].proprice > 1) {
    				this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2);
    			}
    		});
    	},
    };
    </script>
    
    <style scoped>
    .my-product {
    	width: 400px;
    	padding: 20px;
    	border: 2px solid #000;
    	border-radius: 5px;
    	margin: 10px;
    }
    </style>
    

传数据注意事项

数据在父组件中,修改父组件数据时

  • 如果数据类型是一般类型,要在父组件中修改
  • 如果数据类型的复杂类型,可以直接在自组件中修改

因为指针问题,复杂类型访问的是指针。不管是什么类型的数据,都推荐在父组件中修改数据。


案例:Todo 备忘录

Todo案例完成参考.zip

  1. 创建工程和组件

    创建组件 todoHeader.vue、todoMain.vue、todoFooter.vue

    todoHeader.vue

    <template>
      <header class="header">
        <h1>todos</h1>
        <input id="toggle-all" class="toggle-all" type="checkbox" >
        <label for="toggle-all"></label>
        <input
          class="new-todo"
          placeholder="输入任务名称-回车确认"
          autofocus
        />
      </header>
    </template>
    
    <script>
    export default {
     
    }
    </script>
    

    todoMain.vue

    <template>
      <ul class="todo-list">
        <!-- completed: 完成的类名 -->
        <li class="completed" >
          <div class="view">
            <input class="toggle" type="checkbox" />
            <label>任务名</label>
            <button class="destroy"></button>
          </div>
        </li>
      </ul>
      
    </template>
    
    <script>
    export default {
        
    }
    </script>
    

    todoFooter.vue

    <template>
      <footer class="footer">
        <span class="todo-count">剩余<strong>数量值</strong></span>
        <ul class="filters">
          <li>
            <a class="selected" href="javascript:;" >全部</a>
          </li>
          <li>
            <a href="javascript:;">未完成</a>
          </li>
          <li>
            <a href="javascript:;" >已完成</a>
          </li>
        </ul>
        <button class="clear-completed" >清除已完成</button>
      </footer>
    </template>
    
    <script>
    export default {
    
    }
    </script>
    
  2. 导入组件和样式

    App.vue

    <template>
      <div class="todoapp">
        <TodoHeader></TodoHeader>
        <TodoMain></TodoMain>
        <TodoFooter></TodoFooter>
      </div>
    </template>
    
    <script>
    import './styles/base.css'
    import './styles/index.css'
    import TodoHeader from './components/todoHeader.vue'
    import TodoMain from './components/todoMain.vue'
    import TodoFooter from './components/todoFooter.vue'
    
    export default {
      components: {
        TodoHeader,
        TodoMain,
        TodoFooter
      },
    }
    </script>
    
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤安先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值