|
|
51CTO旗下网站
|
|
移动端

Vue.js 父子组件通讯的十种办法

无可否认,现在不管大厂仍是小厂都现已用上了 Vue.js 结构,简略易上手不说,教程翔实,社区活泼,第三方套件还多。

作者:gongph来历:前端大牛爱好者|2019-05-29 14:23

 

面试官:Vue 中父子组件通讯有哪些办法?

自己先想一分钟。

无可否认,现在不管大厂仍是小厂都现已用上了 Vue.js 结构,简略易上手不说,教程翔实,社区活泼,第三方套件还多。

真的是前端开发人员必备技能。

而且在面试傍边也往往会问到关于 Vue 方面的各种问题,其间大部分面试官会问到如上这种问题。

最近一向在做 Vue项目代码层面上的优化,说实话,优化他人的代码真是件苦楚的工作,功用完成姑且不说,就说代码规范我就能再写出一篇文章来。

真的是无规范不成方圆,规范这个东西太重要了!

有点扯了,回到主题,咳咳,那就谈谈我对上面的面试题的了解吧,文笔有限,不当之处,欢迎在文章完毕留言纠正啊,正啊,啊!

概述

几种通讯办法无外乎以下几种:

  • Prop(常用)
  • $emit (组件封装用的较多)
  • .sync语法糖 (较少)
  • $attrs 和 $listeners (组件封装用的较多)
  • provide 和 inject (高阶组件/组件库用的较多)
  • 其他办法通讯

胪陈

下面逐一介绍,大神请绕行。

1. Prop

英式发音:[prɒp]。

这个在咱们日常开发傍边用到的十分多。

简略来说,咱们能够经过 Prop 向子组件传递数据。

用一个形象的比方来说,父子组件之间的数据传递相当于自上而下的下水管子,只能从上往下流,不能逆流。

这也正是 Vue 的规划理念之单向数据流。

而 Prop 正是管道与管道之间的一个衔接口,这样水(数据)才干往下流。说这么多,看代码:

  1. <div id="app">  <child :content="message"></child></div> 
  2. // Js 
  3. let Child = Vue.extend({ 
  4.   template: '<h2>{{ content }}</h2>'
  5.   props: { 
  6.     content: { 
  7.       type: String, 
  8.       default: () => { return 'from child' } 
  9.     } 
  10.   } 
  11. }) 
  12.  
  13. new Vue({ 
  14.   el: '#app'
  15.   data: { 
  16.     message: 'from parent' 
  17.   }, 
  18.   components: { 
  19.     Child 
  20.   } 
  21. }) 

 

浏览器输出:

  1. from parent 

2. $emit

英式发音:[iˈmɪt]。

官方说法是触发当时实例上的事情。

附加参数都会传给监听器回调。

依照我的了解不知道能不能给咱们说理解,先简略看下代码吧:

  1. <div id="app">  <my-button @greet="sayHi"></my-button></div> 
  2. let MyButton = Vue.extend({ 
  3.   template: '<button @click="triggerClick">click</button>'
  4.   data () { 
  5.     return { 
  6.       greeting: 'vue.js!' 
  7.     } 
  8.   }, 
  9.   methods: { 
  10.     triggerClick () { 
  11.       this.$emit('greet', this.greeting) 
  12.     } 
  13.   } 
  14. }) 
  15.  
  16. new Vue({ 
  17.   el: '#app'
  18.   components: { 
  19.     MyButton 
  20.   }, 
  21.   methods: { 
  22.     sayHi (val) { 
  23.       alert('Hi, ' + val) // 'Hi, vue.js!' 
  24.     } 
  25.   } 
  26. }) 

大致逻辑是酱婶儿的:当我在页面上点击按钮时,触发了组件 MyButton 上的监听事情 greet,而且把参数传给了回调函数 sayHi 。

说白了,当咱们从子组件 Emit(派发) 一个事情之前,其内部都提早在事情队列中 On(监听)了这个事情及其监听回调。其实相当于下面这种写法:

  1. vm.$on('greet'function sayHi (val) {  console.log('Hi, ' + val)})vm.$emit('greet''vue.js')// => "Hi, vue.js" 

3. .sync 润饰符

这个家伙在 [email protected] 的时分曾作为双向绑定功用存在,即子组件能够修正父组件中的值。

由于它违反了单向数据流的规划理念,所以在 [email protected] 的时分被干掉了。

可是在 [email protected]+ 以上版别又从头引进了这个 .sync 润饰符。

可是这次它仅仅作为一个编译时的语法糖存在。

它会被扩展为一个自动更新父组件特色的 v-on 监听器。

说白了便是让咱们手动进行更新父组件中的值了,从而使数据改动来历愈加的显着。

下面引进自官方的一段话:

在有些状况下,咱们或许需求对一个 prop 进行“双向绑定”。

不幸的是,真实的双向绑定会带来保护上的问题,由于子组件能够修正父组件,且在父组件和子组件都没有显着的改动来历。

