Vue.js 父子组件通信的十种方式

面试官:Vue 中父子组件通信有哪些方式?

自己先想一分钟。

无可否认,现在无论大厂还是小厂都已经用上了Vue.js 框架,简单易上手不说,教程详尽,社区活跃,第三方套件还多。真的是前端开发人员必备技能。而且在面试当中也往往会问到关于 Vue 方面的各种问题,其中大部分面试官会问到如上这种问题。

最近一直在做 Vue项目代码层面上的优化,说实话,优化别人的代码真是件痛苦的事情,功能实现尚且不说,就说代码规范我就能再写出一篇文章来。真的是无规范不成方圆,规范这个东西太重要了!有点扯了,回到主题,咳咳,那就谈谈我对上面的面试题的理解吧,文笔有限,不妥之处,欢迎在文章结尾留言斧正啊,正啊,啊!

概述

几种通信方式无外乎以下几种:

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

详述

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

1. Prop

英式发音:[prɒp]。这个在我们日常开发当中用到的非常多。简单来说, 我们可以通过Prop 向子组件传递数据 。用一个形象的比喻来说,父子组件之间的数据传递相当于自上而下的下水管子,只能从上往下流,不能逆流。这也正是 Vue 的设计理念之单向数据流。而 Prop 正是管道与管道之间的一个衔接口,这样水(数据)才能往下流。说这么多,看代码:

<div id="app">
 <child :content="message"></child>
</div>
// Js
let Child = Vue.extend({
 template: '<h2>{{ content }}</h2>',
 props: {
  content: {
   type: String,
   default: () => { return 'from child' }
  }
 }
})
new Vue({
 el: '#app',
 data: {
  message: 'from parent'
 },
 components: {
  Child
 }
})

你可以狠狠的戳这里查看Demo!浏览器输出:

from parent

2. $emit

英式发音:[iˈmɪt]。官方说法是 触发当前实例上的事件。附加参数都会传给监听器回调 。按照我的理解不知道能不能给大家说明白,先简单看下代码吧:

<div id="app">
 <my-button @greet="sayHi"></my-button>
</div>

let MyButton = Vue.extend({
 template: '<button @click="triggerClick">click</button>',
 data () {
  return {
   greeting: 'vue.js!'
  }
 },
 methods: {
  triggerClick () {
   this.$emit('greet', this.greeting)
  }
 }
})

new Vue({
 el: '#app',
 components: {
  MyButton
 },
 methods: {
  sayHi (val) {
   alert('Hi, ' + val) // 'Hi, vue.js!'
  }
 }
})

你可以狠狠的戳这里查看Demo! 大致逻辑是酱婶儿的:当我在页面上点击按钮时,触发了组件 MyButton 上的监听事件 greet ,并且把参数传给了回调函数 sayHi 。说白了,当我们从子组件Emit(派发) 一个事件之前,其内部都提前在事件队列中On(监听)了这个事件及其监听回调。其实相当于下面这种写法:

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

3. .sync 修饰符

这个家伙在 vue@1.x 的时候曾作为双向绑定功能存在,即子组件可以修改父组件中的值。因为它违反了单向数据流的设计理念,所以在 vue@2.0 的时候被干掉了。但是在 vue@2.3.0+ 以上版本又重新引入了这个.sync 修饰符。但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。说白了就是让我们手动进行更新父组件中的值了,从而使数据改动来源更加的明显。下面引入自官方的一段话:

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。

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

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

于是我们可以用 .sync 语法糖简写成如下形式:

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

废话这么多,如何做到“双向绑定” 呢?让我们进段广告,广告之后更加精彩! ... 好的,欢迎回来。假如我们想实现这样一个效果:改变子组件文本框中的值同时改变父组件中的值。怎么做?列位不妨先想想。先看段代码:

<div id="app">
 <login :name.sync="userName"></login> {{ userName }}
</div>

let Login = Vue.extend({
 template: `
  <div class="input-group">
   <label>姓名:</label>
   <input v-model="text">
  </div>
 `,
 props: ['name'],
 data () {
  return {
   text: ''
  }
 },
 watch: {
  text (newVal) {
   this.$emit('update:name', newVal)
  }
 }
})

