vue组件通信(props、$emit、$refs和is)
父传子props
使用组件的props属性实现父组件给子组件传数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<ul>
<li v-for="(item,index) in cmovies" :key="item">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data(){
return{
message:"你好",
movies:["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
}
},
components:{
cpn:{
template:"#cpn",
// props:['cmovies', 'cmessage'],//数组写法
props:{
cmessage:{
type:String,//传的数据类型
required:true//在使用组件必传值
},
cmovies:{
type: Array,
},
}
}
}
})
</script>
</body>
</html>
这里父组件给子组传了一个字符message、一个数组movies。
子传父$emit
子组件向父组件传值,使用自定义事件 $emit
<!-- 父组件 -->
<div id="app">
<!-- <cpn @itemclick="cpnclick"></cpn> -->
<!-- 子组件传的数据itemclick,父组件使用cpnclick方法进行使用,这里不用填参数 -->
<cpn @itemclick="cpnclick(arguments)"></cpn>
<!-- 这里使用arguments接受数据 -->
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<button v-for="(item,index) in categoties" :key="item.id" @click="btnClick(item,index)">{{item.name}}</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data() {
return {}
},
watch: {
},
methods: {
cpnclick(arguments){
console.log(arguments)
console.log(arguments[0])//item
console.log(arguments[0].name)
console.log(arguments[1])//index
// console.log('cpnClick'+item.name+'---'+index)
}
},
components: {
cpn: {//创建局部子组件
template: "#cpn",
data() {
return {
categoties: [{
id: 'aaa',
name: '热门推荐'
},
{
id: 'bbb',
name: '手机数码'
},
{
id: 'ccc',
name: '家用家电'
},
{
id: 'ddd',
name: '电脑办公'
},
]
}
},
methods: {
btnClick(item,index) {
this.$emit('itemclick', item,index)//传值给父组件
}
}
},
}
})
</script>
1.在子组件中定义一个方法btnClick(item)
,使用$emit
,'itemclick’是事件名,item
是传过去的值。在子组件中监听点击事件并回调此方法;
2.在父组件中定义一个方法cpnClcik(item) ,并在父组件(vue实例)中调用<cpn @itemclick="cpnClcik"></cpn>
(不写参数默认传递btnClick的item ),父组件监听事件名为itemclick
的子组件传过来的事件;
父子组件的值双向绑定
这里主要使用watch进行监听
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<cpn :number1="num1" :number2="num2" @num1change="num1Change" @num2change="num2Change"></cpn>
<hr>
<h2>父组件num1:{{num1}}</h2>
<input type="text" v-model="num1">
<h2>父组件num2:{{num2}}</h2>
<input type="text" v-model="num2">
<hr >
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<h2>number1:{{number1}}</h2>
<h2>datanum1:{{datanum1}}</h2>
<input type="text" v-model="datanum1">
<!-- 子组件不能直接使用props内的值 -->
<h2>number2:{{number2}}</h2>
<h2>datanum2:{{datanum2}}</h2>
<input type="text" v-model="datanum2">
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data() {
return {
num1: 1,
num2: 2
}
},
methods: {
//接收子组件传来的值后,赋给num
num1Change(value) {
this.num1 = value
},
num2Change(value) {
this.num2 = value
},
},
components: {
cpn: {
template: "#cpn",
data() {
return {
datanum1: this.number1, //子组件同样要加this
datanum2: this.number2
}
},
props: {
number1: {
type: [String, Number]
},
number2: {
type: [String, Number]
},
},
watch: {
//监听datanum的值,发生改变时,传给父组件
datanum1(newValue) {
this.$emit('num1change', newValue)
},
datanum2(newValue) {
this.$emit('num2change', newValue)
},
//监听number即num的值,发生改变时,改变datanum
number1(newValue) {
this.datanum1 = newValue
},
number2(newValue) {
this.datanum2 = newValue
}
}
},
}
})
</script>
</body>
</html>
父子组件的值双向绑定:
1.父组件app下num1、num2用props传给子组件cpn,cpn用number1、number2接收;
2.cpn用data把number1、number2赋给datanum1、datanum2(因子组件不能直接操作props内的值),而后把datanum1、datanum2与input输入框用v-model双向绑定;
3.在cpn中在watch下监听datanum1、datanum2,一旦变化,则用$emit()传给父组件app,app使用num1change、num2change接收,赋给num1、num2;而后回到第1步,第1步执行完后,number1、number2发生变化会触发cpn下的watch监听number1、number2,再将其赋值给datanum1、datanum2,改变了datanum1、datanum2。
4.最终形成闭环,改变任一方的值,都会牵动其他值的改变
父访问子 ref
$refs方式:ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
ref的基本使用 用在元素上
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<p ref="p" @click="handelClick" id="ppp">hello</p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
},
methods: {
handelClick(){
console.log(this.$refs.p);
//两种方法都可获取p标签
const ppp = document.querySelector('#ppp')
console.log(ppp);
}
},
})
</script>
</body>
</html>
ref在子组件上的使用
ref 可以调用组件中的数据
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<counter ref="one" @change="handelChange"></counter>
<counter ref="two" @change="handelChange"></counter>
<div>total:{{total}}</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
Vue.component('counter',{
template:'<div @click="handelclick">{{number}}</div>',
data(){
return {
number:0
}
},
methods:{
handelclick(){
this.number++;
this.$emit('change');
}
}
})
const app = new Vue({
el: "#app",
data: {
total:0
},
methods: {
handelChange(){
this.total = this.$refs.one.number + this.$refs.two.number
}
},
})
</script>
</body>
</html>
ref 可以调用组件中的方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<hello-world ref="hello"></hello-world>
<button @click="getHello">获取helloworld组件中的值</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
Vue.component('helloWorld',{
template:`<div>helloWorld</div>`,
data(){
return {
number:0
}
},
methods:{
handelClick(){
console.log('我是子组件的方法');
}
}
})
const app = new Vue({
el: "#app",
data: {
},
methods: {
getHello(){
console.log(this.$refs.hello.handelClick)//子组件方法
console.log(this.$refs.hello.$el.innerHTML);//helloWorld
}
},
})
</script>
</body>
</html>
is用于动态组件且基于 DOM 内模板的限制来工作。
基于 DOM 内模板的限制来工作
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>is基于 DOM 内模板的限制来工作</title>
</head>
<body>
<div id="app">
<table>
<tr is="row">
</tr>
<!-- <row></row> 这样写tr会在table外面-->
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
Vue.component('row',{
template:'<tr><td>111</td></tr>'
})
const app = new Vue({
el: "#app",
data() {
return {}
},
})
</script>
</body>
</html>
动态组件component
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<!-- <child-one></child-one>
<child-two></child-two> -->
<component :is="type"></component>
<button @click="handerClick">点击切换</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('cpn1',{
template:'<div>child-one</div>'
})
Vue.component('cpn2',{
template:'<div>child-two</div>'
})
const app = new Vue({
el:'#app',
data(){
return {
type:'cpn1'
}
},
methods:{
handerClick(){
this.type=this.type==='cpn1'?'cpn2':'cpn1';
}
}
})
</script>
</body>
</html>
is后的type代表组件名,通过改变type(组件名),来切换显示组件