已然作为一个语法糖,肯定是某种写法的简写办法,哪种写法呢,看代码:

  1. <text-document  v-bind:title="doc.title"  v-on:update:title="doc.title = $event"></text-document> 

所以咱们能够用 .sync 语法糖简写成如下办法:

  1. <text-document v-bind:title.sync="doc.title"></text-document> 

废话这么多,怎样做到“双向绑定” 呢?

让咱们进段广告,广告之后愈加精彩!

... 好的,欢迎回来。

假设咱们想完成这样一个效果:改动子组件文本框中的值一同改动父组件中的值。

怎样做?列位无妨先想想。先看段代码:

  1. <div id="app">  <login :name.sync="userName"></login> {{ userName }}</div> 
  2. let Login = Vue.extend({ 
  3.   template: ` 
  4.     <div class="input-group"
  5.       <label>名字:</label> 
  6.       <input v-model="text"
  7.     </div> 
  8.   `, 
  9.   props: ['name'], 
  10.   data () { 
  11.     return { 
  12.       text: '' 
  13.     } 
  14.   }, 
  15.   watch: { 
  16.     text (newVal) { 
  17.       this.$emit('update:name', newVal) 
  18.     } 
  19.   } 
  20. }) 
  21.  
  22. new Vue({ 
  23.   el: '#app'
  24.   data: { 
  25.     userName: '' 
  26.   }, 
  27.   components: { 
  28.     Login 
  29.   } 
  30. }) 

下面划要点,代码里有这一句话:

  1. this.$emit('update:name', newVal) 

官方语法是:update:myPropName 其间 myPropName 表明要更新的 prop 值。

当然假如你不必 .sync 语法糖运用上面的 .$emit 也能到达相同的效果。仅此而已!

4. $attrs 和 $listeners

  • 官网对 $attrs 的解说如下:

包括了父效果域中不作为 prop 被辨认 (且获取) 的特性绑定 (class 和 style 在外)。

当一个组件没有声明任何 prop 时,这儿会包括一切父效果域的绑定 (class 和 style 在外),而且能够经过 v-bind="$attrs" 传入内部组件——在创立高档其他组件时十分有用。

  • 官网对 $listeners 的解说如下:

包括了父效果域中的 (不含 .native 润饰器的) v-on 事情监听器。

它能够经过 v-on="$listeners" 传入内部组件——在创立更高层次的组件时十分有用。

我觉得 $attrs 和 $listeners 特色像两个收纳箱,一个担任收纳特色,一个担任收纳事情,都是以目标的办法来保存数据。

看下面的代码解说:

  1. <div id="app">  <child    :foo="foo"    :bar="bar"    @one.native="triggerOne"    @two="triggerTwo">  </child></div> 

