认识组件化
什么是组件化
组件就是对象 组件就是函数 组件就是标签 组件就是插件
组件需要封装 封装就会有数据 有数据就会有操作 有操作就会有交互
组件是分离的思想体现
如果我们将一个页面中所有的处理逻辑全部放在一起,处理起来就会变得非常复杂,而且不利于后续的管理以及扩展。
但如果,我们讲一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护就变得非常容易了
我们将一个完整的页面分成很多个组件
每个组件都用于实现页面的一个功能块
而每一个组件又可以进行细分
Vue组件化思想
组件化是Vue.js中的重要思想
- 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。
- 任何的应用都会被抽象成一颗组件树。
组件化思想的应用
- 有了组件化的思想,我们在之后的开发中就要充分的利用它。
- 尽可能的将页面拆分成一个个小的、可复用的组件。
- 这样让我们的代码更加方便组织和管理,并且扩展性也更强
组件化基础
1.简单组件示例
创建组件构造器对象
template
中是组件的DOM元素内容
注册组件
- 全局注册,通过
Vue.component
。 - 局部注册,通过
components:{cpnc:cpnc}
使用组件
像使用html标签一样使用
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<cpnc></cpnc>
</div>
完整示例
<div id="app">
<!-- 3.使用组件 -->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<cpnc></cpnc>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
<div>
<h2>标题</h2>
<p>内容1...<p>
<p>内容2...<p>
</div>`
})
// 2.注册组件
Vue.component('my-cpn', cpnc)
const app = new Vue({
el:"#app",
data:{
},
components:{//局部组件创建
cpnc:cpnc
}
})
</script>
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是 my-cpn
。我们可以在一个通过 new Vue
创建的 Vue 根实例中,把这个组件作为自定义元素来使用: <my-cpn></my-cpn>
2.注册组件
注册组件的基本步骤
组件的使用分成三个步骤
- 创建组件构造器
- 注册组件
- 使用组件
注册流程案例代码
注册组件步骤解析
Vue.extend():
- 调用
Vue.extend()
创建的是一个组件构造器。 - 通常在创建组件构造器时,传入
template
代表我们自定义组件的模板。 - 该模板就是在使用到组件的地方,要显示的HTML代码。
- 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是语法糖它的原理还是这样的
Vue.component():
- 调用
Vue.component()
是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。 - 所以需要传递两个参数:注册组件的标签名 、组件构造器
组件必须挂载在某个Vue实例下,否则它不会生效
全局组件和局部组件
组件的注册方式有两种,一种是全局组件,一种是局部组件
当我们通过调用Vue.component()注册组件时,组件的注册是全局的。这意味着该组件可以在任意Vue示例下使用。
如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件
<div id="app">
<h2>全局组件</h2>
<my-cpn></my-cpn>
<h2>局部组件</h2>
<cpnc></cpnc>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
<div>
<h2>标题</h2>
<p>内容1</p>
<p>内容2</p>
</div>`
})
// 2.注册组件(全局组件,可以在多个vue实例中使用)
Vue.component('my-cpn', cpnc)
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpnc:cpnc
}
})
</script>
全局组件
全局组件,可以在多个vue实例中使用,类似于全局变量
使用Vue.component('my-cpn', cpnc)
方式注册,直接使用<my-cpn></my-cpn>
调用。my-cpn
是全局组件的名字,cpnc
是定义的组件对象
注册全局组件之后,其他的vue实例都能使用它。实际开发中大多数就有一个vue实例
局部组件
局部组件,只能在当前vue实例挂载的对象中使用,类似于局部变量,有块级作用域
其他实例中就渲染不出来
父组件和子组件
在前面我们看到了组件树:
组件和组件之间存在层级关系
而其中一种非常重要的关系就是父子组件的关系
父子组件错误用法:以子标签的形式在Vue实例中使用
因为当子组件注册到父组件的components时,Vue会编译好父组件的模块
该模板的内容已经决定了父组件将要渲染的HTML(相当于父组件中已经有了子组件中的内容了)
<child-cpn></child-cpn>
是只能在父组件中被识别的
子组件就是在父组件中的组件构造器中注册,并在父组件中使用
注册组件的语法糖
Vue为了简化这个过程,提供了注册的语法糖。
主要是省去了调用Vue.extend()的步骤,而是可以直接使用一个对象来代替。
就是将Vue.extend()
中的对象拿出来,在注册组件的时候 ,用它来替代
全局注册中的使用语法糖
局部注册中使用语法糖
模板的分离写法
我们通过语法糖简化了Vue组件的注册过程,另外还有一个地方的写法比较麻烦,就是template模块写法。
如果我们能将其中的HTML分离出来写,然后挂载到对应的组件上,必然结构会变得非常清晰
Vue提供了两种方案来定义HTML模块内容:
- 使用
<script>
标签 - 使用
<template>
标签
动态组件
组件数据存放
组件可以访问Vue实例数据吗?
- 组件是一个单独功能模块的封装:
- 这个模块有属于自己的HTML模板,也应该有属性自己的数据data。
- n组件中的数据是保存在哪里呢?顶层的Vue实例中吗?
- 我们先来测试一下,组件中能不能直接访问Vue实例中的data
- 我们发现不能访问,而且即使可以访问,如果将所有的数据都放在Vue实例中,Vue实例就会变的非常臃肿。
- 结论:Vue组件应该有自己保存数据的地方
数据的存放
- 组件对象也有一个data属性(也可以有methods等属性,下面我们有用到)
- 只是这个data属性必须是一个函数
- 而且这个函数返回一个对象,对象内部保存着数据
为什么data在组件中必须是一个函数呢?
- 首先,如果不是一个函数,Vue直接就会报错。
- 其次,原因是在于Vue让每个组件对象都返回一个新的对象,因为如果是同一个对象的,组件在多次使用后会相互影响
3.数据传递
父子组件的通信
子组件是不能引用父组件或者Vue实例的数据的。但是,在开发中,往往一些数据确实需要从上层传递到下层:
- 比如在一个页面中,我们从服务器请求到了很多的数据。
- 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
- 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件将数据传递给小组件(子组件)。
如何进行父子组件间的通信呢?Vue官方提到
- 通过props向子组件传递数据
- 通过事件向父组件发送消息
watch实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件通信-父子通信案例(watch实现)</title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<cpn :number1='num1' :number2='num2' @num1change="num1Change" @num2change="num2Change"></cpn>
<h2>父组件{{num1}}</h2>
<input type="text" v-model="num1" >
<h2>父组件{{num2}}</h2>
<input type="text" v-model="num2">
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<h2>{{number1}}</h2>
<input type="text" v-model="dnumber1">
<h2>{{number2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 父传子:props
const cpn = {
template: "#cpn",
data() {
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
props:{
number1:[Number,String],
number2:[Number,String],
},
watch: {
dnumber1(newValue){
this.dnumber1 = newValue * 100
this.$emit('num1change',newValue)
},
dnumber2(newValue){
this.dnumber1 = newValue * 100
this.$emit('num2change',newValue)
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
num1:1,
num2:2,
}
},
methods: {
num1Change(value){
this.num1=value
},
num2Change(value){
this.num1=value
}
},
components: {
cpn
},
})
</script>
</body>
</html>
父级向子级传递数据
在组件中,使用选项props来声明需要从父级接收到的数据。
props基本用法
props的值有两种方式:
- 方式一:字符串数组,数组中的字符串就是传递时的名称。
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
props的值有两种方式:
方式一:字符串数组,数组中的字符串就是传递时的名称。 (在实际开发中,一般都会使用对象来传递)
方式二:对象,对象可以设置传递时的类型,也可以设置默认值等
props数据验证
当需要对props进行类型等验证时,就需要对象写法了
我们说过,除了数组之外,我们也可以使用对象,当需要对props进行类型等验证时,就需要对象写法了。
验证都支持哪些数据类型呢?
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
<div id="app">
<child-cpn v-bind:ctitle="title" :cmessage="message" :cfruits="fruits"></child-cpn>
</div>
<template id="cpn">
<div>
<h1>{{ctitle}}</h1>
<h1>{{cmessage}}</h1>
<ul >
<li v-for="item in cfruits">{{item}}</li>
</ul>
<h1>{{cperson.firstName}}</h1>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册组件
// Vue.component("child-cpn", {
// template: "#cpn",
// props: ["ctitle"]
// })
const app = new Vue({
el: '#app',
data: {
title: "阿清",
message: "kkk",
fruits: ["香蕉", "苹果", "梨"],
person: {
firstName: "wu",
lastName: "zhiguang"
}
},
components: {
'child-cpn': {
template: "#cpn",
props: {
// 基础数据类型检查
// ctitle: String,
// 带有默认值的
// ctitle: {
// type: String,
// default: "wzg"
// },
// 必填字符串
ctitle: {
type: String,
required: true
},
// 基础数据类型检测
cfruits: Array,
// 有默认值的检测
// cfruits: {
// type: Array,
// default () {
// return ['111', '333', '222']
// }
// },
// 自定义检测
cmessage: {
validator(value) {
return ['kkk', 'eee'].indexOf(value) !== -1
}
},
// // 必填
// cfruits: {
// type: Array,
// required: true
// }
}
}
}
})
</script>html
再次练习:
复制<div id="app">
<ccpn :ctitle="title" :cfruits="fruits" :cinfo="info"></ccpn>
<!-- <h1>{{person.name}}</h1> -->
</div>
<template id="cpn">
<div>
<h1>{{ctitle}}</h1>
<ul>
<li v-for="item in cfruits">{{item}}</li>
</ul>
<h2>{{cinfo.age}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
title: "阿清大大",
fruits: ['香蕉', '苹果', '梨', '菠萝'],
info: {
name: "wzg",
age: 19
}
},
components: {
ccpn: {
template: "#cpn",
props: {
ctitle: {
type: String,
default: "柯南",
// required: true
},
cfruits: {
type: Array,
default () {
return ['2', '2', '5']
}
},
cinfo: {
type: Object,
default () {
return {}
}
}
}
}
}
})
</script>
父传子(props的驼峰标识
props里面如果是驼峰命名,使用的时候,要改成中划线
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件通信-父传子(props的驼峰标识)</title>
</head>
<body>
<div id="app">
<!-- v-bind不支持驼峰 :cUser改成 :c-User-->
<!-- <cpn :cUser="user"></cpn> -->
<cpn :c-User="user"></cpn>
<cpn :cuser="user" ></cpn>
</div>
<template id="cpn">
<div>
<!-- 使用驼峰 -->
<h2>{{cUser}}</h2>
<!-- 不使用 -->
<h2>{{cuser}}</h2>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 父传子:props
const cpn = {
template: "#cpn",
props: { //对象写法
//驼峰
cUser:Object,
//未使用驼峰
cuser:Object
},
data() {
return {
}
},
methods: {
},
};
const app = new Vue({
el: "#app",
data: {
user:{
name:'zzz',
age:18,
height:175
}
},
components: {
cpn
}
})
</script>
</body>
</html>
子级向父级传递数据
当子组件需要向父组件传递数据时,就要用到自定义事件了。
我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件
在子组件中 发射父元素中 所自定义的事件
自定义事件的流程:
- 在子组件中,通过
$emit()
来触发事件。 - 在父组件中,通过v-on来监听子组件事件。
子元素只能用等号前面的,等号左边只能小写,等号右边是父组件的
等号左边是子组件的,等号右边是父组件的
在子组件中 发射父元素中 所自定义的事件(等号左边)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--等号左边是子组件的,等号右边是父组件的-->
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories"
@click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.子组件
const cpn = {
template: '#cpn',
data() {
return {
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '家用家电'},
{id: 'ddd', name: '电脑办公'},
]
}
},
methods: {
btnClick(item) {
// 发射事件: 自定义事件
this.$emit('item-click', item)
}
}
}
// 2.父组件
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log('cpnClick', item);
}
}
})
</script>
</body>
</html>
我们来看一个简单里例子:
在真实的项目开发中,我们可能需要子组件向父组价中传递数据,如
案例一:
当我们点击手机数码的时候,会向父组件中传递数据,说明我点击的是手机数码,然后父组件才能去发送请求,来请求数据。
在第二步中,<cpn @item-click="cpnClick"></cpn>
cpnClick 可以不用传参数,不加括号的情况下,使用默认的参数,也就是我们想要传的那个参数item
。因为自定义的响应事件不和浏览器中的click等事件一样,有默认的参数event,自定义事件的默认参数就是我们要传的参数
案例二:
我们之前做过一个两个按钮+1和-1,点击后修改counter。
我们整个操作的过程还是在子组件中完成,但是之后的展示交给父组件。
这样,我们就需要将子组件中的counter,传给父组件的某个属性,比如total。
案例源码:
复制<body>
<div id="app">
<cpn @addclick="counterclick" @lessclick="counterclick"></cpn>
<h1>当前数:{{tot}}</h1>
</div>
<template id="cpnC">
<div>
<button @click="add">+</button>
<button @click="less">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
tot: 0
},
methods: {
counterclick(counter) {
this.tot = counter
}
},
components: {
"cpn": {
template: "#cpnC",
data() {
return {
counter: 0
}
},
methods: {
add() {
this.counter++;
this.$emit("addclick", this.counter)
},
less() {
this.counter--;
this.$emit("lessclick", this.counter)
}
}
}
}
})
</script>
</body>
父子组件的访问
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。
父组件访问子组件:使用 c h i l d r e n 或 children或 children或refs reference(引用)。
子组件访问父组件:使用$parent。
父组件访问子组件
使用$children
开发中较少使用
this.$children
是一个数组类型,它包含所有子组件对象。
再通过下标,找到想要的组件
我们这里通过父组件中的两个按钮,分别取出对应子组件中的数据
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
<button @click="btnclick1">点击1</button>
<button @click="btnclick2">点击2</button>
</div>
<template id="cpnC">
<div>
<h1>我是子组件 1</h1>
</div>
</template>
<template id="cpnCo">
<div>
<h1>我是子组件 2</h1>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component("cpn1", {
template: "#cpnC",
data() {
return {
name: "柯南",
showname() {
console.log("我是子组件1中的name");
}
}
}
})
Vue.component("cpn2", {
template: "#cpnCo",
data() {
return {
name: "新一",
showname() {
console.log("我是子组件2中的name");
}
}
}
})
const app = new Vue({
el: '#app',
data: {
message: 'aqing'
},
methods: {
btnclick1() {
console.log(this.$children[0].name);
this.$children[0].showname();
},
btnclick2() {
console.log(this.$children[1].name);
this.$children[1].showname();
}
},
})
</script>
使用$refs reference(引用) 用的较多
开发中较多使用
$children
的缺陷:
通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用$refs
this.$refs
是一个对象类型,它包含所有子组件对象。
$refs
的使用:
$refs和ref指令通常是一起使用的。
首先,我们通过ref给某一个子组件绑定一个特定的ID。
其次,通过this.$refs.ID就可以访问到该组件了。
子组件访问父组件
使用$parent
如果我们想在子组件中直接访问父组件,可以通过$parent
注意事项:
尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
$root直接找到根
#app
$listeners 找到定义好的事件
根据参数触发
非父子组件通信
非父子组件关系包括多个层级的组件,也包括兄弟组件的关系
-
在Vue1.x的时候,可以通过 d i s p a t c h 和 dispatch和 dispatch和broadcast完成
- $dispatch用于向上级派发事件
- $broadcast用于向下级广播事件
-
但是在Vue2.x都被取消了
- 在Vue2.x中,有一种方案是通过中央事件总线,也就是一个中介来完成。
- 但是这种方案和直接使用Vuex的状态管理方案还是逊色很多。
- 并且Vuex提供了更多好用的功能,所以这里我们暂且不讨论这种方案,后续我们专门学习Vuex的状态管理
生命周期(钩子函数)
beforeCreate 创建之前,data中的数据为underfined
created 数据和事件加载完成(更新数据,可以写在这里面)
el若是不写,需要链式在后面
有template解析template模板中的结构;没有模板,则解析外面的html结构
beforeMount 页面结构加载之前
mounted 页面结构加载完成(事件写在这个里面)
beforeUpdate 更新数据
updated 更新数据到页面
beforeDestroy(页面离开之前,销毁)
destoryed
组件化高级
插槽slot
编译作用域
- 官方对于编译的作用域解析比较简单,我们自己来通过一个例子来理解这个概念:
-
我们来考虑下面的代码是否最终是可以渲染出来的:
<my-cpn v-show="isShow"></my-cpn>
中,我们使用了isShow属性。- isShow属性包含在组件中,也包含在Vue实例中。
-
答案:最终可以渲染出来,也就是使用的是Vue实例的属性。
-
为什么呢?
-
官方给出了一条准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。
-
而我们在使用的时候,整个组件的使用过程是相当于在父组件中出现的。
-
那么他的作用域就是父组件,使用的属性也是属于父组件的属性。
-
因此,父组件中的 isShow使用的是Vue实例中的属性,而不是子组件的属性。子组件中的isShow使用的是子组件中定义的属性。
总结一句话:在哪儿使用,在哪儿找变量
为什么使用插槽slot
组件的插槽:
组件的插槽也是为了让我们封装的组件更加具有扩展性。
让使用者可以决定组件内部的一些内容到底展示什么。
栗子:移动网站中的导航栏。
- 移动开发中,几乎每个页面都有导航栏。
- 导航栏我们必然会封装成一个插件,比如nav-bar组件。
- 一旦有了这个组件,我们就可以在多个页面中复用了
如何封装
-
如何去封装这类的组件呢?
- 它们也很多区别,但是也有很多共性。
- 如果,我们每一个单独去封装一个组件,显然不合适:比如每个页面都返回,这部分内容我们就要重复去封装。
- 但是,如果我们封装成一个,好像也不合理:有些左侧是菜单,有些是返回,有些中间是搜索,有些是文字,等等。
-
如何封装合适呢?抽取共性,保留不同。
- 最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
- 一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
- 是搜索框,还是文字,还是菜单。由调用者自己来决定。
-
这就是为什么我们要学习组件中的插槽slot的原因
slot的基本使用
在子组件中,使用特殊的元素<slot>
就可以为子组件开启一个插槽。
该插槽插入什么内容取决于父组件如何使用。
有了这个插槽后,父组件如何使用呢?
<slot>
中的内容表示,如果没有在该组件中插入任何其他内容,就默认显示该内容
插槽slot 可以有默认值,也可以没有默认值。
在自定义标签中写插入的内容。
插入的内容可以是多个值,同时放入到组件中进行替换,则一起替换默认值。
具名插槽
- 当子组件的功能复杂时,子组件的插槽可能并非是一个。
- 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
- 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
- 这个时候,我们就需要给插槽起一个名字
- 如何使用具名插槽呢?
- 非常简单,只要给slot元素一个name属性即可
<slot name='myslot'></slot>
- 非常简单,只要给slot元素一个name属性即可
slot作用域插槽
什么是作用域插槽呐?
我们先记住一句话:父组件替换插槽的标签,但是内容由子组件来提供。
我们先看一个案例:
1.子组件中包括一组数据,比如:fruits: [‘苹果’, ‘香蕉’, ‘菠萝’, ‘西瓜’, ‘榴莲’]
2.需要在多个界面进行展示:
某些界面是以水平方向一一展示的,
某些界面是以列表形式展示的,
某些界面使用符号将它们连接起来,比如:”苹果 - 香蕉 - 菠萝 - 西瓜 - 榴莲”
我就要思考 数据在子组件中,但是展示方式是由我们父组件来决定的。
第一步:我们需要通过<slot :data ="fruits"></slot>
来将子组件中定义的数据绑定到slot上。
第二步:在父组件中使用子组件的时候,定义一个模板 <template>
(也可以是<div>
),并定义slot-scope="slot"
属性通过slot来引入我们第一步定义的插槽对象。
第三步:我们此时得到数据slot.data
就是子组件中的fruits数据,得到数据slot.data
之后,父组件就可以使用自己的展示方式来渲染这些数据。
复制<span>{{slot.data.join(" — ")}}</span>
注意:
1.(slot-scope="slot"
是自己命名的,比如可以slot-scope="slotProps"
)这第三步的slot.data
就改为slotProps.data
。
2.得到数据slot.data
(data是自己定义的名字)之后。
3.在父组件中使用子组件的时候,定义的模板 <template>
也可以是<div>
到现在我们再来理解一下:**父组件替换插槽的标签,但是内容由子组件来提供。**是不是就清楚了作用域插槽的作用。
案例源码:
<!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">
<cpn>
<template slot-scope="slotProps">
<span>{{slotProps.data.join(" - ")}}</span>
</template>
</cpn>
<cpn>
<template slot-scope="slotProps">
<span>{{slotProps.data.join(" * ")}}</span>
</template></cpn>
<cpn>
<template slot-scope="slotProps">
<ul>
<li v-for="item in slotProps.data">{{item}}</li>
</ul>
</template>
</cpn>
</div>
<template id="cpnC">
<div>
<slot :data ="fruits"></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {},
components: {
cpn: {
template: "#cpnC",
data() {
return {
fruits: ['苹果', '香蕉', '菠萝', '西瓜', '榴莲']
}
}
}
}
})
</script>
</body>
</html>