组件
组件是什么:组件就是自定义标签,他是vue中核心功能之一,是把页面当中的可以重复使用的内容进行封装,方便调用
全局组件
作用域:在所有实例中都可以使用
语法:Vue.component:{"组件名不能喝现有的html标签重名",{
template:"<div></div>"
}
基本使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局组件基本使用</title>
</head>
<body>
<div id="app">
<!-- 2. 使用组件 -->
<!-- <demo></demo> -->
<!--
4.使用驼峰法调用 必须大写转小写 前面加个-
例 demoElOne 调用 demo-el-one
-->
<demo-test></demo-test>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script>
// 1.创建组件
// Vue.component('demo',{
// template:"<div>我是组件一</div>"
// })
// 3.如果有个多个标签 必须要用父元素包裹
Vue.component('demo',{
template:"<div><p>组件1</p><p>组件2</p></div>"
})
// 4.组件名使用驼峰法
Vue.component('demoTest',{
template:'<div>我是组件一</div>'
})
const vm=new Vue({
el:'#app',
data:{
}
})
</script>
</html>
外部模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局组件外部模板</title>
</head>
<body>
<div id="app">
<!-- 2. 使用组件 -->
<demo></demo>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<!-- 3.定义外部模板 -->
<template id="text">
<div>
<p>我是组件1</p>
<p>我是组件2</p>
</div>
</template>
<script>
<!-- 1.创建组件 -->
Vue.component('demo',{
template:"#text"
})
const vm=new Vue({
el:'#app',
data:{
}
})
</script>
</html>
自定义数据和方法
如果组件中需要数据和方法,那么必须把数据定义在当前组件中
注意:组件中定义数据的方法是用函数的方式,return 一个对象,自定义方法使用对象的方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局组件自定义数据和方法</title>
</head>
<body>
<div id="app">
<!-- 2. 使用组件 -->
<demo></demo>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<!-- 定义外部模板 -->
<template id="ext">
<div>
<p>{{text}}</p>
<p @click="fun()">点击显示自定义方法</p>
</div>
</template>
<script>
Vue.component('demo',{
template:"#ext",
data(){
return {
text:"我是自定义数据"
}
},
methods:{
fun(){
console.log("我是自定义方法");
}
}
})
const vm=new Vue({
el:'#app',
data:{
}
})
</script>
</html>
局部组件
作用域:定义在指定的vue实例当中 与el data methods等这些同级
components:{
"组件名":{
template:" ",
data(){
return { }
},
methods:{ }
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<demo></demo>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<template id="exl">
<div>
<h1>{{text}}</h1>
<h1>{{text1}}</h1>
<h1>333</h1>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
components: {
"demo": {
template: "#exl",
data() {
return {
text:"111",
text1:"222"
}
}
}
}
})
</script>
</html>
为什么组件中的data是一个函数?
在vue中,每个vue的组件都是一个实例,通过new Vue()来进行实例化,引用同一个对象。
如果data直接是一个对象的话,那么其中一个组件的数据修改,其他位置上的相同组件也会被修改;
如果data对应一个函数的话就解决了这个问题,因为vue组件的data都是函数,就相当于有了自己的作用域,这样一来,组件被多次调用修改数据,他们也互不干涉
总结
- 组件中的数据定义的时候需用函数返回值的方式来进行模型数据的定义
- 组件的本质就是自定义标签
父子组件
父子组件==》组件中再嵌套一个组件 他的语法其实和传统写组件的时候是一模一样的,只是套在一起
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<!-- 组件模块 -->
<template id="fuTem">
<div>
<h1>我是父组件===={{text}}</h1>
<!-- 调用子组件必须在父组件模板中调用 -->
<son></son>
</div>
</template>
<template id="sonTem">
<div>
<h1>我是子组件===={{text1}}</h1>
</div>
</template>
<script>
new Vue({
el: '#app',
data: {},
components: {
"parent": {
template: "#fuTem",
data() {
return {
text: '我是父组件的数据'
}
},
// 创建子组件
// 子组件是创建在父组件中的
components: {
"son": {
template: '#sonTem',
data() {
return {
text1: '我是子组件的数据'
}
}
}
}
}
}
})
</script>
</html>
父组件能使用子组件的data数据吗? 不能
子组件能使用父组件的data数据吗? 不能
注意:无论是哪种类型的组件 每个组件都相当于是一个完整的vue实例。组件的作用域是完全独立的(组件与组件之间的数据不互通)。父子组件之间的作用域是完全完全完全独立的
如何进行父子组件的通信?
通过props向子组件传递数据
通过事件向父组件发送数据
props
作用:可以让组件接受外部传递进来的数据
子组件要使用父组件的数据(默认是不行的 使用props就可以进行传递)
props的使用:
需要在子组件中定义props,其实就是一个变量(可以接受外部传递进来的数据)
基本用法:
1.定义 在子组件中与data template methods 等同级创建一个props属性
2.使用props变量
3.传递数据 在调用当前组件的使用 以属性的方式传递进
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
<h1>京东秒杀</h1>
<div>
<!-- 3.调用数据 调用的是死数据-->
<!-- <son message1="图片1" message2="价格1" message3="商品描述1"></son>
<son message1="图片2" message2="价格2" message3="商品描述2"></son>
<son message1="图片3" message2="价格3" message3="商品描述3"></son>
<son message1="图片4" message2="价格4" message3="商品描述4"></son> -->
<!-- 3.调用父组件中的单个数据 -->
<!-- <son :message1="img" :message2="price" v-bind:message3="text"></son> -->
<!-- 3.调用父组件中的多个数据 -->
<son v-for="(item,index) in arr" :message1="item.img" :message2="item.price" v-bind:message3="item.text"></son>
</div>
</div>
</template>
<template id="zi">
<div>
<!-- 2.父传子使用props -->
<p>{{message1}}</p>
<p>{{message2}}</p>
<p>{{message3}}</p>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
// img:"图片1",
// price:"价格1",
// text:"商品描述1",
// 多个数据循环
arr:[
{img:"图片1",price:"价格1",text:"商品描述1"},
{img:"图片2",price:"价格2",text:"商品描述2"},
{img:"图片3",price:"价格3",text:"商品描述3"},
{img:"图片4",price:"价格4",text:"商品描述4"},
]
}
},
components:{
"son":{
template:"#zi",
data(){
return {
}
},
// 1.定义props属性 message1用来接受外部传来的图片的数据,message2用来接受外部传来的价格,message3用来接受外部传来的商品描述
props:["message1","message2","message3"]
}
}
}
}
})
</script>
</html>
子级向父级传递
使用自定义事件来完成(必须必须必须要通过事件来触发才能进行传递)
抛出自定义事件监听 语法:this.$emit( “自定义的事件名”,你要传递的数据 )
在当前子组件被调用的时候接收自定义事件的监听
什么时候需要自定义事件呢?
当子组件需要向父组件传递数据时,就要用到自定义事件了。
v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
自定义事件的流程:
在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
点击次数:{{total}}
<son @add='changtotal' @minus='changtotal'></son>
</div>
</template>
<template id="zi">
<div>
<button @click='add'>+1</button>
<button @click='minus'>-1</button>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
total:0
}
},
methods: {
changtotal(count){
this.total=count
}
},
components:{
"son":{
template:"#zi",
data(){
return {
count:0
}
},
methods: {
add(){
this.count++
// console.log(this.count);
this.$emit('add',this.count)
},
minus(){
this.count--
this.$emit('minus',this.count)
}
},
}
}
}
}
})
</script>
</html>
父子组件的访问方式
父组件访问子组件:使用$children或$refs
子组件访问父组件:使用$parent
$children的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs。所有$children不过多介绍。
父子组件的访问方式: $refs
$refs和ref指令通常是一起使用的。首先,我们通过ref给某一个子组件绑定一个特定的ID。其次,通过this.$refs.ID就可以访问到该组件了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
<son></son>
<son></son>
<son ref='aaa'></son>
<button @click='btnClick()'>按钮</button>
</div>
</template>
<template id="zi">
<div>
我是子组件
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
components: {
"parent": {
template: "#fu",
data() {
return {
}
},
methods: {
btnClick(){
console.log(this.$refs.aaa.text)
}
},
components: {
"son": {
template: '#zi',
data() {
return {
text:'子组件文本'
}
}
}
}
},
}
})
</script>
</html>
子组件中直接访问父组件,可以通过$parent
注意事项:
尽管在Vue开发中,允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于调试和维护。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
我是父组件
<son></son>
</div>
</template>
<template id="zi">
<div>
<h2>我是子组件</h2>
<button @click='btnclick'>按钮</button>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {
text:'我是根组件'
},
components: {
"parent": {
template: "#fu",
data() {
return {
name:'我是父组件的名字'
}
},
components: {
"son": {
template: '#zi',
data() {
return {
}
},
methods: {
btnclick(){
// 1.访问父组件$parent
console.log(this.$parent.name);
// 2.访问跟组件$root
console.log(this.$root.text);
}
},
}
}
},
}
})
</script>
</html>
Slot槽口/插槽
组件的插槽:
组件的插槽也是为了让我们封装的组件更加具有扩展性。让使用者可以决定组件内部的一些内容到底展示什么。
可以在组件调用的时候在开标签和关标签中传入数据 进行展示。官方描述:用来混合父组件的内容与子组件自己的模版
栗子:移动网站中的导航栏
移动开发中,几乎每个页面都有导航栏。导航栏我们必然会封装成一个插件,比如nav-bar组件。一旦有了这个组件,我们就可以在多个页面中复用了。
如何封装
最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。是搜索框,还是文字,还是菜单,由调用者自己来决定。
slot基本使用
1.插槽的基本使用<slot></slot>
2.插槽的默认值<slot>button</slot>
3.如果有多个值,同时放入到组件进行替换时,一起作为替换元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
<parent>
<h2>我是插槽的h2替换内容</h2>
<span>我是插槽的span替换内容</span >
</parent>
<parent>
<p>我也是插槽的替换内容</p>
</parent>
<parent></parent>
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
<h1>组件1</h1>
<p>我是组件</p>
<slot><button>按钮</button></slot>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
}
},
methods: {
}
},
}
})
</script>
</html>
具名插槽
当子组件的功能复杂时,子组件的插槽可能并非是一个。比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。这个时候,我们就需要给插槽起一个名字
如何使用具名插槽呢?
slot元素一个name属性即可 <slot name='myslot'></slot>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- 没有传入内容 -->
<parent></parent>
<!-- 传入某一个值 -->
<parent><span slot="center">具名插槽</span></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
<slot name='left'><span>left</span></slot>
<slot name='center'><span>center</span></slot>
<slot name='right'><span>right</span></slot>
</div>
</template>
<script>
const vm=new Vue({
el:'#app',
data:{},
components:{
"parent":{
template:"#fu",
data(){
return {
}
},
methods: {
}
},
}
})
</script>
</html>
作用域插槽
父组件替换插槽的标签,但是内容由子组件来提供。
用途
需要在多个界面进行展示,某些界面是以水平方向一一展示的,某些界面是以列表形式展示的,某些界面直接展示一个数组,ü利用slot作用域插槽就可以了
在父组件使用子组件时,从子组件中拿到数据:
我们通过<template v-slot="slotProps">获取到slotProps属性,在通过slotProps.data就可以获取到刚才传入的data了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<parent></parent>
</div>
</body>
<script src="./node_modules/vue/dist/vue.js"></script>
<!-- 外部模块 -->
<template id="fu">
<div>
<son></son>
<son>
<template v-slot='slotProps'>
<span> {{slotProps.data.join(' * ')}}</span>
</template >
</son>
</div>
</template>
<template id="zi">
<div>
<slot :data='arr'>
<ul>
<li v-for='item in arr'>{{item}}</li>
</ul>
</slot>
</div>
</template>
<script>
const vm = new Vue({
el: '#app',
data: {},
components: {
"parent": {
template: "#fu",
data() {
return {
}
},
methods: {
},
components: {
"son": {
template: '#zi',
data() {
return {
arr: ['aarrhyj', 'bftth', 'cvvf', 'dsss']
}
}
}
}
},
}
})
</script>
</html>