从 Html 中能够看到,这儿有俩特色和俩办法,区别是特色一个是 prop 声明,事情一个是 .native 润饰器。

  1. let Child = Vue.extend({ 
  2.   template: '<h2>{{ foo }}</h2>'
  3.   props: ['foo'], 
  4.   created () { 
  5.     console.log(this.$attrs, this.$listeners) 
  6.     // -> {bar: "parent bar"
  7.     // -> {two: fn} 
  8.  
  9.  
  10.     // 这儿咱们拜访父组件中的 `triggerTwo` 办法 
  11.     this.$listeners.two() 
  12.     // -> 'two' 
  13.   } 
  14. }) 
  15.  
  16. new Vue({ 
  17.   el: '#app'
  18.   data: { 
  19.     foo: 'parent foo'
  20.     bar: 'parent bar' 
  21.   }, 
  22.   components: { 
  23.     Child 
  24.   }, 
  25.   methods: { 
  26.       triggerOne () { 
  27.       alert('one'
  28.     }, 
  29.     triggerTwo () { 
  30.       alert('two'
  31.     } 
  32.   } 
  33. }) 

能够看到,咱们能够经过 $attrs 和 $listeners 进行数据传递,在需求的当地进行调用和处理,仍是很便利的。

当然,咱们还能够经过 v-on="$listeners" 一级级的往下传递,子子孙孙无穷尽也!

一个插曲!

当咱们在组件上赋予了一个非Prop 声明时,编译之后的代码会把这些个特色都当成原始特色对待,添加到 html 原生标签上,看上面的代码编译之后的姿态:

  1. <h2 bar="parent bar">parent foo</h2> 

这样会很丑陋,一同也爆了某些东西。

怎样去掉?

这正是 inheritAttrs 特色的用武之地!

给组件加上这个特色就行了,一般是合作 $attrs 运用。

看代码:

  1. // 源码let Child = Vue.extend({  ...  inheritAttrs: false, // 默许是 true  ...}) 

再次编译:

  1. <h2>parent foo</h2> 

5. provide / inject

他俩是对CP, 感觉挺奥秘的。

来看下官方对 provide / inject 的描绘:

provide 和 inject 主要为高阶插件/组件库供给用例。

并不引荐直接用于运用程序代码中。

而且这对选项需求一同运用,以答应一个先人组件向其一切子孙后代注入一个依靠,不管组件层次有多深,并在起上下游联系建立的时间里一直收效。

看完描绘有点懵懵懂懂!

一句话总结便是:小时分你老爸什么东西都先帮你存着等你长大该娶媳妇儿了你要房子给你买要车给你买只需他有的尽量都会满意你。

下面是这句话的代码解说:

  1. <div id="app">  <son></son></div> 
  2. let Son = Vue.extend({ 
  3.   template: '<h2>son</h2>'
  4.   inject: { 
  5.     house: { 
  6.       default'没房' 
  7.     }, 
  8.     car: { 
  9.       default'没车' 
  10.     }, 
  11.     money: { 
  12.       // 长大工作了尽管有点钱 
  13.       // 仅供生活费,需求向爸爸妈妈要 
  14.       default'¥4500' 
  15.     } 
  16.   }, 
  17.   created () { 
  18.     console.log(this.house, this.car, this.money) 
  19.     // -> '房子''车子''¥10000' 
  20.   } 
  21. }) 
  22.  
  23. new Vue({ 
  24.   el: '#app'
  25.   provide: { 
  26.     house: '房子'
  27.     car: '车子'
  28.     money: '¥10000' 
  29.   }, 
  30.  
  31.   components: { 
  32.     Son 
  33.   } 
  34. }) 

6. 其他办法通讯

除了以上五种办法外,其实还有:

  • EventBus

思路便是声明一个大局Vue实例变量 EventBus , 把一切的通讯数据,事情监听都存储到这个变量上。

这样就到达在组件间数据同享了,有点类似于 Vuex。

但这种办法只适用于极小的项目,杂乱项目仍是引荐 Vuex。

下面是完成 EventBus 的简略代码:

  •  
  1. <div id="app">  <child></child></div> 
  2. // 大局变量 
  3. let EventBus = new Vue() 
  4.  
  5. // 子组件 
  6. let Child = Vue.extend({ 
  7.   template: '<h2>child</h2>'
  8.   created () { 
  9.     console.log(EventBus.message) 
  10.     // -> 'hello' 
  11.     EventBus.$emit('received''from child'
  12.   } 
  13. }) 
  14.  
  15. new Vue({ 
  16.   el: '#app'
  17.   components: { 
  18.     Child 
  19.   }, 
  20.   created () { 
  21.     // 变量保存 
  22.     EventBus.message = 'hello' 
  23.     // 事情监听 
  24.     EventBus.$on('received'function (val) { 
  25.       console.log('received: '+ val) 
  26.       // -> 'received: from child' 
  27.     }) 
  28.   } 
  29. }) 
  • Vuex

官方引荐的,Vuex 是一个专为 Vue.js 运用程序开发的状况管理模式。

  • $parent

父实例,假如当时实例有的话。

经过拜访父实例也能进行数据之间的交互,但极小状况下会直接修正父组件中的数据。

  • $root

当时组件树的根 Vue 实例。

假如当时实例没有父实例,此实例将会是其自己。

经过拜访根组件也能进行数据之间的交互,但极小状况下会直接修正父组件中的数据。

  • broadcast / dispatch

他俩是 [email protected] 中的办法,分别是事情播送 和 事情派发。

尽管 [email protected] 里边删掉了,但能够模仿这两个办法。

能够学习 Element 完成。

有时分仍是十分有用的,比方咱们在开发树形组件的时分等等。

总结

烦琐了这么多,期望看到的同学或多或少有点收成吧。

不对的当地还请留言纠正,不胜感激。

父子组件间的通讯其实有很多种,就看你在哪些状况下去用。

不同场景不同对待。

条件是你要心中有数才行!

经过大神之路还有很远,只需每天看看社区,看看文档,写写Demo,每天前进一点点,总会有收成的。

【修改引荐】

  1. 5G年代通讯工程师转型之路
  2. 网络通讯协议之TCP
  3. 从TCP协议到TCP通讯的各种异常现象和剖析(上)
  4. 从TCP协议到TCP通讯的各种异常现象和剖析(下)
  5. 聊聊我国的通讯职业:从“七国八制”到“中华”脊柱
【责任修改:武晓燕 TEL:(010)68476606】

点赞 0
同享:
咱们都在看
猜你喜爱

订阅专栏+更多

Spring Boot 爬虫查找轻松游

Spring Boot 爬虫查找轻松游

全栈式开发之旅
共4章 | 美码师

75人订阅学习

Linux功能调优攻略

Linux功能调优攻略

功能调优规范
共15章 | 南非蚂蚁

206人订阅学习

VMware vSphere常见毛病

VMware vSphere常见毛病

搞定vSphere
共18章 | 王春海

84人订阅学习

读 书 +更多

规范C++开发入门与编程实践

本书侧重介绍规范C++言语,即1998年由ISO正式推出的关于C++的国际性规范版别。 本书从最根底的编程言语概念讲起,共分6篇24章。前4篇完好...

订阅51CTO邮刊

点击这儿检查样刊

订阅51CTO邮刊

51CTO服务号

51CTO播客