【前端小小白的学习之路】vue学习记录⑤(组件通信-父与子)

今天我们看一下组件通信。

经过前面几篇文章,我们已经可以构建出完整的单个组件,并利用路由使其串联起来访问了。

但这明显还是不够的。一个页面不可能就是个单组件,一般是由多个组件合成的。正因为如此,组件之间肯定是有相互关系的,我们就称这种现象叫组件通信

比如父组件发生了某项改变,子组件会跟着相应发生变化;反过来,子组件有了某种改变,父组件有时也会随之做出调整。那么这种现象我们称之为双向数据流动

然而,vue的作者敏锐的认识到,双向数据流带来便捷的同时,也存在着极大的安全隐患。

父组件将变化传递给子组件(父影响子),这是没问题的,在我们日常生活中也是极为普遍的现象(老子教训儿子是天经地义的),然而反过来子影响父的话,这就变得不可理喻了(儿子教训老子?)

子组件修改父组件,增大了组件之间的耦合度。有时开发者根本没有意识到这种修改,这犹如埋下一颗定时炸弹,随着日后项目代码的膨胀,一旦引爆,问题排查难度也会呈指数级的徒增。

为了斩断这种安全隐患,vue提倡的是单向数据流动——也就是只能父影响子,而反过来则不成立。

父传子,我们利用props作为桥梁。下面看一个例子。

首先我将目录调整一下:

其中,helloworld.vue组件,我们设定其为父组件;而children目录下的child.vue,我们设定为其子组件。

Helloworld.vue代码如下:

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父亲</h1>
<!--绑定父组件的某个变量myMsg-->
  <div>
<!--注意子组件接受变量时,需要变为驼峰命令法sendMessage-->
   <child v-bind:send-message="myMsg"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子组件,需要设定子组件的name为child
import child from ‘./children/child.vue‘
export default {
 name:‘helloworld‘,
 data(){
  return {
   myMsg:‘hello,my son!‘
  };
 },
 components:{child}//局部注册子组件
}
</script>
<!--样式部分-->
<style>
.container{
 background: #ccc;
 color:greenyellow;
}

</style>

大家注意下别把组件和路由的概念搞混了,二者之间一毛钱关系都没有。要使用组件,需做局部或者全局注册。我这里只做局部注册,大家仔细看一下写法。

我们在父组件定义了一个myMsg这个变量,然后通过v-bind绑定到了child组件中。这时,child组件就可以通过props来接收这个变量了。

child.vue代码如下:

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是儿子</h3>

  <div>我从父亲那边接收过来的信息:</div>
  <div>{{sendMessage}}</div>
 </div>
</template>
<!--js部分-->
<script>
//子组件
export default {
 name:‘child‘,//必须设定name,否则无法在父组件import
 props:[‘sendMessage‘],
 data(){
  return {

  };
 }
}
</script>
<!--样式部分-->
<style>
.sub-container{
 background: blue;
 color:red;
}
</style>

一个简单的父子通信案例就完成了,看一下路由router/index.js,代码如下:

import Vue from ‘vue‘
import Router from ‘vue-router‘
import Hello from ‘@/components/Hello‘
import HelloWorld from ‘@/components/Helloworld‘ //我们新定义的组件

Vue.use(Router)

export default new Router({
  routes: [{
      path: ‘/‘,
      name: ‘Hello‘,
      component: Hello
    },
    { //新路由
      path: ‘/helloworld/:id‘,
      name: ‘HelloWorld‘,
      component: HelloWorld,
    }
  ]
})

运行一下npm run dev,看一下结果:

可以看到,子组件顺利接收到了父组件传来的信息。

那么,到底子组件能否影响父组件呢?我们将以上代码做一下调整。

调整后的Helloworld.vue

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父亲</h1>

  <input type="text" v-model="myMsg">
  <br>
<!--绑定父组件的某个变量myMsg-->
  <div>
<!--注意子组件接受变量时,需要变为驼峰命令法sendMessage-->
   <child v-bind:send-message="myMsg"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子组件,需要设定子组件的name为child
