Vue学习——组件与组件间的通信
Day1
文章目录
前言
此文章根据官方文档及些网络资料编写,仅供个人使用。提示:以下是本篇文章正文内容,下面案例可供参考
一、组件基本使用
1.1 注册组件的基本步骤
将页面分成若干组件进行开发,然后组件的组成分成三个步骤第一步调用Vue.extend()方法创建组件
第二步调动Vue.component()方法注册组件
第三步使用组件
<div id="app">
<!--3.使用组件-->
<cpn></cpn>
</div>
<script>
// 1.创建组件构造器对象
const cpn = Vue.extend({
template: `
<div>
<h2>组件1</h2>
<p>我是组件1的内容</p>
</div>`
})
// 2.注册组件
Vue.component('cpn', cpn)
const app = new Vue({
el: '#app',
})
</script>
结果:
1.2 全局组件
全局组件的意思是在不同的Vue实例下都可以使用 <div id="app">
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script>
// 1.创建组件构造器对象
const cpn = Vue.extend({
template: `
<div>
<h2>组件1</h2>
<p>我是组件1的内容</p>
</div>`
})
// 2.注册组件
Vue.component('cpn', cpn)
const app = new Vue({
el: '#app',
})
const app2 = new Vue({
el: '#app2',
})
</script>
1.3 局部组件
局部组件只能在注册的实例中使用 <div id="app">
<cpn></cpn>
</div>
<div id="app2">
<cpn></cpn>
</div>
<script>
// 1.创建组件构造器对象
const cpn = Vue.extend({
template: `
<div>
<h2>组件1</h2>
<p>我是组件1的内容</p>
</div>`
})
const app = new Vue({
el: '#app',
// 2.注册组件
components: {
'cpn': cpn
}
})
const app2 = new Vue({
el: '#app2',
})
</script>
1.4 父组件调用子组件
我们在父组件中注册子组件在调用,其实从这里我们就可以看出来,每一个组件都是一个独立的模块,里面有方法,计算属性,等。 <div id="app">
<cpn2></cpn2>
</div>
<script>
// 1.创建子组件
const cpn1 = Vue.extend({
template: `
<div>
<h2>子组件</h2>
<p>我是子组件的内容</p>
</div>
`
})
// 2.创建父组件
const cpn2 = Vue.extend({
template: `
<div>
<h2>父组件</h2>
<p>我是父组件的内容</p>
<!-- 在这里调用的是子组件的内容 -->
<cpn1><cpn1>
</div>`,
components: {
cpn1: cpn1
}
})
const app = new Vue({
el: '#app',
components: {
cpn2: cpn2
}
})
</script>
1.5 组件语法糖
组件语法糖省去了Vue.extend(),使用了一个对象代替 // 1.注册全局组件语法糖
Vue.component('cpn1', {
template: `
<div>
<h2>我是全局组件语法糖</h2>
</div>`
})
// 2.注册局部组件的语法糖
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
'cpn2': {
template: `
<div>
<h2>我是局部组件语法糖</h2>
</div>`
}
}
})
1.6 模板分离
将模板分离到HTML中,其中Vue提供了2种方案这里要注意script标签的类型必须是text/x-template
使用< script >标签
使用< template >标签
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<!--1.script标签-->
<script type="text/x-template" id="cpn1">
<div>
<h2>我是script</h2>
</div>
</script>
<!--2.template标签-->
<template id="cpn2">
<div>
<h2>我是template</h2>
</div>
</template>
<script>
// 1.注册script标签组件
Vue.component('cpn1', {
template: '#cpn1'
})
// 2.注册template标签组件
Vue.component('cpn2', {
template: '#cpn2'
})
const app = new Vue({
el: '#app'
})
</script>
1.7 组件数据问题
首先组件是一个单独的模块,拥有属于自己的模板,也就是他是不能直接访问Vue实例中的data的。 <div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是Vue实例中的data数据:{{message}}</h2>
<h2>{{title}}</h2>
<p>内容</p>
</div>
</template>
<script>
// 1.注册一个全局组件
Vue.component('cpn', {
template: '#cpn',
data() {
return {
title: 'abc'
}
}
})
const app = new Vue({
el: '#app',
data: {
message: 'data',
}
})
</script>
我们可以看到结果,message的数据没有显示,而打开控制台也显示找不到message。
结论:就是组件他有自己存放数据的地方所以data()就出现了
2. 至于为什么,官方文档的意思是,让每一个组件都返回一个新的对象,如果用同一个对象,那么他们数据会互相影响,意思就是会共享数据。
3. 例如我们设置了2个按钮A和B,每点击一下A按钮,**A按钮+1**,点B按钮则**B按钮+1**
4. 如果是用相同的对象,那么按一下,A和B个按钮**都+1**。
二、组件间通信
2.1 父组件向子组件传递数据,props
父组件需要通过props向子组件传递数据,props的值有两种实现方式,一种是以**字符串数组**,一种是以**对象**实现。最简单的实现:
<div id="app">
<!-- 第三步,使用v-bind绑定 -->
<cpn :chello="hello"></cpn>
</div>
<template id="cpn">
<!-- 第二步,调用子组件的变量名 -->
<h2>{{chello}}</h2>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
hello: 'Hello'
},
components: {
'cpn': {
template: '#cpn',
// 第一步,写上你打算引用的变量名
props: ['chello']
}
}
})
</script>
第一步先在props里面写上你打算引用的变量名,这里我们用chello,然后在模板上调用你子组件刚刚创建的 chello ,然后我们直接调用模板实现cpn模板,使用v-bind将你子组件创建的 chello 与父组件hello相绑定。就结束了
字符串数组:
<div id="app">
<cpn :cmessage="message" :chello="hello"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmessage">{{item}}</li>
</ul>
<h2>{{chello}}</h2>
</div>
</template>
<script>
// 父传子: props
const cpn = {
template: '#cpn',
props: ['chello', 'cmessage'],
}
const app = new Vue({
el: '#app',
data: {
message: ['java', 'python', 'c++'],
hello: 'Hello'
},
components: {
cpn
}
})
</script>
对象:
const cpn = {
template: '#cpn',
props: {
// 1.类型限制
// cmessage: Array,
// chello: String,
// 2.提供一些默认值, 以及必传值
chello: {
type: String,
default: 'aaaaaaaa',
required: true
},
// 类型是对象或者数组时, 默认值必须是一个函数
cmessage: {
type: Array,
default () {
return []
}
}
},
data() {
return {}
},
}
对象可以进行类型限制,以及默认值的设置,和是否设置必传值required: true,意思是这个数值必传的,不传会报错
这里需要注意的是,如果类型是对象或数组的时候,默认值必须是一个函数。
2.2 关于props的驼峰标识问题
我们在使用props传递数据的时候,可能会使用驼峰标识,但是在标签上,Vue他是不支持驼峰标识的。 <div id="app">
<cpn :cInfo="info" :childMyMessage="message" />
</div>
如果你打算这么写的话,运行代码,会显示空对象,你需要在大写前面加上 -
<div id="app">
<cpn :c-info="info" :child-my-message="message" />
</div>
2.3 子组件向父组件传递数据,$emit
我们需要使用自定义事件来完成,具体流程是:1. 在子组件中使用$emit来触发事件
2. 在父组件中,使用v-on监听事件
<!--父组件模板-->
<div id="app">
<!--第二步:监听名为item-click的自定义事件-->
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in laugh" @click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
data() {
return {
laugh: [{
id: '1',
name: 'java'
},
{
id: '2',
name: 'python3'
},
{
id: '3',
name: 'html'
}
]
}
},
methods: {
btnClick(item) {
// 第一步:发射自定义事件
this.$emit('item-click', item)
}
}
}
// 2.父组件
const app = new Vue({
el: '#app',
components: {
cpn
},
methods: {
// 第三步:将按钮信息发送到控制台
cpnClick(item) {
console.log('cpnClick', item);
}
}
})
</script>
2.4 父子组件间双向通信
我们直接做个案例第一步:让父组件向子组件传递数据
第二步:让子组件向父组件传递数据,同时弄一个输入框可以改变两者的数值
理解:当子组件的值发生变化,父组件也会变化。
第一步很简单,我们就这样写:
<!--父组件模板-->
<div id="app">
<cpn :number1="num1" :number2="num2"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<h2>{{number1}}</h2>
<h2>{{number2}}</h2>
</div>
</template>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
num1: 0,
num2: 1
},
components: {
cpn
},
})
</script>
第二步:
<!--父组件模板-->
<div id="app">
<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<input type="text" :value="dnumber1" @input="num1Input">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" :value="dnumber2" @input="num2Input">
</div>
</template>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
// 第一步:先定义属于子组件的数值
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods: {
num1Input(event) {
this.dnumber1 = event.target.value
this.$emit('num1change', this.dnumber1)
},
num2Input(event) {
this.dnumber2 = event.target.value
this.$emit('num2change', this.dnumber2)
}
},
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
num1: 0,
num2: 1
},
components: {
cpn
},
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
},
}
})
</script>
// 第一步:先定义属于子组件的元素
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
第二步,先创建个输入框,让输入框的值与子组件元素dnumber1相绑定,然后给输入框绑定个输入事件num1Input。
<input type="text" :value="dnumber1" @input="num1Input">
第三步,将输入框的值给到dnumber1子组件元素,然后发送自定义事件:num1change。
num1Input(event) {
this.dnumber1 = event.target.value
this.$emit('num1change', this.dnumber1)
}
第四步,在父组件中使用v-on监听 num1change 自定义事件,触发后调用父组件函数: num1change
<div id="app">
<cpn :number1="num1" :number2="num2" @num1change="num1change" @num2change="num2change"></cpn>
最后一步,父组件将值转型后赋给num1
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
2.5 父组件访问子组件
在父组件中访问子组件的数据有两种办法一种是使用children,将子组件所有对象显示出来
一种是使用refs,先给一个组件绑定一个ID,然后使用 this.$refs.ID 调用
2.5.1 children
<div id="app">
<cpn></cpn>
<cpn ref="test"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
// 1.$children
console.log(this.$children);
/*for (let c of this.$children) {
console.log(c.name);
c.showMessage();
}*/
// console.log(this.$children[3].name);
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
}
},
}
})
</script>
2.5.2 refs
btnClick() {
// 2.$refs => 对象类型
console.log(this.$refs.test.name);
}
2.6 子组件访问父组件
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
// 1.访问父组件$parent
// console.log(this.$parent);
// console.log(this.$parent.name);
// 2.访问根组件$root
console.log(this.$root);
console.log(this.$root.message);
}
}
}
}
}
}
})
</script>
总结
Vue学习(一)插值操作
Vue学习(二)动态绑定与事件监听
Vue学习(三)循环遍历与表单绑定
Vue学习(四)组件与组件间的通信
Vue学习(五)组件的插槽