第四节:Vuejs组件及组件之间的交互
一. 组件及其交互
1.组件的注册
(1).全局注册
Vue.component('组件名称', { }) 第1个参数是标签名称,第2个参数是一个选项对象。
选项参数包括
data:必须是一个function,然后要return,在return里面声明变量。
template: 用``符号包裹
methods: 声明方法
注意事项:
A. 模板必须是单个根元素
B. 如果使用驼峰式命名组件,那么在使用组件的时候,只能在另一个组件字符串模板中用驼峰的方式使用组件,在普通标签中直接使用的话,必须加短横线。如: HelloWord模板,在body里用的时候必须<hello-word>,而在btn-counter组件中,则可以直接使用<HelloWord>
总之:不推荐使用驼峰命名,建议小写+短横线。
Vue.component('HelloWord', {
template: `<div>我是HelloWord组件</div>`
});
Vue.component('btn-counter', {
data: function() {
return {
count: 0
}
},
template: `
<div>
<div>{{count}}</div>
<button @click="handle">点击了{{count}}次</button>
<HelloWord></HelloWord>
</div>
`,
methods: {
handle: function() {
this.count += 3;
}
}
});
<p>1.全局组件</p>
<btn-counter></btn-counter>
<hello-word></hello-word>
(2).局部组件
在Vue实例中components进行声明局部组件,仅供该实例使用。
2.父组件向子组件传值
父组件发送形式是以属性的形式绑定在子组件上,可以直接往里传值,也可以用:符号进行绑定变量。然后子组件用属性props接收,props是一个数组。
注意事项:
A.在props使用驼峰形式,在页面中需要使用短横线的形式字符串, 而在字符串模板中则没有这个限制。
PS:这里面不演示了,总之在vue中不建议使用驼峰命名。
B.比如p3是props里声明的一个属性,在使用的时候 p3:'true' 这种情况p3是string类型; 而 :p3:'true',这种情况p3是布尔类型。
3.子组件向父组件传值
子组件用 $emit() 触发事件,第一个参数为 自定义的事件名称 第二个参数为需要传递的数据(这里最多只能传递一个参数,但他可以是一个对象哦,对象里面包括很多属性,如下:),父组件用v-on(或者@) 监听子组件的事件,这里用固定命名 $event 来获取子组件传递过来的参数。
this.$emit('change-num', {
id: id,
type: 'change',
num: 10
});
4.兄弟组件之间的交互
兄弟之间传递数据需要借助于事件中心,通过事件中心传递数据,提供事件中心 var hub = new Vue()。
(1).传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据),这里可以传递多个数据哦
(2).接收数据方,通过mounted(){} 钩子中 触发hub.$on()方法名,这里可以接收多个数据哦
(3).销毁事件 通过hub.$off()方法名销毁之后无法进行传递数据
5.插槽
组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力,在template中使用<slot>标签
(1).匿名插槽
使用时,组件标签中嵌套的内容(包含html)会替换掉slot; 如果不传值 ,则使用 slot 中的默认值。
(2).具名插槽
使用时,通过slot属性来指定, 这个slot的值必须和下面slot组件得name值对应上 如果没有匹配到 则放到匿名的插槽中
特别注意:具名插槽的渲染顺序,完全取决于模板中的顺序,而不是取决于父组件中元素的顺序!
(3).作用域插槽
A. 作用域插槽使用场景
a.父组件对子组件加工处理
b.既可以复用子组件的slot,又可以使slot内容不一致
B.作用域插槽的使用
a.子组件模板中,<slot>元素上有一个类似props传递数据给组件的写法msg="xxx
b.插槽可以提供一个默认内容,如果如果父组件没有为这个插槽提供了内容,会显示默认的内容。 如果父组件为这个插槽提供了内容,则默认的内容会被替换掉
完整代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>06-组件及交互</title>
<style type="text/css">
p {
font-size: 20px;
color: #0000FF;
font-weight: bold;
}
.current {
color: orange;
}
</style>
</head>
<body>
<div id="myApp">
<p>1.全局组件</p>
<btn-counter></btn-counter>
<hello-word></hello-word>
<p>2.局部组件</p>
<ypfzj1></ypfzj1>
<p>3.父组件向子组件传值</p>
<father-child p1="ypf1" :p2="p2" p3="true"></father-child>
<father-child p1="ypf1" :p2="p2" :p3="true"></father-child>
<p>4.子组件向父组件传值</p>
<div :style="{fontSize:myFontSize+'px'}">我是内容,等着被控制</div>
<child-father @exchangebig='handle1($event)'></child-father>
<p>5.兄弟组件相互交互</p>
<brother-one></brother-one>
<brother-two></brother-two>
<button @click="destoryHandle">销毁事件</button>
<p>6.1 匿名插槽</p>
<my-tips1>您超标了</my-tips1>
<my-tips1>用户名不正确</my-tips1>
<my-tips1></my-tips1>
<p>6.2 具名插槽</p>
<my-tips2>
<div slot='header'>我是header</div>
<div>我是内容1</div>
<div>我是内容2</div>
<div slot='footer'>我是footer</div>
</my-tips2>
<my-tips2>
<div slot='footer'>我是footer</div>
<div>我是内容1</div>
<div slot='header'>我是header</div>
<div>我是内容2</div>
</my-tips2>
<p>6.3 作用域插槽</p>
<my-tips3 :list='list'>
<template slot-scope='slotProps'>
<strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
<span v-else>{{slotProps.info.name}}</span>
</template>
</my-tips3>
</div> <script src="js/vue.min.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
//全局组件
Vue.component('HelloWord', {
template: `<div>我是HelloWord组件</div>`
});
Vue.component('btn-counter', {
data: function() {
return {
count: 0
}
},
template: `
<div>
<div>{{count}}</div>
<button @click="handle">点击了{{count}}次</button>
<HelloWord></HelloWord>
</div>
`,
methods: {
handle: function() {
this.count += 3;
}
}
});
//父组件向子组件中传值
Vue.component('father-child', {
props: ['p1', 'p2', 'p3'],
data: function() {
return {
msg: '我是用来看父组件向子组件中传值的'
}
},
template: `
<div>
<div>{{msg+"--"+p1+"--"+p2+"--"+p3}}</div>
<div>p3的类型为:{{typeof p3}}</div>
</div> `
}); //子组件向父组件传值
Vue.component('child-father', {
props: [],
data: function() {
return {
msg: '我是用来看父组件向子组件中传值的'
}
},
template: `
<div>
<button @click='$emit("exchangebig",5)'>增大测试1</button>
<button @click='$emit("exchangebig",10)'>增大测试2</button>
</div> `
});
//两个兄弟组件
//事件中心
var hub = new Vue();
Vue.component('brother-one', {
data: function() {
return {
num: 0
}
},
template: `
<div>
<div>borther1:{{num}}</div>
<div>
<button @click='handle'>控制兄弟brother2</button>
</div>
</div>
`,
methods: {
handle: function() {
//传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('yChild2Event', 2, 1);
}
},
//Dom创建后
mounted: function() {
//接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名)
hub.$on('yChild1Event', (val1, val2) => {
this.num += val1 + val2;
});
}
})
Vue.component('brother-two', {
data: function() {
return {
num: 0
}
},
template: `
<div>
<div>borther2:{{num}}</div>
<div>
<button @click='handle'>控制兄弟brother1</button>
</div>
</div>
`,
methods: {
handle: function() {
//传递数据方,通过一个事件触发hub.$emit(方法名,传递的数据) 触发兄弟组件的事件
hub.$emit('yChild1Event', 4, 3);
}
},
//Dom创建后
mounted: function() {
//接收数据方,通过mounted(){} 钩子中 触发hub.$on(方法名)
hub.$on('yChild2Event', (val1, val2) => {
this.num += val1 + val2;
});
}
})
//匿名插槽
Vue.component('my-tips1',{
template:`
<div>
<strong>提醒:</strong>
<slot>默认内容</slot>
<slot>默认内容1</slot>
</div>
`,
})
//具名插槽
Vue.component('my-tips2', {
template: `
<div>
<div>我是具名插槽</div>
<slot name='header'></slot>
<slot></slot>
<slot name='footer'></slot>
</div>
`
});
//作用域插槽
Vue.component('my-tips3', {
props: ['list'],
template: `
<div>
<li :key='item.id' v-for='item in list'>
<slot :info='item'>{{item.name}}</slot>
</li>
</div>
`
}); //放在下面的局部组件里
var ypfzj1 = {
data: function() {
return {
msg: '我是局部组件1'
}
},
template: '<div>{{msg}}</div>'
}; //Vm实例
var vm = new Vue({
el: '#myApp',
data: {
p2: 'ypf2',
myFontSize: 12,
list: [{
id: 1,
name: 'apple'
},{
id: 2,
name: 'orange'
},{
id: 3,
name: 'banana'
}]
},
methods: {
handle1: function(val) {
this.myFontSize += val;
},
//销毁事件
destoryHandle: function() {
hub.$off('yChild1Event');
hub.$off('yChild2Event');
}
},
components: {
'ypfzj1': ypfzj1,
}
});
</script>
</body>
</html>
运行效果:
二. 购物车案例
1.需求分析
实现购物车商品列表的加载,数量的增加和减少、物品的删除、及其对应总价的变化。
效果图如下:
2. 实战演练
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件之购物车案例</title>
<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>
</html>
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
第四节:Vuejs组件及组件之间的交互的更多相关文章
- 2017-12-15python全栈9期第二天第四节之格式化输出%s和用户交互个人简历模板
#!/user/bin/python# -*- coding:utf-8 -*-name = input('姓名:')age = input('年龄:')job = input('工作:')hobbi ...
- Angular开发实践(四):组件之间的交互
在Angular应用开发中,组件可以说是随处可见的.本篇文章将介绍几种常见的组件通讯场景,也就是让两个或多个组件之间交互的方法. 根据数据的传递方向,分为父组件向子组件传递.子组件向父组件传递及通过服 ...
- vuejs 单文件组件.vue 文件
vuejs 自定义了一种.vue文件,可以把html, css, js 写到一个文件中,从而实现了对一个组件的封装, 一个.vue 文件就是一个单独的组件.由于.vue文件是自定义的,浏览器不认识,所 ...
- Vuejs——(12)组件——动态组件
版权声明:出处http://blog.csdn.net/qq20004604 目录(?)[+] 本篇资料来于官方文档: http://cn.vuejs.org/guide/components ...
- vue02—— 动画、组件、组件之间的数据通信
一.vue中使用动画 文档:https://cn.vuejs.org/v2/guide/transitions.html 1. Vue 中的过渡动画 <!DOCTYPE html> < ...
- [Vue]子组件与父组件之间传值
1.父组件与子组件传值props 1.1定义父组件,父组件传递 inputText这个数值给子组件: //父组件 //引入的add-widget组件 //使用 v-bind 的缩写语法通常更简单: & ...
- 实验四 CC2530平台上UART组件的TinyOS编程
实验四 CC2530平台上UART组件的TinyOS编程 实验目的: 加深和巩固学生对于TinyOS编程方法的理解和掌握 让学生初步掌握CC2530的UART.及其TinyOS编程方法 学生通过本实验 ...
- Vue组件之间数据交互与通信
Vue 的组件作用域都是孤立的,不允许在子组件的模板内直接引用父组件的数据.必须使用特定的方法才能实现组件之间的数据传递. 一.父组件向子组件传递数据 在 Vue 中,可以使用 props 向子组件传 ...
- Vuejs - 单文件组件
为什么需要单文件组件 在之前的实例中,我们都是通过 Vue.component 或者 components 属性的方式来定义组件,这种方式在很多中小规模的项目中还好,但在复杂的项目中,下面这些缺点就非 ...
随机推荐
- 转 C#中哈希表(HashTable)的用法详解
看了一遍有关哈希表的文字,作者总结的真是不错 .收藏起来 1. 哈希表(HashTable)简述 在.NET Framework中,Hashtable是System.Collections命名空间提 ...
- 2019牛客暑期多校训练营(第三场) J LRU management 模拟链表操作
输入n, m,n表示n种操作,m表示最多可以容纳m个串. 第一种操作:先在容器里找是否存在这个串,如果不存在,则添加在末尾,这个串携带了一个值v. 如果存在,则先把之前存在的那个拿出来,然后在后面添加 ...
- Git - 04. git 缓存
1. 概述 简单描述, 已经被 暂存 过的文件的 操作 和 生命周期 在 缓存区 中的声明周期 暂时只与 缓存区 做交互 2. 文件生命周期 略 这个之前讲过 3. 命令 1. stage 文件 概述 ...
- ALSA driver --PCM 实例创建过程
前面已经写过PCM 实例的创建框架,我们现在来看看PCM 实例是如何创建的. 在调用snd_pcm_new时就会创建一个snd_pcm类型的PCM 实例. struct snd_pcm { struc ...
- 【Vue组件系统】
目录 全局组件 局部组件 注册 子组件的用法 父子组件的通讯 子父组件的通讯 非父子组件的通讯 混入 插槽 具名插槽 使用组件的注意事项 使用组件实现导航栏 "vue.js既然是框架,那就不 ...
- 用python实现文件加密功能
生活中,有时候我们需要对一些重要的文件进行加密,Python 提供了诸如 hashlib,base64 等便于使用的加密库. 但对于日常学习而言,我们可以借助异或操作,实现一个简单的文件加密程序,从而 ...
- RedHat7.0 网络源的配置
RedHat7.0 yum源的配置 写这篇随笔的目的 为了记录自己的操作,能够为以后的再次配置提供参考 因为网上的教程都有些年代了,为了方便其他同学方便配置 提高下自己的写作水平 参考资料 RedHa ...
- Android学习06
从开始学习Android,已经第5天,今天我换了一种方式,去观望了观望别人以及大佬的博客园,今天来做一下对比和反思,以便为后期的努力方向做好更充足的准备.在这里不得不佩服一下大佬们的学习能力和自我控制 ...
- Fluent_Python_Part4面向对象,10-seq-hacking,序列的修改、散列和切片
第四部分第10章,序列的修改.散列和切片 中文电子书P423 这一章接第1章.第9章,以第9章定义的Vector2d类为基础,定义表示多为向量的Vector类.这个类的行为与Python中标准的不可变 ...
- C#中equals和==的区别有哪些
本文导读:C# 中==是用来判断变量的值是否相等,相等返回true,不相等返回false.Equals是用来判断两个对象(除string类型外)是否相等,相等的 条件是:值,地址,引用全相等,因为St ...