import child from ‘./children/child.vue‘
export default {
 name:‘helloworld‘,
 data(){
  return {
   myMsg:‘hello,my son!‘
  };
 },
 components:{child}//局部注册子组件
}
</script>
<!--样式部分-->
<style>
.container{
background: #ccc;
color:greenyellow;
}

</style>

调整后的child.vue

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是儿子</h3>

  <div>我从父亲那边接收过来的信息:</div>
  <div>{{sendMessage}}</div>
  <div>是否可以子影响父呢?</div>
  <input type="text" v-model="sendMessage">
 </div>
</template>
<!--js部分-->
<script>
//子组件
export default {
 name:‘child‘,//必须设定name,否则无法在父组件import
 props:[‘sendMessage‘],
 data(){
  return {

  };
 }
}
</script>
<!--样式部分-->
<style>
.sub-container{
 background: blue;
 color:red;
}
</style>

运行一下npm run dev,看一下结果:

可以看出,子组件是无法影响父组件的,这样父组件就成功解耦了。

大家要注意一个问题——传递的数据类型问题。

传递的是值类型肯定没问题,假如是对象或者数组这样的引用类型,变量共用同一内存,所以父子会出现双向影响,这个问题一定要注意。

为了杜绝隐患,提高安全性,我们在子组件将prop接收的类型做一下强制检测,假如传入的数据类型并不是我需要的,可以抛出异常。

Helloworld.vue

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父亲</h1>

  <input type="text" v-model="myMsg">
<!--<input type="text" v-model="myNum">-->
  <ul v-for="item of myObj">
   <li>{{item.name}}</li>
  </ul>
 <br>
<!--绑定父组件的某个变量myMsg-->
  <div>
<!--注意子组件接受变量时,需要变为驼峰命令法sendMessage-->
   <child :send-message="myMsg" :send-num="myNum" :send-obj="myObj"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子组件,需要设定子组件的name为child
import child from ‘./children/child.vue‘
export default {
 name:‘helloworld‘,
 data(){
  return {
   myMsg:‘hello,my son!‘,
   myNum:‘123‘,
   myObj:[{id:1,name:‘Tom_Lo‘},{id:2,name:‘tom‘}]
  };
 },
 components:{child}//局部注册子组件
}
</script>
<!--样式部分-->
<style>
.container{
 background: #ccc;
 color:green;
}
</style>

父组件这次传了三个不同类型的信息,分别是字符串、数字和json对象。其中,数字类型我故意写错了,看一下子组件是否会检测出来。

child.vue

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是儿子</h3>

  <div>我从父亲那边接收过来的信息:</div>
  <div>字符串:{{sendMessage}}</div>
  <div>数字:{{sendNum}}</div>
  <div>
   <ul v-for="item of list">
    <li>{{item.name}}</li>
   </ul>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//子组件
export default {
 name:‘child‘,//必须设定name,否则无法在父组件import
 props:{
 sendMessage:{
  type:String,//传入类型必须是字符串
  required:true//必传
 },
 sendNum:{
  type:Number,//传入类型必须是数字
  required:true
 },
//如果是数组或对象
 sendObj:{
  validator:function(val){

   if(Object.prototype.toString.call(val) === ‘[object Array]‘ || Object.prototype.toString.call(val) === ‘[object Object]‘){
    return true;
    }
   }
  }
 },//声明验证类型
 data(){
  var parentObj = this.sendObj;//获取父组件传来的json
  var childObj =JSON.parse(JSON.stringify(parentObj));//复制一份,避免污染父组件的数据
  childObj.push({id:3,name:‘mike‘});//追加一个对象
   return {
    list:childObj
   };
 }
}
</script>
<!--样式部分-->
<style>
.sub-container{
background: blue;
color:red;
}
</style>

大家需要注意下子组件props的验证规则。

那么对于容易引发错误的引用类型,大家应该如何避免呢?

当我们接收过来这个对象数据后,先将其拷贝一份副本,然后用副本做增加修改等操作,这样便不会影响到父组件的数据了。

我们运行一下看看:

看起来验证规则生效了哈~~

那么以上都是同步的情况,那么异步呢?

