1、父传子
父:自定义属性名 + 数据(要传递)=> :value="数据"
子:props ["父组件上的自定义属性名"] =>进行数据接收
父级调用子组件,通过子组件的属性传入数据
子元素内部通过props配置(数组),来接受对应的数据,如props:['属性1','属性2',...]
props
选项接收。props
:使用存储数据,组件内部使用数据的方式与data的一样data(){ return { 属性名:属性值 } }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>组件传参-父传子</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- vue实例中的属性 -->
<p v-text="name2"></p><hr>
<!-- 父传参给子 ,使用v-bind 绑定属性的方式 ,
下面的pages与num是绑定的自定义属性-->
<my-component :pages="10" :num="100"></my-component>
<!--使用v-bind指令绑定对象或数组的值-->
<my-component v-for="elf in arrs" :key="elf.id" v-bind:username="elf.username"></my-component>
</div>
<script>
// 自定义全局组件
Vue.component("my-component",{
props:["pages","num","username"],
template:`
{{name1}}
{{pages}}
{{num}}
{{username}}
`,
// data必须写成函数,使各组件之间空间独立,数据互不影响
data(){
return {
// name1是自身的属性
name1:"我是自身的属性值啊"
}
}
});
let app = new Vue({
el:"#app",
data:{
// 子组件的属性不能与实例中的属性名相同
// 这里的name是实例属性
name2:"我是实例属性值",
arrs:[
{id:1,username:"皮卡丘"},
{id:2,username:"喷火龙"},
{id:3,username:"可达鸭"}
]
},
// methods:{
// getName(){
// return this.name;
// }
// }
});
</script>
</body>
</html>
注意:
2、子传父
子:this.$emit('自定义事件名称', 数据)
子组件标签写法: <子组件名 @自定义事件名称='回调函数'></子组件名>
父:methods: {
回调函数() {
//处理数据
}
}
注意:
vue中的数据默认是单向流动,只能父到子直接传递,但是子到父不能直接修改;
原因:父级的数据,不一定只是某个子级使用,可能还有其他子级也在使用,
如果一个子级内部随意去修改了父级的数据,很容易导致数据混乱。
(这样语法没有错误,控制台不会报错,出来的不是自己想要的数,排查起来很麻烦)
所以,如果子级想修改数据
:
子级在特定条件下,触发自定义事件来通知父级,父级通过监听接收到这个通知后,
自己决定是否改变数据或改变多少
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>vue组件传参-子传父</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- v-on:自定义事件名称="函数名称" 或者 @事件名称="函数名称"
这个事件名称的字母必须要小写,或者使用-隔开小写
-->
<!-- 绑定自定义点击事件hello ,点击事件一触发,就触发了父级监听函数fn
注意:这里举例是自定义点击事件,自定义事件类型很多,不限于点击事件-->
<div-child @hello="fn"></div-child>
<!-- <div-child @hello-nice="fn"></div-child> -->
<!-- 子元素传参过来的name -->
<p>我是子元素传过来的<b>{{name}}</b></p>
</div>
<script>
Vue.component('div-child',{
data(){
return {
// 子组件传来的属性
name:"小精豆"
}
},
// // 如果只让父级触发一个事件,不去处理它,事件函数写在这里
// template:`
// 第一
// 第二
//
// `
// 模板中的元素自定义事件,可以写表达式
// 子组件中元素的点击事件触发methods中的函数
template:`
第一
第二
`,
methods:{
go(){
// $emit("自定义事件名称",数据);
this.$emit("hello",this.name);
// 自定义事件名如果有多个字母,比较长的,使用`-`连接命名
// this.$emit("hello-nice",this.name);
}
}
});
let app = new Vue({
el:"#app",
data:{
// 子元素传来的参数
name:""
},
methods:{
// 父级监听事件 回调函数fn
// fn(){
// console.log("我是父级派来监听事件的");
// }
fn(n){
// 按条件触发函数,如下,如果传来的数据是对象,不触发
// if (n==Object) return;
console.log(n);
this.name = n;
}
}
});
</script>
</body>
</html>
好了,来看看利用组件传参,去实现的分页案例,如下:
.pagination a{
width: 50px;
margin-left: 5px;
display: inline-block;
text-align: center;
text-decoration: none;
border: 1px solid lightgreen;
}
.pagination a.ago{
width: 26px;
}
.pagination a.active{
background-color: lightblue;
color:white;
}
<div id="app">
<ul id="ulist">
<li v-for="user of showUsers" :key="user.id">
{{user.username}}
li>
ul>
<partpage :pages="totalpage" :page="curpage" @change="changepage">partpage>
div>
Vue.component("partpage",{
// 属性:总页数pages,当前页page
props:["pages","page"],
// 阻止a标签的默认事件(点击a标签后自动刷新页面): @click.prevent="执行事件的函数"
// 给显示页码的a标签绑定一个类active,
// 这个类中右指定的样式,如果当前页数在它自身所在的a标签,
// 即page===p为true时,就改变它的样式 ,否则还是原来的样式
template:``,
methods:{
// 改变当前页数(当用户点击当前页数时,触发点击事件gotopage)
gotopage(p){
// 组件传参: 子传父
this.$emit("changepage",p);
},
// 上一页
prev(){
if(this.page>1){
this.$emit("changepage",this.page-1);
}
},
// 下一页
next(){
if(this.page<this.totalpage){
this.$emit("changepage",this.page+1);
}
}
}
});
let app = new Vue({
el:"#app",
data:{
// 准备一些数据,做分页效果的演示使用(实际应用中是从数据库中获取的)
users:[
{id:1,username:"汤姆"},
{id:2,username:"约翰"},
{id:3,username:"玛丽"},
{id:4,username:"杰克"},
{id:5,username:"露丝"},
{id:6,username:"萨利"},
{id:7,username:"乔治"},
{id:8,username:"罗娜"},
],
// 测试v-for指令使用的
// nums:10
// 当前页数 (用户点击页码,显示当前页数是多少)
curpage:1,
// 每页显示多少条数据
pagesize:2,
// 总页数 (根据总数据的条数决定的,计算得出,不需要直接设置)
},
// computed存放属性,存放计算后的数据
computed:{
// 计算总页数
// totalpage:{ get(){} }
// 下面是computed中get方法的简写方式 ,totalpage是属性不是函数
totalpage(){
// 总页数=数据总条数/每页显示的数据条数
// Math.ceil()向上取整,当每页数据不足3条时,翻到下一页显示
return Math.ceil(this.users.length/this.pagesize);
},
// 获取每页显示的数据
showUsers(){
// 每页显示的数据起点(即每一页从原数据中截取数据的起始位置)
let start = (this.curpage-1)*this.pagesize;
// 从总数据中截取需要的数据
// slice(开始,结束):截取数组中指定范围,并将其返回(多次使用时定义新数组来接收)
return this.users.slice(start,start+this.pagesize);
/* 假如当前页是1 (1-1)*3 = 0 --> [0,3)
2 (2-1)*3 = 3 --> [3,5)
3 (3-1)*3 = 6 --> [5,7)
... ... ...
*/
}
},
methods:{
// 父组件来处理触发的监听函数changpage
changepage(page){
// console.log("子组件传过来的数据"+page);
// 如果需要在满足一定条件下去处理,则如右 if(条件){ // 处理代码 }
this.curpage = page;
}
}
});