vue2 component组件
引入vue.js的写法,不是脚手架的写法
一、描述
将有共同结构的东西进行模块化,提升代码的复用性
const cpn = Vue.extend({
template:'',
components: {}
})
二、通过引用vue.js的使用步骤
1、创建
可以理解为在vue的最原始的对象上进行扩充,类似jquery的组件扩展
一般会将创建出来的组件对象赋给一个变量
const cpn = Vue.extend({
template:obj
});
2、注册
需要传2个参数,一个是组件的名字,一个是创建好了的组件对象
Vue.component('testcpn', cpn)
3、引用
将注册时指定的名字直接放到<>中
<testcpn></testcpn>
4、语法糖
// 可以使用语法糖的方式,将前2步骤合并为一个步骤【常用】
Vue.component('testcpn', {
template: obj
});
// 其中obj可以是组件模班的html字符串,也可以是一个id值
<script type="text/x-template" id=""></script> // script标签的id
<template id=""><div></div></template> // template标签的id,template里面一定要有一个包裹所有标签的div标签,否则会报错
// template标签是和script标签、body标签同级
5、完整代码
<template id="cpn3">
<div>
<p>{{title}}</p>
</div>
</template>
<script>
Vue.component('my-cpn1', cpn3);
const app = new Vue({
el: '#app'
});
</script>
</body>
<div id='app'>
<my-cpn1></my-cpn1>
</div>
</body>
三、组件类型
1、全局组件
可以在多个vue实例中使用
2、局部组件
在某一个vue实例的components属性下创建,只对当前实例有效【常用,以下写法使用了语法糖】
const app = new Vue({
el:'#app',
components:{
name: {
template: '#cpn3'
}
}
});
区分全局和局部的关键在于注册的地方,实在实例中,还是和实例是同级的关系
3、子组件
在父组件注册的时候,components中指定的组件,就是该组件的子组件
子组件无法在全局中使用,只能在父组件中使用
// 子组件
const chCpnC = Vue.extend({
template:``,
});
// 父组件
const fapnc = Vue.extend({
template:`<chnc></chnc>`,
components: {
chnc: chCpnC
}
});
语法糖写法,因为常用的是局部组件,所以用局部组件作为例子
// 把cpn1用一个变量存起来,是为了代码结构看起来更加清晰
const cpn1 = {
template: ``,
components:{
'cpn1-child1': {
template:''
},
'cpn1-child2': {
template:''
}
}
}
const app = new Vue({
el: '#app',
components:{
cpn: cpn1
}
})
子组件中也可以有自己的data、methods等等,只要vue实例(本身就是一个父组件)有的,子组件也有
但是,有个特殊的地方,父组件中的data是个json对象,到子组件中必须是有一个函数,函数返回一个json对象
这是因为每个子组件都需要有属于自己的数据,不能改一个地方的数据把其他地方的数据也改掉了
// 示例省略了注册的代码
const cpn1 = {
template: ``,
components:{
'cpn1-child1': {
template:'',
data(){
return {
num: 1
}
},
methods:{}
},
}
}
四、父子组件通信
1、父 --> 子
除了一开始渲染的时候的数据展示,在父组件中改变了传给子组件中的变量,子组件上的显示也会改变
props属性 和 v-bind 搭配使用
(1)父组件:在引用这个组件的标签处打上标记,传递变量
<cpn1-child :name1="message"></cpn1-child>
当等号左边是 “:”+传递到子组件中的变量名字,那么传递的是动态的变量值
当等号左边是“传递到子组件中的变量名字=xxx”,那么传递的是固定的值【此处注意,一开始学习的时候误解了用法】
传递过去的名字不可与父组件中存在的变量名字重复
传递过去的名字不可以使用驼峰式命名,因为html代码的区分大小写,并且没有大写【仅限于引入vue.js写法,脚手架写法不受影响】
(2)子组件:接收组件,然后直接使用接收的变量
// 写在子组件注册的地方,和template是同级
props: {
// 写法1(简写)
name1: obj,
// 写法2
name2: {
type: obj,
default: '',
required: boolean
}
}
1、obj是代表传的这个变量的类型,可以是String、Array、{}
2、default缺省值
3、required代表这个是是否一定要传,为true时候,default的没有效果了
(3)完整代码
<body>
<div id="app">
<cpn1></cpn1>
</div>
</body>
<template id="cpn-1">
<div>
<p>这是父组件</p>
<cpn-child :mess-from-parent="message"></cpn-child>
</div>
</template>
<template id="cpn-1-child">
<div>
<p>这是子组件</p>
<p>{{messFromParent}}</p>
</div>
</template>
<script>
const cpn1 = {
template: '#cpn-1',
data(){
return {
message: '这是父组件传到子组件的内容'
}
},
components:{
'cpn-child':{
template: '#cpn-1-child',
props:{
'mess-from-parent':String
},
},
}
}
const app = new Vue({
el: '#app',
components:{
cpn1: cpn1
}
})
</script>
2、子 --> 父
当子组件做了某些操作(比如鼠标事件)的时候,改变了某些需要传回给父组件的变量的值
使用 this.$emit 触发
(1)父组件:引用子组件的标签处加上 “@自定义事件名字=方法1”
一定是要在引用子组件的标签上写,在子组件模板中的任何一个html标签中写都没有用
事件名字不能使用驼峰式命名,并且需要必须 /$emit 方法中定义的名字一一对应
“方法1”在methods中定义
(2)子组件:触发的通信的函数里面写上 this.$emit(事件名字, param[不是必须的]);
(3)完整代码
<body>
<div id="app">
<cpn1></cpn1>
</div>
</body>
<template id="cpn1">
<div>
<h2>这是父组件的标题</h2>
<p>下面开始展示子组件的内容了</p>
<cpn1-child @child-click="childInfo"></cpn1-child>
</div>
</template>
<template id="cpn1-child">
<div>
<h3>这是cpn1的一个子组件</h3>
<button @click="returnInfo">返回信息</button>
</div>
</template>
<script>
const cpn1 = {
template: '#cpn1',
components: {
'cpn1-child': {
template: '#cpn1-child',
methods: {
returnInfo(){
this.$emit('child-click')
}
}
}
},
methods: {
childInfo(){
console.log('子组件触发了这个方法');
}
}
}
const app = new Vue({
el: '#app',
components:{
cpn1: cpn1
}
})
</script>
3、特殊标签的父子通信
通过 v-model 、data()、watch 属性搭配
(1)父传过来的变量,一定要通过data这个方法返回一个新命名的变量,否则是无法实现v-model的双向绑定
(2)监听绑定了 v-model 的标签的值变化,可以直接使用watch属性【和methods是同级】
(3)watch中的函数名,和v-model绑定的变量一致,就能监听到对应的标签的值变化
使用watch的原因是,子组件在v-model中绑定的变量,需要通过data()方式去return,所以就跟父组件中的不一样了,
所以这个时候,要是想父组件的对应变量的值也去发生改变,那么就要用watch去监听,然后去修改父组件中的变量值
(4)父–>子:直接在函数中修改传给子组件的变量的值
父访问子对象的属性
① $children:缺点是下标是会变化的,无法确定的
② $refs:需要在引用组件的html标签上增加ref属性,默认是一个空的对象【常用】
this.$children[0].变量名 = xxx
this.$refs.ref属性值.变量名 = xxx
// 两个属性的变量名,都应该是子组件中data方法中返回的那个变量名,不是父组件传递进去的那个名字
(5)子–>父:触发$emit这个方法,具体参考步骤(2)
子访问父对象的属性【用的不多,记录一下而已,和该父子双向绑定没有关系的,不要误解】
① $parent:this.$parent【用的很少】
② $root:获取到的是vue实例,无论嵌套几层【用的不多】
(6)完整代码
<template id="cpn1">
<div>
<h2>这是父组件的标题</h2>
<p>下面开始展示子组件的内容了</p>
<input v-model="count" />
<cpn1-child :ccount="count" @item-change="setNewVal"></cpn1-child>
</div>
</template>
<template id="cpn1-child">
<div>
<h3>这是cpn1的一个子组件</h3>
<input v-model="c1count" @input="changC1">
</div>
</template>
<script>
const cpn1 = {
template: '#cpn1',
data(){
return {
count:'0'
}
},
components: {
'cpn1-child': {
template: '#cpn1-child',
props:{
ccount: String
},
data(){
return {
c1count: this.ccount
}
},
watch:{
c1count(){
this.$emit('item-change',this.c1count)
}
},
}
},
watch:{
count(){
this.$refs.aaa.c1count = this.count
// this.$children[0].c1count = this.count
}
},
methods: {
setNewVal(newVal){
this.count = newVal
}
}
}
const app = new Vue({
el: '#app',
components:{
cpn1: cpn1
}
})
</script>
五、插槽
用来扩展,或者自定义
1、用法
在template标签中 使用 slot标签 占位
在引用组件的标签中间的内容,就会替换掉slot这个标签
slot标签中间的内容就是默认值,一旦获得传值,整个都会被替换掉
2、具名插槽
用来给指定槽位替换内容
<!-- 子组件cpn1 -->
<slot name="title"></slot>
<!-- 父组件 -->
<cpn1><span slot="title">哈哈哈</span></cpn1>
3、作用域插槽
父组件替换插槽里面的标签,内容是由子组件来决定,也就是把子组件的某个data数据显示在父组件上,【参看例子 --> 8. 组件 案例.html】
不直接在子组件那边写,是因为有可能一种情况是我需要用到子组件返回的数据,但是呢,我需要用不同的格式展示出来,这个时候,就要在父组件那里写展示的格式
(1) 在slot标签中传参数,参数的名字可以自定义
<template>
<slot :插槽名称="父组件上的一个变量"></slot> // 支持使用默认值
</template>
(2)在引用子组件的标签中,使用template的标签替换插槽的内容
<template slot-scope="自定义名称2">
<p>{{自定义名称2.插槽名称}}</p>
</template>
(3)完整代码
<body>
<div id="app">
<cpn1></cpn1>
<cpn1>
<p slot="message">忘记要在这里加上文字了</p>
</cpn1>
<cpn1>
<template slot-scope="hhhh">
<ul v-for="item in hhhh.mydata">
<li>{{item}}</li>
</ul>
<p>{{hhhh.mydata.join(' -*- ')}}</p>
</template>
</cpn1>
</div>
</body>
<template id="cpn-1">
<div>
<h2>父组件这里</h2>
<slot name="message"></slot>
<slot :mydata="arr"></slot>
</div>
</template>
<script>
const app = new Vue({
el: '#app',
components:{
cpn1: {
template: '#cpn-1',
data(){
return {
arr: ['1','2','3','4','5']
}
}
}
},
})
</script>