2、使用$emit('事件名称',值)向父组件传值。
这种方式就是在原来的基础上可以传递参数了。需要注意的是在父组件中接受该值的时候要使用$event来接受,也就是说子组件向父组件传递的数据实际上是个事件对象。
3、非父子组件之间的数据传递
非父子组件之间的数据传递其实指的就是兄弟组件之间或者是完全无关的两个组件之间的数据的传递(例如A组件和B组件是兄弟组件,而A组件和B组件的自组件关系就比较远了,因此这两个组件就是完全无关系的组件)。
1、兄弟组件传值
兄弟组件之间传值可以将数据传递到父组件进行中转。假设存在兄弟组件A、B,如果子组件A向子组件B传递数据首先A组件要向父组件传递数据,父组件接受到数据之后将数据传递给子组件B。
子组件向父组件传值:$emit
父组件向子组件传值:props
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>组件</title> </head> <body> <script src="../js/vue.js"></script> <div id="app"> <!--4.父组件捕获子组件A发射的事件,并获取子组件A的值,然后将获取的值传递给父组件的value--> <com-a @btna="value=$event"></com-a> <!--5.这里的value是从父组件获取的,是组件A传递到父组件的--> <p>父组件:{{value}}</p> <!--6.父组件将值value值传递给另一个变量value上--> <com-b :value="value"></com-b> </div> <!--A租几间--> <template id="tempA"> <div> <!--1.获取子组件A中的内容--> <p>组件A:{{value}}</p> <!--2.btna事件触发--> <button @click="btna(value)">发送</button> </div> </template> <!--B组件--> <template id="tempB"> <div> <!--8.子组件B模板获取子组件B中的value--> <p>组件B:{{value}}</p> </div> </template> <script> const v = new Vue({ el:"#app", data:{ //value用来进行数据中转 value:'' }, methods:{ fu(){ this.value=$event; } }, //注册子组件 components:{ "com-a":{ template:`#tempA`, data(){ return{ value: "hahah" } }, methods:{ /*3.将触发事件发射出去*/ btna(value){ this.$emit("btna",value) } } }, "com-b":{ /*7.子组件B获取value值供子组件B的模板使用*/ props:["value"], template: `#tempB` } } }) </script> </body> </html>
2、完全无关的组件之间的传值(EventBus)
上面的兄弟组件使用了父组件中转传值,首先A组件使用$emit向父组件传值,父组件接受到值后使用props向B组件传值,但是如果层级不只有一层的话该怎么办呢?很显然使用父组件中转传值的话就比较麻烦了,此时就需要用到EventBus.
何为EventBus?
1、EventBus是一个独立的事件中心,用于管理不同组件之间的传值操作。简单来说,EventBus更像是个邮递员,主要负责将组件A的数据传递给组件B,而他本身是不存储数据的。只是负责中转处理。
2、EventBus是通过一个新的vue实例来管理组件传值操作,组件通过给实例注册事件、调用事件来实现数据传递。因为在实际开发中vue实例会书写很多功能代码,其复杂度和体积可能非常巨大,如果使用该vue实例进行数据传递的话可能消耗的性能会非常大,因此需要一个新的vue实例来进行传值操作。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>组件</title> </head> <body> <script src="../js/vue.js"></script> <div id="app"> <coma></coma> <comb></comb> </div> <script> /*事件管理中心bus*/ const bus = new Vue(); /*全局组件A*/ Vue.component('coma',{ template:` <div> <p>苹果,数量是:{{ counta }}</p> <button @click="btnclick">+1</button> </div>> `, data(){ return{ counta:0 } }, methods:{ btnclick(){ bus.$emit('btnclick',1); this.counta++ } } }) /*全局组件B*/ Vue.component('comb',{ template: ` <p>数量是:{{ countb }}</p> `, data(){ return{ countb:0 } }, created(){ /*给bus注册事件并接受数据,$on('事件名称',事件处理程序)*/ bus.$on('btnclick',(counta)=>{ /*实例创建完毕可以使用data等功能了*/ this.countb+=counta }) } }) /*根组件*/ const v = new Vue({ el:"#app", data:{ message:"你好啊!" } }) </script> </body> </html>
8、插槽
1、何为插槽?
插槽,英文名是slot.很容易让人联想到电脑上,手机上的形形色色的插槽,这些插槽有的链接着音响有的链接着鼠标、有的链接着键盘…… 组件的插槽能使我们封装的组件更有扩展性可以让使用者决定组件内部到底需要展示哪些东西。
2、slot的基本使用
我们可以对比网购商城的页面来深化一下对组件插槽的理解,网购商城的每个页面基本上都会有一个导航栏或者说是搜索框,但是这些搜索框在每个页面中的显示的效果是不一样的,因次我们说这些页面都有一个共性,那就是都包含有个搜索框组件,但是每个搜索框的显示样式又是不同的,说白了就是每个搜索框的结构是一样的但是内容不一样。所以我们说这个组件使用了插槽,在不同页面中插槽引入了不同的样式展示给用户。在开发中我们将共性封装成组件,不同的地方定义成插槽。
小案例:在第一次使用组件的时候使用button,第二次使用组件的时候不想显示button按钮而是显示span标签。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>slot</title> </head> <body> <script src="../js/vue.js"></script> <div id="app"> {{message}} <!--相当于是在cpc中将标签写入到插槽--> <cpc><button>按钮</button></cpc> <cpc><span>span</span></cpc> <cpc></cpc> <cpc></cpc> </div> <template id="cpc"> <dov> <h1>我是组件</h1> <p>我是组件哈啊哈</p> <!--定义个插槽,就相当于是电脑上的预留的插槽--> <slot></slot> </dov> </template> <script> const v = new Vue({ el:"#app", data:{ message:"你好啊!" }, //注册子组件 components:{ "cpc":{ template:`#cpc` } } }) </script> </body> </html>
代码运行结果:
如果在插槽中设置了默认标签的话,是会显示标签中的内容的。要是标签中有自己的内容的话那么就会覆盖掉原来的标签中的内容。
<div id="app"> {{message}} <!--相当于是在cpc中将标签写入到插槽--> <cpc></cpc> <cpc><span>不显示按钮,显示自己的内容!</span></cpc> <cpc></cpc> <cpc><span>不显示按钮,显示自己的内容!</span></cpc> </div> <template id="cpc"> <dov> <h1>我是组件</h1> <p>我是组件哈啊哈</p> <!--定义个插槽,就相当于是电脑上的预留的插槽--> <slot><button>按钮</button></slot> </dov> </template>
运行结果:
3、具名slot的使用
所谓的具名插槽就是为每个插槽取个具体的名字。
如果说组件中存在三个插槽,标签中又有自己的内容那么,标签中的内容会将所有的标签中的内容替换。这时候要想自定义替换插槽中的内容的话就需要给插槽取名字。这时候标签中自己的内容只会替换掉没有名字的插槽。
以上面购物商城的搜索框为例,具名插槽就是为了方面在展示不同页面的时候,导航栏的左边替换成什么,中间部分替换成什么,右边部分替换成什么。
<div id="app"> {{message}} <cpc><span slot="left">替换左边的</span></cpc> </div> <template id="temp"> <div> <slot name="left"><span>左边</span></slot> <slot name="center"><span>中间</span></slot> <slot name="right"><span>右边</span></slot> </div> </template>
代码运行结果:
4、编译作用域
何为【编译作用域】?
简单理解就是变量的作用域!通过代码简单说明一下。
官方给出的准则:父组件模板中的所有东西都会在父级作用域内编译,子组件模板的所有东西都会在子级作用域内编译。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script src="../js/vue.js"></script> <div id="app"> {{message}} <cpc v-show="isShow"></cpc> </div> <template id="temp"> <div> <h1>我是组件</h1> <p>我是组件</p> </div> </template> <script> const v = new Vue({ el:"#app", data:{ message:"你好啊!", isShow:true }, //注册局部组件 components:{ "cpc":{ template:`#temp`, data(){ return{ isShow:false } } } } }) </script> </body> </html>
代码运行结果:
上面的代码中vue实例和其子组件各有一个布尔类型的变量isShow ,在<cpc v-show="isSow"></cpc>使用了该变量isShow,但是这个变量究竟是使用的vue实例中的变量呢?还是使用的vue子组件中的编变量呢?答案是:vue实例中的变量。
那为啥是vue组件中的变量呢?
其实不光是这一个变量,只要是在vue实例中出现的变量,在使用时都会首先取vue实例中寻找,也就是说此时的<cpc><cpc/>就和普通的div没有区别。姑且将他看成时div。这样就在情理之中了。如果是在模板中使用isShow变量时,会优先从子组件中寻找该变量。代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script src="../js/vue.js"></script> <div id="app"> {{message}} <!--去vue实例中寻找变量--> <cpc v-show="isShow"></cpc> </div> <template id="temp"> <div> <h1>我是组件</h1> <p>我是组件</p> <!--去子组件中寻找该变量--> <button v-show="isShow">按钮</button> </div> </template> <script> const v = new Vue({ el:"#app", data:{ message:"你好啊!", isShow:true }, //注册局部组件 components:{ "cpc":{ template:`#temp`, data(){ return{ isShow:false } } } } }) </script> </body> </html>
代码运行结果:发现button未展示。
5、作用域插槽(*较难理解)
何为作用域插槽?
作用域插槽就是父组件替换插槽中的标签,但是内容是由子组件来提供的。
小案例:将第二组数据展示的形式由原来的列表变为元素之前以”-"链接。
因为我们想让第二个<cpc></cpc>展示的数据格式发生变化所以我们很容易想到让子组件模板里面的数据放到插槽中,当用户使用模板时默认展示插槽中的数据,用户在模板标签中自定义内容时就会展示用户自定义的内容。下面是代码演示:
<div id="app"> 第一组数据 <cpc></cpc> 第二组数据 <cpc> <span>java-</span> <span>vue-</span> <span>go-</span> <span>swift-</span> <span>c#-</span> <span>c++-</span> <span>javascript-</span> </cpc> 第三组数据 <cpc></cpc> </div> <template id="temp"> <!--以列表的形式展示子组件的数据--> <div> <slot> <ul> <li v-for="language in languages">{{language}}</li> </ul> </slot> </div> </template>
代码运行结果:
看吧,第二组数据的值确实是变化了。一二组数据使用的还是插槽中的数据。但是为什么不直接使用v-for遍历子组件中的数据?一个个写span标签岂不是太麻烦?还真不能用v-for遍历子组件中的数据,因为我们是在vue模板中使用的插槽,要想在vue模板中使用变量那么该变量一定得是vue实例中存在的变量。就此案例而言vue实例中不存在languages数组,因此我们只能一个一个地写span完成需求。当然在实际开发中我们想到的肯定是如何将子组件的数据传递到父组件中。下面演示子组件传递数据到父组件(未完待续……)