目录
1、组件化思想
组件化是Vue.js中的重要思想。
它提供了一种抽象,让我们可以开发出一个个独立的可复用的小组件来构造我们的应用。
任何的应用都会被抽象成一颗组件树。
2、组件的使用
- 创建组件构造器:Vue.extend()
- 注册组件:Vue.component();
- 使用组件
<div id="demo">
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>
<script>
//创建组件构造器对象
const cpnConstructor = Vue.extend({
template: `<div>
<h2>我是标题</h2>
<p>我是内容</p>
<p>我是内容哈哈哈哈哈哈哈</p>
</div>`,
});
//注册组件
//component(组件标签名,组件构造器)
Vue.component('my-cpn',cpnConstructor)
const app = new Vue({
el:'#demo',
});
</script>
3、全部组件和局部组件
全局组件的注册
//注册全局组件:全局组件意味着可以在多个vue实例对象下使用 Vue.component('my-cpn', cpnConstructor)
局部组件的注册
const app = new Vue({ el: '#demo', components:{ //组件标签名:组件构造器 cpn:cpnConstructor } });
4、父组件和子组件的区分
<div id="demo">
<cpn2></cpn2>
</div>
<script>
//创建第一个组件构造器
const cpn1 = Vue.extend({
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容哈哈哈哈哈1</p>
<p>我是内容哈哈哈哈哈1</p>
<hr>
</div>
`,
});
//创建第二个组件构造器
const cpn2 = Vue.extend({
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容哈哈哈哈哈2</p>
<p>我是内容哈哈哈哈哈2</p>
<hr>
<cpn1></cpn1>
</div>
`,
components:{
cpn1: cpn1,
}
});
const app = new Vue({
el: '#demo',
components:{
cpn2:cpn2,
},
});
</script>
cpn2就是父组件,cpn1就是子组件
5、注册组件的语法糖
<div id="demo">
<cpt></cpt>
<hr>
<cpt2></cpt2>
</div>
<script>
//注册全局组件
Vue.component('cpt', {
template: `
<div>
<h2>我是标题111</h2>
<p>语法糖语法糖语法糖111</p>
</div>
`,
});
let vm = new Vue({
el: '#demo',
//注册局部组件语法糖
components: {
cpt2: {
template: `
<div>
<h2>我是标题222</h2>
<p>语法糖语法糖语法糖222</p>
</div>
`,
}
},
});
</script>
6、组件模板抽离
使用<script id="cpt" type="text/x-template">标签
<div id="demo">
<cpt></cpt>
<cpt></cpt>
<cpt></cpt>
</div>
<script id="cpt" type="text/x-template">
<div>
<h2>我是标题</h2>
<p>我是内容哈哈哈哈哈</p>
<p>我是内容哈哈哈哈哈</p>
<hr>
</div>
</script>
<script>
let vm = new Vue({
el: '#demo',
components: {
cpt: {
template: '#cpt',
}
}
});
</script>
或者使用template标签
<template id="cpt2">
<div>
<h2>我是标题222</h2>
<p>我是内容哈哈哈哈哈222</p>
<p>我是内容哈哈哈哈哈222</p>
<hr>
</div>
</template>
7、组件的数据域data必须是函数
注意:组件不可以直接访问Vue实例里的数据!Vue组件有自己保持数据的地方。
<script id="cpt" type="text/x-template">
<div>
<h2>{{ title }}</h2>
<p>我是内容哈哈哈哈哈</p>
<p>我是内容哈哈哈哈哈</p>
<hr>
</div>
</script>
<script>
let vm = new Vue({
el: '#demo',
components: {
cpt: {
template: '#cpt',
data() {
return {
title:'ABC',
}
},
},
},
});
</script>
组件里的data必须是一个函数,要返回一个实例对象,我们可以在这个对象里存放数据。
8、父子组件通信 - 父传子props
props传值有两种方式
- 方式一:字符串数组,数组中的字符串是传递时的名称
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值
<template id="fu">
<div>
<h2>父组件</h2>
<hr>
<zi v-bind:moviezi="movies" :messagezi="message"></zi>
</div>
</template>
<template id="zi">
<div>
<h2>子组件</h2>
<h2>{{ messagezi }}</h2>
<h2>{{ moviezi }}</h2>
<hr>
</div>
</template>
<div id="demo">
<fu></fu>
</div>
<script>
Vue.component('zi', {
template: '#zi',
props: ['moviezi', 'messagezi'],
});
let vm = new Vue({
el: '#demo',
components: {
fu: {
template: '#fu',
data() {
return {
movies: ['123', 456, '789'],
message: '没错我是子组件要用的消息',
}
},
},
}
});
</script>
在子组件中定义props,在父组件使用子组件时,用v-bind让子组件的props和父组件的data绑定关系
也可以使用对象传递,
这个时候我们可以指定类型验证
props: {
/*可指定类型*/
moviezi: Array,
messagezi: String,
}
支持验证的数据类型有:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
- 当我们有自定义构造函数时,也可以支持自定义类型的验证
还可以指定默认值,在没有传值的情况下生效。
props: {
/*可指定类型*/
/*可提供默认值*/
moviezi: {
type:Array,
default:[1,2,3,4,5,6]
},
messagezi: {
type:String,
default: 'defaultValue',
//required:为true时,指定这个值必须传递(绑定),不传就报错
required:true,
},
}
注意:props中的驼峰标识aaBbCc,使用时要转换为aa-bb-cc
比如props定义的seeMovies,在v-bind绑定和使用时,要这样用:see-movies
9、父子组件通信 - 子传父(自定义事件)
<template id="zi">
<div>
<h2>zi组件</h2>
<h2>{{ message }}</h2>
<button v-for="item in certificates"
@click="btnclick(item)"
:key="item.id">{{ item.name }}
</button>
<hr>
</div>
</template>
<template id="fu">
<div>
<h2>fu组件:</h2>
<h2>{{ fumessage }}</h2>
<h2 id="fumessage"></h2>
<hr>
<zi @itemclick="fuhanadler"></zi>
</div>
</template>
<div id="demo">
<fu></fu>
</div>
<script>
const zi = Vue.component('zi', {
template: '#zi',
data() {
return {
message: '子组件的message',
certificates: [
{
id: 1,
name: 'type1'
},
{
id: 2,
name: 'type2'
},
{
id: 3,
name: 'type3'
},
{
id: 4,
name: 'type4'
},
]
}
},
methods: {
btnclick(item) {
//子组件发射事件(自定义事件,传递的参数)
this.$emit('itemclick',item);
}
}
});
Vue.component('fu', {
template: '#fu',
components: {
zi: zi
},
data() {
return {
fumessage: 0,
}
},
methods: {
fuhanadler(item) {
this.fumessage = item.id;
}
}
});
let vm = new Vue({
el: '#demo',
data: {
message: '',
}
});
</script>
1、子组件的methods中定义自定义事件,使用this.$emit将事件传递出去
2、父组件在使用子组件时,注册已经定义的自定义事件
3、父组件中定义事件绑定的方法
10、父子组件通信 - 双向绑定
尝试实现但不推荐
<template id="zi">
<div>
<h2>zi组件:</h2>
<h2>number1:{{ number1 }}</h2>
<h2>number2:{{ number2 }}</h2>
<label>number1:
<input type="text" v-model="number1">
</label>
<br>
<label>number2:
<input type="text" v-model="number2">
</label>
</div>
</template>
<template id="fu">
<div>
<h2>fu组件</h2>
<h2>num1:{{ num1 }}</h2>
<h2>num2:{{ num2 }}</h2>
<label>num1:
<input type="text" v-model="num1">
</label>
<label>num2:
<input type="text" v-model="num2">
</label>
<hr>
<zi :number1="num1" :number2="num2"></zi>
</div>
</template>
<div id="demo">
<fu></fu>
</div>
<script>
const zi = Vue.component('zi', {
template: '#zi',
props: {
number1: Number,
number2: Number,
}
});
let vm = new Vue({
el: '#demo',
data: {
message: '',
},
components: {
fu: {
template: '#fu',
components: {
zi: zi,
},
data() {
return {
num1: 1,
num2: 2,
}
},
}
}
});
</script>
测试发现,父组件的属性,通过v-model的绑定,修改可以同步子组件的数据,而子组件通过v-model绑定进行进行修改,则不可以影响到父组件的数据。
而且有报错。
![]()
子组件通过v-model绑定进行进行修改,不可以影响到父组件的数据 ![]()
父组件的属性的修改,可以同步子组件的数据
改进:
<template id="zi">
<div>
<h2>zi组件:</h2>
<h2>props:number1:{{ number1 }}</h2>
<h2>props:number2:{{ number2 }}</h2>
<h2>data:dnumber1:{{ dnumber1 }}</h2>
<h2>data:dnumber2:{{ dnumber2 }}</h2>
<label>number1:
<input type="text"
v-model="dnumber1"
@input="number1Input">
</label>
<br>
<label>number2:
<input type="text" v-model="dnumber2">
</label>
</div>
</template>
<template id="fu">
<div>
<h2>fu组件</h2>
<h2>num1:{{ num1 }}</h2>
<h2>num2:{{ num2 }}</h2>
<label>num1:
<input type="text" v-model="num1">
</label>
<br>
<label>num2:
<input type="text" v-model="num2">
</label>
<hr>
<zi :number1="num1"
:number2="num2"
@number1input="changenum1"></zi>
</div>
</template>
<div id="demo">
<fu></fu>
</div>
<script>
const zi = Vue.component('zi', {
template: '#zi',
props: {
number1: String,
number2: String,
},
data() {
return {
dnumber1:this.number1,
dnumber2:this.number1,
}
},
methods:{
number1Input(event){
this.dnumber1 = event.target.value;
this.$emit('number1input',this.dnumber1);
}
}
});
let vm = new Vue({
el: '#demo',
data: {
message: '',
},
components: {
fu: {
template: '#fu',
components: {
zi: zi,
},
data() {
return {
num1: '1',
num2: '2',
}
},
methods:{
changenum1(number1){
this.num1 = number1;
}
}
}
}
});
</script>
思路:使用data替代props和input进行绑定,子组件的input双向绑定了data的dnumber1,这时,data:dnumber会随着输入变,
同时,子组件绑定input事件,在触发的方法中,向父组件发射自定义事件,把修改的值传递过去,
父组件使用子组件时,注册自定义事件,并绑定触发方法,方法内实现修改父组件的对应数据。
父组件的方法修改了对应数据后,由于props的缘故,父组件又把修改后的数据传递到子组件的props的number1,所以number1也改变了,
相当于三向绑定,C绑定A,A指定B,B绑定C.....当B修改时,并不会马上修改A,但会修改C,而C又可以修改A,所以A也随之改变,有点绕。
11、父子组件的访问方式
(1)父访问子 $children或$refs
this.$children是一个数组类型,它包含所有子组件的对象。,我们可以通过讴歌遍历,取出所有子组件的message状态。
<template id="cpn">
<div>
<h2>cpn</h2>
<button @click="showMessage">Click</button>
</div>
</template>
<div id="demo">
<cpn></cpn>
<cpn></cpn>
<hr>
<button @click="btnClick">父组件的click</button>
</div>
<script>
let vm = new Vue({
el: '#demo',
data: {
messages: '123',
},
methods:{
btnClick(){
console.log(this.$children);
//调用子组件的方法
//this.$children[0].showMessage();
//使用子组件的属性
alert(this.$children[0].message+'打印')
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
message: 'message#cpn'
}
},
methods: {
showMessage() {
alert(this.message)
},
}
}
}
});
</script>
![](https://i-blog.csdnimg.cn/blog_migrate/c2aa68561a3a37635d50382108a4f6ce.png)
在使用子组件的时候,用ref属性指定一个xxxid,使用this.$refs.xxxid就可以得到子组件的对象。
(2)子访问父$parent
使用方法和上边一样,但是不推荐使用,耦合性太高了。
(3)访问Vue实例$root
12、slot插槽的基本使用
类似于Java里的泛型
<template id="cpn">
<div>
<hr>
<h2>cpn</h2>
<p>我是cpn组件</p>
<slot>
<input type="text" placeholder="插槽的默认值">
</slot>
<hr>
</div>
</template>
<div id="demo">
<cpn>
<button>按钮一</button>
</cpn>
<cpn><h2>标题二</h2></cpn>
<cpn></cpn>
</div>
在template里定义slot标签,作为插槽。
slot标签内可以定义默认值,没有插入时,使用默认值。
使用时,在组件的注册的标签里填上要插入的东西即可。
13、slot具名插槽的使用
定义多个插槽的使用
<template id="cpn">
<div>
<hr>
<h2>cpn</h2>
<p>我是cpn组件</p>
<slot name="left"><span>左边的插槽</span></slot>
<slot name="center"><span>中间的插槽</span></slot>
<slot name="right"><span>右边的插槽</span></slot>
<hr>
</div>
</template>
<div id="demo">
<cpn></cpn>
<cpn>
<span slot="center">替换后的中间插槽</span>
</cpn>
<cpn>
<button slot="right">右边的按钮</button>
</cpn>
</div>
14、作用域插槽
父组件模板的所有信息,都会在父级作用域内编译,子组件模板的所有东西都会在子级作用域内编译。
作用域插槽:父组件替换插槽的标签,但内容由子组件来提供。
<template id="cpn">
<div>
<slot name="test" :data="pLanguages">
<ul>
<li v-for="item in pLanguages">{{ item }}</li>
</ul>
</slot>
<hr>
</div>
</template>
<div id="demo">
<cpn></cpn>
<cpn>
<!-- 获取子组件中的pLanguages-->
<div slot="test" slot-scope="slot">
<span v-for="i in slot.data">{{ i }}-</span>
</div>
</cpn>
</div>
<script>
let vm = new Vue({
el: '#demo',
data: {
message: '',
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['Java', 'JavaScript', 'C++', 'C##', 'Python', 'Go'],
}
},
},
}
});