假设父组件的数据是异步ajax获取的,然后渲染页面。那么此时,子组件如何跟着更新视图呢?

这就得有个监听机制。当子组件监听到父组件的数据到位后,将本地数据更新即可。

vue提供的watch可以达到此目的,我们看一下例子。

Helloworld.vue

<!--模板部分-->
<template>
 <div class="container">
  <h1>我是父亲</h1>
  <input type="text" v-model="myMsg">
<!--<input type="text" v-model="myNum">-->
  <ul v-for="item of myObj">
   <li>{{item.name}}</li>
  </ul>
 <br>
<!--绑定父组件的某个变量myMsg-->
  <div>
<!--注意子组件接受变量时,需要变为驼峰命令法sendMessage-->
   <child :send-message="myMsg" :send-num="myNum" :send-obj="myObj"></child>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//引入子组件,需要设定子组件的name为child
import child from ‘./children/child.vue‘
export default {
 name:‘helloworld‘,
 data(){
//初始化obj
  var obj = {
   myMsg:‘hello,my son!‘,
   myNum:123,
   myObj:[]
  };
//延时返回myObj,模拟ajax的延时情况
  setTimeout(function(){
   obj[‘myObj‘] = [{id:1,name:‘jack‘},{id:2,name:‘tom‘}];

  },2000);
  return obj;
 },
 components:{child}//局部注册子组件
}
</script>
<!--样式部分-->
<style>
.container{
 background: #ccc;
 color:green;
}
</style>

child.vue

<!--模板部分-->
<template>
 <div class="sub-container">
  <h3>我是儿子</h3>

  <div>我从父亲那边接收过来的信息:</div>
  <div>字符串:{{sendMessage}}</div>
  <div>数字:{{sendNum}}</div>
  <div>
   <ul v-for="item of list">
     <li>{{item.name}}</li>
   </ul>
  </div>

 </div>
</template>
<!--js部分-->
<script>
//子组件
export default {
 name:‘child‘,//必须设定name,否则无法在父组件import
 props:{
  sendMessage:{
   type:String,//传入类型必须是字符串
   required:true//必传
  },
  sendNum:{
   type:Number,//传入类型必须是数字
   required:true
  },
//如果是数组或对象
  sendObj:{
   validator:function(val){

    if(Object.prototype.toString.call(val) === ‘[object Array]‘ || Object.prototype.toString.call(val) === ‘[object Object]‘){
     return true;
    }
   }
  }
 },//声明验证类型
 data(){

   return {
    list:null
   };
 },
 watch:{//监控父组件sendObj的变化
  sendObj(newval,oldval){

   if(newval !== oldval){

    var newobj =JSON.parse(JSON.stringify(newval));//复制一份,避免污染父组件的数据
    newobj.push({id:3,name:‘mike‘});//追加一个对象
    this.list = newobj;//将新值赋予data的list
   }

  }
 }
}
</script>
<!--样式部分-->
<style>
.sub-container{
 background: blue;
 color:red;
}
</style>

watch的函数名称,就是要监听的值,这里我们监听父组件传来的prop;而newval和oldval参数,分别代表了新值和旧值。

在父组件未更新之前,子组件监听到的sendObj,新值和旧值都是空数组,二者是相等的;而当监听到二者发生不等时,就说明父组件传来的信息发生了改变。我们取新值,并将拷贝后的数据传给data里面的list,然后更新到视图。

看一下运行情况:

父组件与子组件的交互就实现了。

参考文章:http://mp.weixin.qq.com/s/rrKGRPTIt-aiZXijIli3ww

参考文章:http://www.cnblogs.com/ghostwu/p/7518002.html

时间: 2024-08-18 22:02:37

【前端小小白的学习之路】vue学习记录⑤(组件通信-父与子)的相关文章

Vue中的组件通信

这两天在学Vue,记录一下我认为比较重要的东西 Vue中的组件通信: 我们可以分为3个步骤来: 1.声明局部子组件,简称 "声子", 2.挂载到dom树上面去,简称:"挂子" 3.进行使用,简称:"用子" 上面写的别人可能看不太明白,毕竟只是我的看法: <body> <div id="app"> </div> </body> 先写一个div,给它一个ID=app <scri