new Vue({
 el: '#app',
 data: {
  userName: ''
 },
 components: {
  Login
 }
})

你可以狠狠的戳这里查看Demo!下面划重点,代码里有这一句话:

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 属性像两个收纳箱,一个负责收纳属性,一个负责收纳事件,都是以对象的形式来保存数据。看下面的代码解释:

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

从 Html 中可以看到,这里有俩属性和俩方法,区别是属性一个是 prop 声明,事件一个是 .native 修饰器。

let Child = Vue.extend({
 template: '<h2>{{ foo }}</h2>',
 props: ['foo'],
 created () {
  console.log(this.$attrs, this.$listeners)
  // -> {bar: "parent bar"}
  // -> {two: fn}

  // 这里我们访问父组件中的 `triggerTwo` 方法
  this.$listeners.two()
  // -> 'two'
 }
})

new Vue({
 el: '#app',
 data: {
  foo: 'parent foo',
  bar: 'parent bar'
 },
 components: {
  Child
 },
 methods: {
  triggerOne () {
   alert('one')
  },
  triggerTwo () {
   alert('two')
  }
 }
})

你可以狠狠的戳这里查看Demo! 可以看到,我们可以通过 $attrs 和 $listeners 进行数据传递,在需要的地方进行调用和处理,还是很方便的。当然,我们还可以通过 v-on="$listeners" 一级级的往下传递,子子孙孙无穷尽也!

一个插曲!

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

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

这样会很难看,同时也爆了某些东西。如何去掉?这正是inheritAttrs 属性的用武之地!给组件加上这个属性就行了,一般是配合 $attrs 使用。看代码:

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

再次编译:

<h2>parent foo</h2>

5. provide / inject

他俩是对CP, 感觉挺神秘的。来看下官方对provide / inject 的描述:

provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。并且这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

看完描述有点懵懵懂懂!一句话总结就是:小时候你老爸什么东西都先帮你存着等你长大该娶媳妇儿了你要房子给你买要车给你买只要他有的尽量都会满足你。下面是这句话的代码解释:

<div id="app">
 <son></son>
</div>

let Son = Vue.extend({
 template: '<h2>son</h2>',
 inject: {
  house: {
   default: '没房'
  },
  car: {
   default: '没车'
  },
  money: {
   // 长大工作了虽然有点钱
   // 仅供生活费,需要向父母要
   default: '¥4500'
  }
 },
 created () {
  console.log(this.house, this.car, this.money)
  // -> '房子', '车子', '¥10000'
 }
})

new Vue({
 el: '#app',
 provide: {
  house: '房子',
  car: '车子',
  money: '¥10000'
 },
 components: {
  Son
 }
})

你可以狠狠的戳这里查看Demo!

6. 其他方式通信

除了以上五种方式外,其实还有:

EventBus

思路就是声明一个全局Vue实例变量 EventBus , 把所有的通信数据,事件监听都存储到这个变量上。这样就达到在组件间数据共享了,有点类似于Vuex。但这种方式只适用于极小的项目,复杂项目还是推荐 Vuex。下面是实现 EventBus 的简单代码:

<div id="app">
 <child></child>
</div>

// 全局变量
let EventBus = new Vue()

// 子组件
let Child = Vue.extend({
 template: '<h2>child</h2>',
 created () {
  console.log(EventBus.message)
  // -> 'hello'
  EventBus.$emit('received', 'from child')
 }
})

new Vue({
 el: '#app',
 components: {
  Child
 },
 created () {
  // 变量保存
  EventBus.message = 'hello'
  // 事件监听
  EventBus.$on('received', function (val) {
   console.log('received: '+ val)
   // -> 'received: from child'
  })
 }
})

你可以狠狠的戳这里查看Demo!

Vuex

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

$parent

父实例,如果当前实例有的话。通过访问父实例也能进行数据之间的交互,但极小情况下会直接修改父组件中的数据。

$root

