vue组件 component
简单理解
- 组件可以理解为网页上的的html元素,也可以理解为网页上一个独立的部分(比如说网页头部、主体、尾部等)
- 组件实际上是让我们写好的代码复用性更高的一种方式
- 通过多种基础组件的组合形成完整的功能,而且因为代码的复用不会增加多余的代码,这样的话修改相对而言也会更加方便
- vue new出来的实例其实也是组件(实例的属性组件也都有)
一、定义
let CommonHeader = {
template:"",
data(){
return{
}
},
methods:{
}
}
二、分类
全局组件
在Vue上注册的就是全局组件,在任意的组件和实例上都可以使用
let CommonHeader = {
template:"<h1>这是一个公共的头部文件</h1>",//里面写html标签,即视图文件
data(){
return{
}
},
methods:{
}
}
Vue.component("CommonHeader",CommonHeader)//组件命名建议使用大驼峰或连接符
const vm = new Vue({
el: '#box',
data: {
},
methods: {
}
})
局部组件
1、在某个组件或者实例中注册的就是局部组件,只能在注册的组件或者实例上使用
2、局部组件可以在其他组件的conponents上注册(即既可以是new Vue,也可以是其他组件)
示例1:在 new Vue上注册
//在 new Vue上注册可以在这个实例中应用 当前是id为box的标签
let CommonTitle = {
template:`<h1>这是局部组件标题</h1>`,
data () {
return {
}
},
methods: {
}
}
const vm = new Vue({
el: '#box',
data: {
},
methods: {
},
components: {
'CommonTitle':CommonTitle
}
})
示例2:在 其他组件的components属性上注册
//在其他组件注册则只能在当前组件的template模板中使用
let CommonTitle = {
template:`<h1>这是局部组件标题</h1>`,
data () {
return {
}
},
methods: {
}
}
let CommonHeader = {
template:`
<div>
<h1>这是大标题</h1>
<common-title></common-title>
</div>
`,
data(){
return{
}
},
methods:{
},
components: {
'CommonTitle': CommonTitle
}
}
Vue.component("CommonHeader",CommonHeader)
const vm = new Vue({
el: '#box',
data: {
},
methods: {
},
// components: {
// 'CommonTitle':CommonTitle
// }
})
注意
- 组件中的data 必须是 一个函数 返回一个对象 (原因是 组件是需要复用的 ,如果直接是对象,多次使用时,使用的是同一个对象,不符合项目开发要求,而如果是返回对象的话(实际上是闭包),会让组件在每一次使用的时候都形成一个独立的空间 )
- 每个组件有自己的作用域,组件内部的数据,和方法只能在组件内部使用(如向跨组件,需要组件间通信)
- 组件的命名 可以有两种 1大驼峰 2下划线命名法
eg:
CommonHead common-head
使用时二者都一样
- 局部组件 在哪个组件的components中注册,就只能在这个组件的template中使用
- 组件的template 有且只能有一个根标签(元素)
三、关系
组件间的嵌套使用形成了组件间的关系
1.父子
2.兄弟
3.无关系
四、通信
父向子通信
- props通信
//子组件的props属性 值是一个数组,数组内容是父组件传进来的参数列表
//父组件 相当于增加一个自定义属性
//1 传一个静态的数据
<body>
<div id="box">
<home></home>
</div>
<script>
let CommonTitle = {
props:['title'],
template: `
<div>
这是标题
<h2>{{ title }}</h2>
</div>`,
}
let Home = {
template: `
<div>
<common-title title="我是首页"></common-title>
home页面内容
</div>`,
data () {
return {
title:"我是home页"
}
},
components: {
CommonTitle
}
}
let vm = new Vue({
el: '#box',
components: {
Home
}
})
</script>
</body>
//2 传一个动态的数据
//父组件 自定义属性之前加冒号: 自动去找data里面的变量
let Home = {
template: `
<div>
<common-title :title="title" :title2="title2"></common-title>
home页面内容
</div>`,
data () {
return {
title:"我是home页",
title2:"我是home页2"
}
},
components: {
CommonTitle
}
}
- props验证
props用法,没有校验 props数据类型,以及必须填写,默认值 等,会造成 程序 存在 不稳定性 (代码不健壮)
验证三种类型: 数据类型 是否必须填写 默认值
数据类型:String Number Boolean Array Object Date Function Symbol
是否必须填写: required:true/false true必填 false非必填
默认值 : default
// 此时props不是数组,而是对象
props:{
//只验证类型
a:String,
//既验证类型,又要求必传
b:{
type:[String,Number],//类型是多个中的一个
required:true
},
//验证类型,并设置默认值
c:{
type:Number,
default:0
}
}
注意
- props中定义的参数名会自动的编译成实例,属性
- props名字不能和data中以及methods和计算属性,不能同名
- 如果默认值是数组或者对象,则需要一个函数返回这个默认值
- props能否改变props保持单向的 即父向子(子不能改变),易于维护
子向父通信
通过自定义事件触发
//子组件 methods中 this.$emit("事件名",携带的数据)
/*
父组件 <子组件 @事件名="fn"></子组件>
methods中 fn(data){ data就是子组件传过来的数据}
*/
<script>
let CommonTitle = {
template: `
<div>
子组件
<button @click="change">单击传输数据</button>
</div>`,
data () {
return {
msg: '我是子组件的数据'
}
},
methods: {
change () {
// this.$emit("事件名",this.msg)
this.$emit("change1",this.msg)
}
}
}
let Home = {
template: `
<div>
<common-title @change1="fn"></common-title>
home页面内容
</div>`,
data () {
return {
title:"我是home页",
title2:"我是home页2"
}
},
methods: {
// fn () {
// alert("出发了fn")
// }
fn (msg) {
alert(msg)
}
},
components: {
CommonTitle
}
}
let vm = new Vue({
el: '#box',
components: {
Home
}
})
</script>
</body>
兄弟组件通信
<body>
<div id="box">
<home></home>
</div>
<script>
//事件总线 ---中央事件总线
/*
利用了第三方的实例
this.$emit("事件名",参数) //组件一
this.$on("事件名",fn) //组件二
*/
let eventBus = new Vue() //事件总线 用来弹射和监听事件
let CommonTitle = {
template: `
<div>
子组件1
<button @click="change">单击传输数据</button>
</div>`,
data () {
return {
msg: '我是子组件1的数据'
}
},
methods: {
change () {
eventBus.$emit("change1",this.msg)
}
}
}
let CommonTitle2 = {
template: `
<div>
子组件2
{{ msg}}
</div>`,
mounted() {
// eventBus.$on("change1",()=>{
// alert("接收到了")
// })
eventBus.$on("change1",(msg)=>{
this.msg=msg
})
},
data () {
return {
msg: '我是子组件2的数据'
}
}
}
let Home = {
template: `
<div>
<common-title ></common-title>
<hr/>
<common-title2 ></common-title2>
home页面内容
</div>`,
data () {
return {
title:"我是home页",
title2:"我是home页2"
}
},
components: {
CommonTitle,
CommonTitle2
}
}
let vm = new Vue({
el: '#box',
components: {
Home
}
})
</script>
</body>
注意
1 兄弟组件通信,是谁emit的谁去on,所以需要定义一个第三方组件eventBus
2 回调函数如果不写成箭头函数,需要处理this问题
- 可以在外部定义一个变量接收this
let _this=this
eventBus.$on("change1",function(msg){
_this.msg=msg
})
- 可以使用bind(this)
eventBus.$on("change1",function(msg){
this.msg = msg
}.bind(this))
另外三种通信
ref通信
直接在父组件里面拿到了子组件这个实例
let CommonTitle = {
template: `
<div>
子组件1
</div>`,
data () {
return {
msg: 123
}
},
}
let Home = {
template: `
<div>
<common-title ref="commonTitle" ></common-title>
</div>`,
mounted () {
console.log(this.$refs.commonTitle)//此时打印得到的是子组件这个实例本身
},
components: {
CommonTitle,
}
}
let vm = new Vue({
el: '#box',
components: {
Home
}
})
//不建议直接使用ref操作子组件
$children $parent
子组件里引用$parent
mounted () {
console.log(this.$parent)//得到的是当前组件的父组件
}
/*
VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
*/
父组件引用$children
mounted () {
console.log(this.$parent) // 得到的当前组件的所有子组件,是数组,通过下标可以直接得到子组件
}
// [VueComponent]
provide/inject
父组件 提供provide
let msg=12345
let Home = {
provide:{
msg:msg,
num:107
},
}
子组件 接收inject
let CommonTitle = {
inject: ["msg","num"],
mounted () {
console.log(this.msg)//12345
console.log(this.num)//107
}
}
五、插槽
其实是用来占位的,是vue的系统组件
<solt></slot>
//在使用组件时,自定义标签内容,会自动的灌入到solt占的位置上
//可以实现组件在不同的父组件中使用时,内部可以有不一样的代码块(布局)
普通插槽
// News 组件和 Home组件是父组件 在当前页面使用
// CommonTitle 是子组件 组件中使用了插槽<slot>插槽的默认值</slot>
/*
1 如果是在父组件中直接使用子组件<common-title><common-title>
则渲染的的是内容 插槽的默认值
2 如果是在父组件中使用
<common-title>
<div>
你好
<div>
<common-title>
则渲染的内容是 你好
*/
let CommonTitle = {
template: `
<div>
<h2>我是子组件</h2>
<slot>插槽的默认值</slot>
</div>
`
}
let msg = 12345
let Home = {
template:`
<div>
home页内容
<common-title>
<h5>我是插槽对应的内容</h5>
<p>lllll</p>
</common-title>
</div>`,
data () {
return {
title:"我是home页"
}
},
components: {
CommonTitle,
}
}
let News = {
template:`
<div>
news页内容
<common-title>
<button>按钮</button>
</common-title>
</div>`,
data () {
return {
title:"我是news页"
}
},
components: {
CommonTitle,
}
}
let vm = new Vue({
el: '#box',
components: {
Home,
News
}
})
命名插槽(具名插槽)
可以在子组件中,定义多个插槽,且每个插槽,有自己的名字,在代码传入时,指定传入哪个
let CommonTitle = {
template: `
<div>
<h2>我是子组件</h2>
<slot name="xm"></slot>
<slot name="xq"></slot>
</div>
`
}
let Home = {
template:`
<div>
home页内容
<common-title>
<template slot="xq">
<button>我是小强</button>//此时的button会占用 name="xq"的位置
</template >
<div slot="xm">
<button>我是小明</button>//此时的button会占用 name="xm"的位置
</div>
</common-title>
</div>`,
components: {
CommonTitle,
}
}
let vm = new Vue({
el: '#box',
components: {
Home,
}
})