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>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值