当前组件树的根 Vue 实例。如果当前实例没有父实例,此实例将会是其自己。通过访问根组件也能进行数据之间的交互,但极小情况下会直接修改父组件中的数据。

broadcast / dispatch

他俩是 vue@1.0 中的方法,分别是事件广播 和 事件派发。虽然 vue@2.0 里面删掉了,但可以模拟这两个方法。可以借鉴 Element 实现。有时候还是非常有用的,比如我们在开发树形组件的时候等等。

总结

以上所述是小编给大家介绍的Vue.js 父子组件通信的十种方式,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • vue.js父子组件通信动态绑定的实例

    如下所示: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <div id='app'> <!--这里的作用是将父组件渲染到页面上--> <father></father> </d

  • Vue.js 父子组件通讯开发实例

    vue.js,是一个构建数据驱动的 web 界面的库.Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件.(这是官方的一个解释!) 小编没使用过angularjs,也没使用过react.js,不能详细的说明三者的区别,想了解的话,在官方有一个分析,请点这里查看 小编从业前端开发也有了一年多的时间,刚开始的时候,前端开发展现的技术太多,小编有心无力,兼顾不过来,经过了解之后,还是选择了学原生js基础兼并jquery的学习上路.小编使用vue.js,也是因为业务的需求

  • Vue.js每天必学之组件与组件间的通信

    什么是组件? 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能.在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展. 使用组件 注册 之前说过,我们可以用 Vue.extend() 创建一个组件构造器: var MyComponent = Vue.extend({ // 选项... }) 要把这个构造器用作组件,需要用 `Vue.compone

  • 深入探讨Vue.js组件和组件通信

    基本是按照官网的 Guide 全部梳理了一遍:http://vuejs.org/guide/index.html 这里我们以一个 Todo List 应用为例来把相关的只是都串起来,这篇里面的全部代码都在github上 https://github.com/lihongxun945/vue-todolist  Vue 实例 一个 Vue 应用是由一个 root vue instance 引导启动的,而 Vue instance 是这么创建的: var vm = new Vue({ // opti

  • vue.js $refs和$emit 父子组件交互的方法

    本文介绍了vue.js $refs和$emit 父子组件交互的方法,分享给大家,废话不多说直接看代码: <strong>父调子 $refs (把父组件的数据传给子组件) </strong><br><br><template> <div id="app"> <input type="button" name="" id="" @click="

  • vue.js入门(3)——详解组件通信

    本文介绍vue.js组件,具体如下: 5.2 组件通信 尽管子组件可以用this.$parent访问它的父组件及其父链上任意的实例,不过子组件应当避免直接依赖父组件的数据,尽量显式地使用 props 传递数据.另外,在子组件中修改父组件的状态是非常糟糕的做法,因为: 1.这让父组件与子组件紧密地耦合: 2.只看父组件,很难理解父组件的状态.因为它可能被任意子组件修改!理想情况下,只有组件自己能修改它的状态. 每个Vue实例都是一个事件触发器: $on()--监听事件. $emit()--把事件沿

  • vue.js单文件组件中非父子组件的传值实例

    最近在研究vue.js,总体来说还算可以,但是在web开发群里有一些人问在单文件组件开发模式中非父子组件如何传值的问题,今天在这里讲讲,希望对大家有所帮助! 在官网api中的这段讲解很少,也很模糊:官网中说明如下: 非父子组件通信: 有时候两个组件也需要通信 (非父子关系).在简单的场景下,可以使用一个空的 Vue 实例作为中央事件总线: var bus = new Vue(): // 触发组件 A 中的事件 bus.$emit('id-selected', 1) // 在组件 B 创建的钩子中

  • Vue.js 父子组件通信的十种方式

    面试官:Vue 中父子组件通信有哪些方式? 自己先想一分钟. 无可否认,现在无论大厂还是小厂都已经用上了Vue.js 框架,简单易上手不说,教程详尽,社区活跃,第三方套件还多.真的是前端开发人员必备技能.而且在面试当中也往往会问到关于 Vue 方面的各种问题,其中大部分面试官会问到如上这种问题. 最近一直在做 Vue项目代码层面上的优化,说实话,优化别人的代码真是件痛苦的事情,功能实现尚且不说,就说代码规范我就能再写出一篇文章来.真的是无规范不成方圆,规范这个东西太重要了!有点扯了,回到主题,咳

  • vue非父子组件通信问题及解决方法

    问题描述: 最近在做登录部分时遇到一个场景,当点击 "用户"按钮时,首先渲染login组件,在用户登录后直接跳转到用户信息界面(user组件).这里遇到了需要将login组件通过异步请求获得的用户信息传向 user组件,但是login和login组件不是父子组件,就暂称为兄弟组件吧. 简而言之,我要解决的就是兄弟组件之间的信息传递问题. //位于login.vue 中 export default { methods:{ login(){ this.$axios({ method: '

  • 如何用Vue实现父子组件通信

    一.父子组件的关系构成 本篇文章将要总结的是Vue中父子组件之间的通信方式. 那在vue中父子组件之间的关系是怎么构成的呢,或者说哪个组件可以称为是父组件,哪个组件又可以称为是子组件呢. 在我的理解中,父子组件的关系构成也比较简单. 使用vue-cli工具构建的项目中,我们经常会在一个组件中注册引用另外一个组件. Home.vue <template> <div class="home"> <p>这里是Home组件</p> </di

  • Vue中父子组件通信与事件触发的深入讲解

    目录 一.组件 子组件 父组件 二.父子组件通信 父组件给子组件通信 子组件向父组件通信 三.父子组件事件触发 父组件调用子组件中的事件方法 子组件调用父组件中的事件方法 四.总结 一.组件 子组件 <template> <div style="border:1px solid black;width:400px; height: 130px;"> <h3>我是子组件</h3> <button>子组件将值传递给父组件</

  • Vue非父子组件通信详解

    组件是Vue核心的一块内容,组件之间的通信也是很基本的开发需求.组件通信又包括父组件向子组件传数据,子组件向父组件传数据,非父子组件间的通信.前两种通信Vue的文档都说的很清楚,但是第三种文档上确只有下面的几句 具体如何去实现却没有很详细的说明,于是自己试着进行了实现.先看下简单的通信效果: 就是点击了一个组件,另一个组件的数字递加. html如下: <div id="app"> <component-a></component-a> <com

  • 详解Vue 非父子组件通信方法(非Vuex)

    一提到两个非父子组件通信方法,有经验的 coder 肯定会说用 Vuex 啊,我个人建议不要为了用 Vuex 而用 Vuex,除非你的项目很大,耦合度很高,需要大量的储存一些 data,组件之间通信频繁.当然还是要根据自己的业务场景的来决定,总之还是那句话,不要为了用 Vuex 而用 Vuex! Vue 官网介绍了非父子组件通信方法: 不过官网说的太简单了,新手看完估计还是一脸懵逼.还有这个空的 Vue 实例放到哪里合适也值得商榷. 这篇文章的目的就是用一个简单的例子让你明白如何用 Bus

  • Vue非父子组件之间的通信方式详解

    目录 非父子组件的通信 1.Provide和Inject 1.1基本使用 1.2处理响应式数据(了解) 2.全局事件总线 总结 非父子组件的通信 此篇讲解的是, 在学习状态管理之前, 非父子间通信的方案 在开发中,我们构建了组件树之后,除了父子组件之间的通信之外,还会有非父子组件之间的通信. 这里我们主要讲两种方式: Provide/Inject: 全局事件总线: 1.Provide和Inject 1.1基本使用 Provide/Inject用于非父子组件之间共享数据: 比如有一些深度嵌套的组件

  • vue element-ui table组件动态生成表头和数据并修改单元格格式 父子组件通信

    父组件 定义表头和表内容 data(){ return{ // 表格数据 tableColumns: [], // 表头数据 titleData:[], } } 引入并注册子组件 import TableComponents from "../../components/table/table"; //注册子组件table components: { tableC: TableComponents }, 获取表头和表内容数据.(真实数据应该是从接口获取的,由于是测试数据这里我先写死)

随机推荐