一、父子组件的访问方式
父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问跟组件。
父组件访问子组件:使用 c h i l d r e n 或 children或 children或refs
子组件访问父组件:使用 p a r e n t 我 们 先 来 看 下 parent 我们先来看下 parent我们先来看下children的访问
this.$children是一个数组类型,它包含所有子组件对象。
一、如代码父组件访问子组件的值与调用子组件的方法
<div id="app">
<cpn></cpn> //这里开了两个子组件标签
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
console.log(this.$children); //再点击的时候访问子组件,this.$children,访问到是两个子组件数组,数组里面装的是子组件
for (let c of this.$children) { //遍历这个数组
console.log(c.name); //访问它的值
c.showMessage(); //调用它的方法
}
console.log(this.$children[1].name); //访问第二个子组件的值
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name' //子组件的值
}
},
methods: {
showMessage() { //子组件的方法
console.log('showMessage');
}
}
},
}
})
</script>
c h i l d r e n 的 缺 陷 : 通 过 children的缺陷: 通过 children的缺陷:通过children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化(比如再数组中间插一个值,使数组序列号发生变化)。
二、 r e f s 的 使 用 : 有 时 候 , 我 们 想 明 确 获 取 其 中 一 个 特 定 的 组 件 , 这 个 时 候 就 可 以 使 用 refs的使用: 有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用 refs的使用:有时候,我们想明确获取其中一个特定的组件,这个时候就可以使用refs
r e f s 和 r e f 指 令 通 常 是 一 起 使 用 的 。 首 先 , 我 们 通 过 r e f 给 某 一 个 子 组 件 绑 定 一 个 特 定 的 I D 。 其 次 , 通 过 t h i s . refs和ref指令通常是一起使用的。 首先,我们通过ref给某一个子组件绑定一个特定的ID。 其次,通过this. refs和ref指令通常是一起使用的。首先,我们通过ref给某一个子组件绑定一个特定的ID。其次,通过this.refs.ID就可以访问到该组件了。
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn> //这里使用ref 属性给这个子组件定义个标识符
<button @click="btnClick">按钮</button>
</div>
<template id="cpn">
<div>我是子组件</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
// 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
console.log(this.$refs.aaa.name); //再使用的时候,通过this.$refs.标识符.属性,就可以获取这个属性的值,
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
}
},
}
})
</script>
三、父子组件的访问方式: p a r e n t 如 果 我 们 想 在 子 组 件 中 直 接 访 问 父 组 件 , 可 以 通 过 parent 如果我们想在子组件中直接访问父组件,可以通过 parent如果我们想在子组件中直接访问父组件,可以通过parent
注意事项:
尽管在Vue开发中,我们允许通过 p a r e n t 来 访 问 父 组 件 , 但 是 在 真 实 开 发 中 尽 量 不 要 这 样 做 。 子 组 件 应 该 尽 量 避 免 直 接 访 问 父 组 件 的 数 据 , 因 为 这 样 耦 合 度 太 高 了 。 如 果 我 们 将 子 组 件 放 在 另 外 一 个 组 件 之 内 , 很 可 能 该 父 组 件 没 有 对 应 的 属 性 , 往 往 会 引 起 问 题 。 另 外 , 更 不 好 做 的 是 通 过 parent来访问父组件,但是在真实开发中尽量不要这样做。 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。 如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。 另外,更不好做的是通过 parent来访问父组件,但是在真实开发中尽量不要这样做。子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。另外,更不好做的是通过parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
// 1.访问父组件$parent
// console.log(this.$parent);
// console.log(this.$parent.name);
// 2.访问根组件$root
console.log(this.$root); //根组件是vue , 而其他父组件是vuecomponent
console.log(this.$root.message);
}
}
}
}
}
}
})
二、组件化高级开发
一、slot插槽
插槽的意思与作用:在生活中很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。
插槽的目的是让我们原来的设备具备更多的扩展性。
比如电脑的USB我们可以插入U盘、硬盘、手机、音响、键盘、鼠标等等。
组件的插槽:
组件的插槽也是为了让我们封装的组件更加具有扩展性。
让使用者可以决定组件内部的一些内容到底展示什么。
栗子:移动网站中的导航栏。
移动开发中,几乎每个页面都有导航栏。
导航栏我们必然会封装成一个插件,比如nav-bar组件。
一旦有了这个组件,我们就可以在多个页面中复用了。
最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。
一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。
是搜索框,还是文字,还是菜单。由调用者自己来决定。
二、slot基本使用
1.插槽的基本使用
2.插槽的默认值 button
3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
1、基本使用,再使用组件的时候,为呈现每个组件不同的样子,而现在插槽的功能就出现了,如果模板那里没有 的时候, 里面里面放的标签是不会显示的
<div id="app">
<cpn><span>哈哈哈</span></cpn> //2、这里面的标签才能实现
<cpn><i>呵呵呵</i></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件, 哈哈哈</p>
<slot></slot> //1、这里使用了插槽
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
2、如果组件cpn标签里没有如标签内容的时候,就会把里面的内容去变成cpn标签里面的内容,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn><span>哈哈哈</span></cpn>
<cpn><i>呵呵呵</i></cpn>
<cpn> //这是再组件内设置多组值
<i>呵呵呵</i>
<div>我是div元素</div>
<p>我是p元素</p>
</cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>我是组件, 哈哈哈</p>
<slot><button>按钮</button></slot> //这里设置了,如果组件标签内 没有值,那么插槽内的值就会就会是组件内的值,如果有值,就呈现值
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn'
}
}
})
</script>
</body>
</html>
3、多个插槽的情况
如代码,此时有三个插槽,由结果图片可以看到,由于只有一个cpn组件,所以这三个插槽的内容就像一个插槽里的三个内容一样,都会传给cpn里面
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<slot ><span>左边</span></slot>
<slot ><span>中间</span></slot>
<slot ><span>右边</span></slot>
</div>
</template>
那多个插槽多个cpn呢
如代码,由结果可以看到,每一个cpn都能得到所有的插槽
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<slot ><span>左边</span></slot>
<slot ><span>中间</span></slot>
<slot ><span>右边</span></slot>
</div>
</template>
4、具名插槽的使用
当子组件的功能复杂时,子组件的插槽可能并非是一个。
比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。
那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?
这个时候,我们就需要给插槽起一个名字
如何使用具名插槽呢?
给slot元素一个name属性即可
然后通过name去控制具体的某个插槽的显示
比较复杂,我们一步步来研究
首先看代码,结果是替换cpn里面的内容没有替换一个插槽,原因是它的span标签带有对应具名插槽得到slot属性
<div id="app">
<cpn><span slot="aaa">标题</span></cpn>
</div>
<template id="cpn">
<div>
<slot ><span>左边</span></slot>
<slot ><span>中间</span></slot>
<slot ><span>右边</span></slot>
</div>
</template>
再看代码,这时候,带有name属性的并且值对应的插槽被对应的cpn替换了,其他插槽五影响,只替换有对应属性的
<div id="app">
<cpn><span slot="aaa">标题</span></cpn>
</div>
<template id="cpn">
<div>
<slot name="aaa"><span >左边</span></slot>
<slot ><span>中间</span></slot>
<slot ><span>右边</span></slot>
</div>
</template>
编译作用域
这是个什么东西呢,看代码,下面就是验证父组件的作用域和子组件的作用域再哪,然后说明父组件的作用域不能直接用子组件里面的属性,子组件同理,所以下面代码的cpn是能够显示东西的
<div id="app"> //这个app是父组件的东西,属于父组件作用域
<cpn v-show="isShow"></cpn> //v-show决定是否显示
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<p>我是内容, 哈哈哈</p>
<button v-show="isShow">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isShow: true //父组件定义一个ture值的 isShow
},
components: {
cpn: {
template: '#cpn',
data() {
return {
isShow: false //子组件定义一个false值的 isShow
}
}
},
}
})
作用域插槽的使用
如代码
<div id="app">
<cpn>
<template slot-scope="slot"> //2、调用那些存储起来的数据需要用到template和slot-scope属性,就可以调用了
<span>{{slot.data.join(' - ')}}</span> //3、这样子调用
</template>
</cpn>
<!--<cpn></cpn>-->
</div>
<template id="cpn">
<div>
<slot :data="pLanguages"> //1、再子组件的模板的插槽里,用来把子组件的数据存储起来,data是自定义的
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
}
}
}
}
})
上面这个案例用一句话概括,父组件替换插槽的标签,但是内容由子组件来提供。