Vue学习-组件中的数据通信
目录
组件可以访问Vue实例数据吗?
-
组件是一个单独功能模块的封装,有属于自己的HTML模板,也有自己的数据data
-
组件中不能直接访问Vue实例里的数据,以下代码在组件是不能访问到message的
<div id="app">
<hello></hello>
</div>
<template id="myId">
<div>消息内容:{{message}}</div>
</template>
<script src="vue.js"></script>
<script>
let app=new Vue({
el:"#app",
data:{
message:"hello world"
},
components:{
'hello':{
template:"#myId"
}
}
})
</script>
组件的data参数
- 组件定义时也有一个data参数,但这个data参数不像Vue实例的data是一个对象类型,它这里是一个函数类型,如果你定义成了一个对象类型,会报以下错误
- 该data函数需要返回一个对象,对象内部保存着数据
- 示例代码:https://jsrun.net/EevKp/edit
const cpnC = Vue.extend({
template: "#cpn",
data(){
return {
title:"这里是标题吧",
content:"这里是内容哦!~~"
}
}
})
组件中的methods函数
- 用于定义一些事件处理的函数,同Vue实例里的methods函数类型
- 示例代码:https://jsrun.net/JevKp/edit
const cpnC = Vue.extend({
template: "#cpn",
data(){
return {
counter:0
}
},
methods:{
increment:function(){
this.counter++
},
decrement:function(){
this.counter--
}
}
})
父子组件
使用场景
- 比如一个商城的首页,上面有一个轮播图,下面有一个商品的列表
- 像这种页面,我们在开发时,会在最外层组件(父组件)里,向服务端一次请求到轮播图,商品列表数据,然后将数据分别传给轮播图组件,商品列表组件;然后商品列表组件在将数据传传商品组件来进行整个页面的渲染
如何通信(传递数据)
- Vue实例也可以看作一个父组件
父传子
- 通过
prots
向子组件传数据(prots
为proterties
的简写,意为属性
) - props的二种方式
1.一种是可以传一个字符串数组,里面第一项对应变量名
将godos和message传给子组件进行渲染,示例代码:http://jsrun.net/5PvKp/edit
<div id="app">
<my-cpn v-bind:cgoods="goods" :cmessage="message"></my-cpn>
</div>
<script type="text/x-template" id="cpn1">
<div>
<h2>{{cmessage}}</h2>
<ul>
<li v-for="item in cgoods">{{item}}</li>
</ul>
</div>
</script>
//1.Vue.extend创建组件
const cpnC = Vue.extend({
template: "#cpn1",
props:['cgoods','cmessage']
})
//2.注册组件
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: "#app",
data: {
message: "我是message",
goods:["衣服","鞋子","帽子","短裤"]
}
})
2.第二种是传一个对象类型,可以进行类型限制type
和默认值default
,是否必需reauired
type
-指定参数的类型,有:String,Array,Boolean,Number,Date,Function,Symbol
default
-指定默认值
reauired
-指定是否必须要传该参数,是boolean类型,传(true或false)
const cpnC = Vue.extend({
template: "#cpn1",
props: {
//1.类型限制
// cgoods: Array,
// cmessage: String
//2.提供默认值,没传时显示
cgoods:{
type:Array,
default:[]
},
cmessage:{
type:String,
default:"这里是默认值",
required:true
}
}
})
props
的一些使用示例
Vue.component('cnp',{
props:{
//1.基础的类型检查(null匹配任何类型)
propA:Number,
//2.多个可能的类型
propB:[String,Number],
//3.必填的字符串
propC:{
type:String,
required:true
},
//4.带有默认值的数字
propD:{
type:Number,
default:100
},
//5.默认值的对象,数组或对象的需要返回一个函数
propE:{
type:Object,
default:function(){
return {name:"hello"}
}
},
//6.带自定义验证函数
propF:{
validator:function(val){
//这个val必须匹配下列数组中的一个
return ['张三','李四','王五'].indexOf(val) !== -1
}
}
}
})
- 组件中驼峰标识,
props
中定义了cTitle
,但在组件使用是需要变为:c-title
,子组件中使用时又为cTitle
<div id="app">
<my-cpn :c-title="title"></my-cpn>
</div>
<script type="text/x-template" id="cpn1">
<div>
<h1>{{cTitle}}</h1>
</div>
</script>
//1.Vue.extend创建组件
const cpnC = Vue.extend({
template: "#cpn1",
props: {
//驼峰标识
cTitle:{
type:String
}
}
})
//2.注册组件
Vue.component('my-cpn', cpnC)
const app = new Vue({
el: "#app",
data: {
title:"我是驼峰标识"
}
})
子传父
- 通过
emit
发送自定义事件向父组件发送消息
示例代码:http://jsrun.net/J7vKp/edit
<div id="app">
<my-cpn @itemclick="btnClick"></my-cpn>
<h3>{{message}}</h3>
<image width="200px" :src="imgUrl"></image>
</div>
<script type="text/x-template" id="cpn1">
<div>
<button @click="btnclick(stu)" v-for="stu in students">
{{stu.name}}
</button>
</div>
</script>
//1.Vue.extend创建组件
const cpnC = Vue.extend({
template: "#cpn1",
data: function () {
return {
students: [
{ id: "001", name: "关晓彤",imgUrl:"https://5b0988e595225.cdn.sohucs.com/images/20181224/ecf125cb199d4808a067f682c6c5e6c6.jpeg" },
{ id: "002", name: "迪丽热巴",imgUrl:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593861542943&di=9fe9365124a4aae6cc6cf483aa2a9e18&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fzhidao%2Fwh%253D680%252C800%2Fsign%3Dbb541c4377f40ad115b1cfe56f1c3de7%2F50da81cb39dbb6fde349ce5d0524ab18972b37be.jpg" },
{ id: "003", name: "郑爽",imgUrl:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593861570612&di=60b6d39dcbac9e1ea77b747d42f9f19d&imgtype=0&src=http%3A%2F%2Ftc.sinaimg.cn%2Fmaxwidth.800%2Ftc.service.weibo.com%2Fmmbiz_qpic_cn%2F1ab918195e7c8a94cc6c95182fbde786.jpg" },
{ id: "004", name: "杨幂",imgUrl:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1593861759697&di=6771e7279c145513968ce221a29a6b0e&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20130816%2FImg384334041.jpg" }
]
}
},
methods: {
btnclick(item) {
//发射事件,自定义事件
this.$emit("itemclick", item)
}
}
})
const app = new Vue({
el: "#app",
components: {
"my-cpn": cpnC
},
data: {
message: "",
imgUrl:""
},
methods: {
btnClick: function (item) {
this.message = "你选择了:" + item.name + ",学号:" + item.id
this.imgUrl=item.imgUrl
}
}
})
组件中的双向绑定
- 不能直接绑定
props
里的属性,要绑定data里定义的属性 - 示例:http://jsrun.net/R7vKp/edit
<div id="app">
<my-cpn :number1="num"></my-cpn>
</div>
<script type="text/x-template" id="cpn">
<div>
<h2>{{dnumber}}</h2>
<input type="text" :value="dnumber" @input="numInput"></input>
</div>
</script>
//1.Vue.extend创建组件
const cpnC = Vue.extend({
template: "#cpn",
data(){
return {
dnumber:this.number1
}
},
props:{
number1:{
type:Number,
default:0
}
},
methods:{
numInput:function(event){
this.dnumber=event.target.value
}
}
})
const app = new Vue({
el: "#app",
components:{
'my-cpn':cpnC
},
data: {
num: 1
}
})
父组件访问子组件($children或$refs)
$children
-是一个数组,通过下标访问,这种方式不建议使用,因为组件变化维护后,相应的下标也要变化,后期难维护
示例代码:http://jsrun.net/VcvKp/edit
<div id="app">
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<button @click="btnClick">点击</button>
</div>
<script type="text/x-template" id="cpn">
<div>
<h2>{{title}}</h2>
</div>
</script>
const app = new Vue({
el: "#app",
methods:{
btnClick:function(){
//1.$children是一个数组
alert(this.$children[0].title)
},
},
components:{
'my-cpn':{
template:"#cpn",
data(){
return {
title:"我是子组件的标题"
}
}
}
}
})
$refs
-通过在组件上定义ref
进行访问
示例代码:http://jsrun.net/7cvKp/edit
<div id="app">
<my-cpn ref="aaa"></my-cpn>
<my-cpn ref="bbb"></my-cpn>
<button @click="btnClick">点击</button>
</div>
<script type="text/x-template" id="cpn">
<div>
<h2>{{title}}</h2>
</div>
</script>
const app = new Vue({
el: "#app",
methods:{
btnClick:function(){
//1.$children是一个数组
//alert(this.$children[0].title)
alert(this.$refs.aaa.title)
},
},
components:{
'my-cpn':{
template:"#cpn",
data(){
return {
title:"我是子组件的标题"
}
}
}
}
})
子组件访问父组件($parent)
- 子组件通过
$parent
访问父组件 $root
可以用来访问根组件,一般是Vue实例
示例代码:http://jsrun.net/dcvKp/edit
<div id="app" class="main">
<h2>{{message}}</h2>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script type="text/x-template" id="cpn">
<div class="cpn">
我是子组件
<button @click="btnClick">点击</button>
</div>
</script>
const app = new Vue({
el: "#app",
data: {
message: "我是Vue标题"
},
components: {
'my-cpn': {
template: "#cpn",
methods: {
btnClick: function () {
alert(this.$parent.message)
//访问根组件还可以使用$root,根组件一般是Vue实例
//我们这里的parent就等于根组件
//alert(this.$root.message)
}
}
}
}
})
组件的扩展-slot
基本使用
- 使组件具有更好的扩展性,引入了
slot
插槽的概念 - slot可以有默认值
- 示例代码:http://jsrun.net/DcvKp/edit
<div id="app" class="main">
<h2>{{message}}</h2>
<my-cpn>
<span>我是slot</span>
</my-cpn>
<my-cpn>
<button>我是slot按钮</button>
<sapn>我是slot span</sapn>
</my-cpn>
<my-cpn></my-cpn>
</div>
<script type="text/x-template" id="cpn">
<div class="cpn">
我是组件
<div class="div-slot">
<slot><i>我是slot的默认值</i></slot>
</div>
</div>
</script>
const app = new Vue({
el: "#app",
data: {
message: "我是Vue标题"
},
components: {
'my-cpn': {
template: "#cpn"
}
}
})
具名插槽
- 使用场景-一个组件中有需要多个插槽的情况
- 示例代码:http://jsrun.net/DcvKp/edit
<div id="app" class="main">
<h2>{{message}}</h2>
<my-cpn></my-cpn>
<my-cpn>
<span slot="center" style="color:red">center</span>
</my-cpn>
<my-cpn>
<span slot="right" style="color:red">right</span>
<span slot="left" style="color:red">right</span>
</my-cpn>
</div>
<script type="text/x-template" id="cpn">
<div class="cpn">
我是组件-具名插槽
<div class="div-slot">
<slot name="left"><i>左边</i></slot>
<slot name="center"><i>中间</i></slot>
<slot name="right"><i>右边</i></slot>
</div>
</div>
</script>
const app = new Vue({
el: "#app",
data: {
message: "我是Vue标题"
},
components: {
'my-cpn': {
template: "#cpn"
}
}
})