vue(三)-父子组件通信

原因 :  Vue 的组件作用域都是孤立的,不允许在子组件的模板内直接引用父组件的数据.必须使用特定的方法才能实现组件之间的数据传递. props  父组件给子组件传递数据 props:作用是父组件给子组件传递数据. 语法:参考<vue(二)-父子组件语法>. 注意要点: 1:  子组件要显式声明需要哪些数据.语法: props{ item:{ default(){ return } } } 2:从父组件用props传进来的数据,一般不要改动,因为改动之后,父组件中数据也变了,但是(敲黑板),

vue的组件通讯 父传子 -- 子传父-- 兄弟组件的传值 vue的组件传值

首先文字简单撸一下 父子传子   -------首先在父组件上绑定一个属性,在子组件里用props接收,可以是数组或者是对象 子传父   ------在父组件升上自定义一个方法,在子组件里通过this.$emit("父组件上的方法名",a)     /-------a------/代表需要传递的参数        兄弟组件通讯   需要创建一个公共的vue 实例, new vue()    在main.js里 书写Vue.prototype .com=new vue()    通过pr

前端小白的学习之路--HTML学习

HTML的补充学习 1. meta与base <meta http-equiv="refresh" content="2" > 2秒刷新一次 <base href="https://www.baidu.com" target="_blank"> 设置默认跳转地址以及跳转方式 <link rel="icon" sizes="any" mask href=&qu

Java学习之路-Hessian学习

Hessian是基于HTTP的轻量级远程服务解决方案,Hessian像Rmi一样,使用二进制消息进行客户端和服务器端交互.但与其他二进制远程调用技术(例如Rmi)不同的是,它的二进制消息可以移植其他非Java的语言中.  一.创建Hessian程序的4个步骤  1.定义一个远程接口的接口.  2.定义一个实现该接口的类.  3.在web.xml中定义导出Hessian服务需要的信息.  4.编写客户端访问代码.  二.程序的具体实现  一.首先我们先创建Web项目,并新建一个实体类,这个类需要实

VSTO学习之路:学习使用Epplus(1)

关于读取其它工作簿数据的几个方式的比较: 1.VBA的GetObject方法,会调用Excel程序打开工作簿(虽然不可见,但确实是打开的) 2.SQL,使用繁琐缺少灵活,不支持单元格样式的操作,也似乎不支持delete语句删除源数据. 3.使用Open XML SDK,基于Open XML,不依赖于Excel程序,但步骤繁琐. 4.Epplus,基于OpenXML,简单灵活,不依赖Excel程序打开工作簿,处理数据的速度快. 下载:Epplus,引用Epplus,然后  using Office

VSTO学习之路:学习使用Epplus——读写VBA代码

创建xlsm工作簿: 宏工作簿,必须有VBProject对象,至少要有一个工作表 1 string path = @"E:\studyvs\open xml\test.xlsm"; 2 var package = new ExcelPackage(); 3 package.Workbook.Worksheets.Add("Sheet1"); 4 //创建工程对象 5 package.Workbook.CreateVBAProject(); 6 //保存工作簿 7 p

Android开发学习之路-Service和Activity的通信

在很多时候,Service都不仅仅需要在后台运行,还需要和Activity进行通信,或者接受Activity的指挥,如何来实现,来看代码. 定义一个服务 1 // 创建一个服务,然后在onBind()中返回内部类(继承自Binder)的实例,使得活动能获得该实例,并操作此服务 2 public class MyService extends Service { 3 4 // 创建一个内部类的实例,被活动获得后,操作服务 5 private mBinder binder = new mBinder

Java学习之路-RMI学习

Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口.它使客户机上运行的程序可以调用远程服务器上的对象.远程方法调用特性使Java编程人员能够在网络环境中分布操作.RMI全部的宗旨就是尽可能简化远程接口对象的使用. 一.创建RMI程序的6个步骤 1.定义一个远程接口的接口,该接口中的每一个方法必须声明它将产生一个RemoteException异常. 2.定义一个实现该接口的类. 3.