组件
什么是组件
在html中,我们学习的单元是标签。 可以把Html中的标签当成最小最小最小的组件。
在vue中,我们学习的单元是组件
组件就是我们自己扩展的 “HTML 元素”,封装可重用的代码。
在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能。
组件(Component)是 Vue.js 最强大的功能之一。
在vue当中,一切都是组件,写项目就是在写组件。
一个组件本质就是一个对象。
区别:
- 根组件(vue的实例)它有el。而子组件中有template
- data的写法不同。根组件中就是对象,子组件中是函数。
子组件中,同样可以也包含子组件
什么是根组件
在 let vm = new Vue({}) 我们得到 的vm是一个Vue的一个对象(或者叫实例)
它也可以理解为一个组件,只不过它是一切其它的组件的根组件
一个项目就通常只有一个根组件
子组件
- 全局组件 全局注册一个子组件
- 局部组件 局部注册一个子组件
组件三步曲:
- 定义组件 难
- 注册组件 Vue.component(“my-header” ) 全局注册 在所有的根组件中都可以使用
- 使用组件 使用组件就是在使用标签
定义组件template 下,data必须是一个函数,而且必须返回一个对象。
全局子组件
全局:所有的vue实例都可以使用
在使用一个组件时,当成自定义标签就OK了
自定义标签默认都是行内的,都是女标签。
可以通过vue.extend与vue.component来定义组件。
<body>
<!--是根组件的模板-->
<div id="app1">
<!--3)使用子组件-->
<my-header></my-header>
</div>
<div id="app2">
<!--3)使用子组件-->
<my-header></my-header>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// 定义一个全局组件 一个组件就是一个JS对象
// Vue.component();第1个参数是组件的名字,第2个参数代表这个组件
// 组件指的是:
/* 一个组件就是一个JS对象
{
// tempplate 指定子组件的模板
template:"<div>{{msg}}</div>",
data(){ //在自组件中data 必须是函数,必须返回对象
return{
msg:"hello 子组件"
}
}
}
*/
// Vue.component 表示注册一个组件
Vue.component("my-header",{
// tempplate指定子组件的模板
template:"<div>{{msg}}</div>",
data(){
return{
msg:"hello 子组件"
}
}
});
// 1)定义组件
let myComponent = {
template: "<div>{{msg}} <button @click='f1'>点击</button></div>", // 当前组件模板
data(){ return { msg:"hello component" } }, // 给当前组件提供数据
methods:{ f1(){console.log("f1....")} }, // 当前组件模板中使用到的方法
}
// 2)注册组件 注册分全局注册和局部注册 由于是全局注册,意味着在所有的根组件对应的模板中都可以使用
Vue.component("my-header",myComponent);
let vm1 = new Vue({
el:"#app1",
})
let vm2 = new Vue({
el:"#app2",
})
</script>
</body>
组件的名字:
- 组件的定义时的名字
- 组件的注册时的名字
- 组件使用时,使用的是注册时的名字
我们很少会用全局组件,因为,在一个页面中,我们只会设置一个vue实例。
局部子组件
局部子组件就是说注册一个组件,这个组件只属于某个根组件
局部组件就是要去配置一下components这个选项。(就和配置 methods,data,filters,computed 一样)
<div id="app1">
<!-- 3)使用子组件-->
<my-header></my-header>
</div>
<script>
// 1) 定义子组件
let myComponet = {
template:"<div>{{msg}}</div>",
data(){ return{ msg:"hello 局部注册子组件" } }
}
let vm1 = new Vue({
el:"#app1",
// 2)局部注册一个子组件
components:{
"my-header":myComponet
}
})
</script>
template
<body>
<!--根组件模板-->
<div id="app">
<abc></abc>
</div>
<!--子组件模板--> //常用
<!--<template id="test">
<div>
<div>我是一个div</div>
<p>我是一个p标签</p>
<h1>我是一个h1</h1>
</div>
</template>-->
<script type="text/html" id="test">
<div>
<div>我是一个div</div>
<p>我是一个p标签</p>
<h1>我是一个h1</h1>
</div>
</script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let com = {
/*template:"<div><div>div</div><span>span</span></div>",*/
template:"#test"
}
let vm = new Vue({
el:"#app",
components:{
"abc":com
}
})
</script>
</body>
配置template注意点:
- template指令模板时,必须使用根标签包起来,否则报错:Component template should contain exactly one root element.
template指定模板常见的方式:
- template:后面写字符串
template:"<div><div>div</div><span>span</span></div>"
- 把模板定义到外面,在template引用
A)<template id="test"> //常用
<div>
<div>我是一个div</div>
<p>我是一个p标签</p>
<h1>我是一个h1</h1>
</div>
</template>
B)<script type="text/html" id="test">
<div>
<div>我是一个div</div>
<p>我是一个p标签</p>
<h1>我是一个h1</h1>
</div>
</script>
组件的名字
!--根组件模板-->
<div id="app">
<!--现在下面的写法不OK 后面还是这样的写法-->
<!--<MyHeader></MyHeader>-->
<!--现在,手动换成小驼峰也不OK-->
<!--<myHeader></myHeader>-->
<!--中划线命名是OK-->
<my-header></my-header>
<my-footer></my-footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
// MyHeader 定义组件时的名字
let MyHeader = {
template:"<div>MyHeader...</div>"
}
let vm = new Vue({
el:"#app",
components:{
// MyHeader 注册组件时的名字和定义组件时名字一样
MyHeader,
"my-footer":{
template: "<div>MyFooter....</div>"
}
}
})
</script>
使用组件三步曲之名字:
-
定义组件时,最好使用大驼峰命名
-
注册时,可以和定义组件时名字一样
-
目前来说,使用组件时,不要使用大驼峰,但是后面,我们会使用大驼峰
如果说,定义组件和注册组件都是大驼峰,使用组件时,名字换成中划线
子组件中的数据项
与vm实例中的data语法不同:
子组件中的data是一个函数,在其中写return { }。这是固定写法:
let 组件名 = {
template:””,
data:function(){
return {
//数据项
}
}
}
如果你在data中使用到了this,那么,你的data函数不能简写,简写后的箭头函数中没有this。
组件的嵌套
在一个组件中使用 components 去注册另一个组件,就可以使用注册的那个组件了。
<!--根组件模板-->
<div id="app">
<Father></Father>
</div>
<!--Father组件对应的模板-->
<template id="father">
<div>
<h1>Father组件 ---》{{fatherData}}</h1>
<Son></Son>
</div>
</template>
<!--Son组件对应的模板-->
<template id="son">
<div>
<h1>Son组件 ---》{{sonData}}</h1>
</div>
</template>
<script>
let Son = { // 定义Son组件对象
template:"#son",
data(){ return { sonData:"Son组件中的data" } }
}
let Father = { // 定义Father组件对象
template:"#father",
data(){ return { fatherData:"Father组件中的data" } },
components:{
Son // 在Father组件中注册Son组件
// 就可以在Father组件所对应的模板中使用Son组件
}
}
let vm = new Vue({
el:"#app",
components:{
Father,
}
})
</script>
组件数据传递 - 父传子
完整代码简介
把根组件作为父组件。父子之间不能正常共享数据的,父中的数据在子中是没有办法使用。
一个组件中的数据,默认情况下,只能让本组件所对应的模板使用,其它组件所对应的模板不能使用。但是可能通过一定的段,让子组件也可以使用父组件所对应的数据,也就是说需要把父组件中的数据传递到子组件,让子组件使用。这叫父传子。如果说把子组件中数据,传递给父组件,让父组件使用,那叫做子传父。
<!--根组件模板-->
<div id="app">
<Father></Father>
</div>
<!--Father组件对应的模板-->
<template id="father">
<div>
<h1>Father组件 ---》{{fatherData}}</h1>
<Son :xxx="fatherData"></Son>
</div>
</template>
<!--Son组件对应的模板-->
<template id="son">
<div>
<h1>Son组件 ---》{{sonData}}</h1>
<!--你在SON组件中使用Father组件中数据就报错-->
<!--<h2>要Son组件中使用Father组件中的数据 ---》 {{fatherData}}</h2>-->
<h2>使用父组件传递过来的数据:{{xxx}}</h2>
</div>
</template>
<script>
let Son = {
template:"#son",
props:{
// fatherData是接收的数据 String是对数据检验
// 我期待父组件传给子组件的数据的类型是String
xxx:String
},
data(){ return { sonData:"Son组件中的data" } }
}
let Father = {
template:"#father",
data(){ return { fatherData:"Father组件中的data" } },
components:{
Son
}
}
let vm = new Vue({
el:"#app",
components:{
Father,
}
})
</script>
HTML中标签:
<h1 title="hello"></h1> title="hello" html中自带的属性
<h1 abc="123"></h1> abc="123" html中自定义属性
<Son abc="123"></Son> <Son>不是html中的标签 组件
<Son abc="123"></Son> abc="123" 自定义属性
<Son title="hello"></Son> title="hello" 自定义属性
传输步骤
传递数据就靠自定义属性
第一步:在父组件所对应模板中使用子组件,属性位置绑定父组件中的数据
<template id="father">
<div>
<h1>Father组件 ---》{{fatherData}}</h1>
<Son :xxx="fatherData"></Son>
</div>
</template>
第二步:在子组件中通过props接收数据,如下:
let Son = {
template:"#son",
rops:{
// fatherData是接收的数据 String是对数据检验
// 期待父组件传给子组件的数据的类型是String
xxx:String
},
data(){ return { sonData:"Son组件中的data" } }
}
第三步:在子组件所对应的模板中就可以使用父组件传递过来的数据了
<template id="son">
<div>
<h1>Son组件 ---》{{sonData}}</h1>
<h2>使用父组件传递过来的数据:{{xxx}}</h2>
</div>
</template>
props:
一个组件的配置项,作用:接收父组件传递过来数据。 如父组件中绑定了一个数据:
props:{
xxx:{
type:String, // 期待父组件传递过来一个String类型的数据
// required:true, // 必须要求父传递数据
default:"lalala", // 配置默认值
}
}, //type:[String,Number] 表示string和number都可以
单个可以简写:
props:{
xxx:Number
}
步骤传输
- 在父组件中,正常定义自己的数据
父中有数据了,就意味着在父的视图中就可以得到数据了 - 在父组件的模板中通过属性绑定把数据绑定到子组件上
- 在子组件中定义props属性。用来接收父组件传递的数据
- 在子组件模板中使用数据
- 在子组件中的函数中使用数据
父传子有一个核心 子组件中的props配置项
父子传递,讲究”你情我愿”,父需要绑定数据,子需要接收数据
组件数据传递 - 子传父
自定义属性-自定义事件
HTML标签才有自带属性
只要不是HTML标签,里面写的都叫自定义属性
<button title="haha">登录</button> title="haha" 标签自带属性
<Son title="haha"></Son> title="haha" 自定义属性
标签的点击事件
给html标签上button按钮绑定一个点击事件,当我们点击时,调用f1方法,不需要写代码,只需要鼠标点击就会触发点击事件
<button @click="f1"></button> html标签 click自带事件
非HTML标签的自定义事件
不能说:给Son上绑定一个点击事件
给Son组件上绑定了一个自定义事件, 需要写代码去触发这个自定义事件
<Son @click="f1"></Son> 不是html标签 自定义事件
<Son @Hello="f1"></Son> 不是html标签 @Hello="f1 自定义事件 订阅事件
事件分两类:
- 浏览器自带的事件 click 点击了
- 自定义事件 Hello 需要写代码触发Hello事件 发布订阅
发布订阅难点:
<Son hello="ff"></Son> 自定义属性 值是 "ff" 字符串
<Son :hello="ff"></Son> 自定义属性 值是 ff 对应的数据
<Son @hello="ff"></Son> 自定义事件 事件发生时执行 ff 方法
子传父完整代码
<!--根组件模板-->
<div id="app">
<Father></Father>
</div>
<!--Father组件对应的模板-->
<template id="father">
<div>
<h1>Father组件 ---》{{fatherData}}</h1>
<h3>来自于子组件的数据:{{datafromson}}</h3>
<hr>
<!--hello是一个自定义事件,需要写代码触发这个自定义事件,当这个事件发生时,会调用f1方法-->
<!--订阅一个事件-->
<Son @hello="ff"></Son>
</div>
</template>
<!--Son组件对应的模板-->
<template id="son">
<div>
<h1>Son组件 ---》{{sonData}}</h1>
<button @click="sf">点击</button>
</div>
</template>
<script>
let Son = {
template:"#son",
data(){ return { sonData:"Son组件中的data" } },
methods:{ // this.$emit() 触发一个自定义事件
sf(){ // 在发布时就可以传递数据了
this.$emit("hello",this.sonData)
} //this当前组件对象,vue中默认挂了 $emit
}
}
let Father = {
template:"#father",
data(){ return { fatherData:"Father组件中的data",datafromson:"", } },
methods:{
ff(msg){
console.log("ff....: ",msg);
this.datafromson = msg;
}
},
components:{
Son
}
}
let vm = new Vue({
el:"#app",
components:{
Father,
}
})
</script>
重点步骤
在子模板的里面使用点击事件,点击调用方法,然后方法会触法一个自定义事件{ this.$emit(“自定义”)},可以传递参数。这个自定义事件在父模板使用子模板时绑定到子模板上的,这个自定义事件写在父模板的方法里面,形参就是接收的数据。
-
子组件的模板绑定一个天生点击事件,点击时调用子的方法 ff1
<button @click="ff1">点击</button>
-
在子组件的方法中的 ff1 中使用 this.$emit(“ange”),触发该自定义事件:发布
ff1(){ this.$emit( "ange" ,this.sonData)} 传递数据sonData
-
在父组件的模板中使用子组件,用触发的自定义事件去调用父的方法 ff2 : 订阅
<Son @ange="ff2"></Son> 父模板使用子组件 data(){ dataformson:"") ff2(msg) {console.log(msg); 方法2接收子传递的数据 this.dataformson = msg } 接收的数据赋值给父组件
父传子和子传父
父传子:
- 在父组件对应的模板中绑定自定义属性
- 在子组件中通过props接收自定义属性(数据)
- 在子组件中就可以使用这些数据
子传父:
- 在父组件对应的模板中子组件上绑定自定义事件
- 在子组件中触发自定义事件,触发自定义事件时,可以携带数据
- 在父组件中,当自定义事件发生时,调用监听器,监听器的形就是接收到的数据
vue中组件通信方案:
- 父传子 绑定自定义属性 + props接收
- 子传子 绑定自定义事件 + this.$emit() 触发自定义事件并携带数据