摘要:结果又把这个代码写了一遍,如果后面再有呢?原样又写一遍。重复多了,我们就会发现,这片代码外观不变,只是数据不同,那么我们就把这片代码抽象成组件,只要提供给它不同的数据即可,如下所示:
这几天事情比较多,所以没有更新了。
这篇研究组件的问题,我们写慢点,争取将一个问题说清楚。
写下作者的理解,比如你要显示某个用户的信息,代码如下:
姓名:bob年龄:20地址:广州天河区没有问题,过了一会,又要显示一个人的信息:
姓名:bob年龄:20地址:广州天河区.........姓名:peter年龄:23地址:广州黄浦区结果又把这个代码写了一遍,如果后面再有呢?原样又写一遍。重复多了,我们就会发现,这片代码外观不变,只是数据不同,那么我们就把这片代码抽象成组件,只要提供给它不同的数据即可,如下所示:
我们抽象出来一个组件名为user,在vue2.x的版本中,要求必须有一个根标签,所以作者这里添加了一个红色div表示根标签。那么在使用的时候就变成下面这样:
............视觉上清爽多了。
除了这些,它还有很多优点,随着学习会一步步加深了解。
使用vue.extend创建,代码如下:
姓名:{{name}}年龄:{{age}}地址:{{address}}//创建一个组件的构造函数var user = Vue.extend({template: '#user',data {return {}},props: {name: String,age: Number,address: String}})//全局注册Vue.component('user', user)我们看这个组件,就像一个独立的模块,有自己专有的模板,数据,方法等,和Vue这个构造几乎一样,如果我们能将它们等价类比也是可以的。
组件创建好之后,我们就可以拿来使用它,如下所示:
组件就像是一个独立模块,五脏俱全,在当前例子中,这个组件唯一欠缺的就是不知道数据是多少。举个通俗的例子,我们买了一台做馒头的机器,它可以自己执行各个流程不需要外界干扰,但唯一缺的就是加工原料,假如把原料输给它之后,后面我们就什么都不管了。那么如何把原料送给它呢?这就是组件通信的问题。
我想把一个人的信息发给user组件让它显示出来,怎么做呢?这个问题我们需要好好的研究一下,但是我们不看组件,而是先看C的函数调用,比如C语言定义一个函数:
void foo(int a,char b,bool c){ //操作 printf("%d,%c,%d",a,b,c);}我们知道,函数声明就像是一个契约,它表示这个函数能接收多少个参数,而且每个参数是什么类型,图示如下:
函数foo如果能说话的话,它就会说:我有三个参数(也就是三个入口),请传给我三个数据,类型分别是int,char,bool。我们看到这个声明,马上就明白怎么发数据给它。
回到组件,它和这个原理几乎相同。站在组件这一方,它肯定会想,我怎么能让别人知道传什么数据给我呢?这是首要解决的问题。其次,站在调用方的角度,比如说我们,怎么知道传哪些数据给它呢?那么组件就想,干脆我也学C语言好了,做个声明。
组件的“函数声明”就是props。
//创建一个组件的构造函数var user = Vue.extend({template: '#user',data {return {}},props: {name: String,age: Number,address: String}})在组件的props字段中,定义了三个参数,然后,这个组件可能会在文档上给我们暴露,让我们知道要传这三个数据给它。
那么数据怎么传呢?有很多种方式,但是在当前这个例子中只有一个地方可以传,就是在组件的属性上,如下:
这种方式是最符合正常思维的,因此我们作为第一种方式特别拿出来举例。
这是其一。
其二,我们传的基本都是数据,不会出现style,class等相关的内容,因为这个标签是被替换掉的,它不是真正的HTML标签。因此组件就认为我们传过来的都是数据。
首先,组件在内部已经声明好了三个“变量”,然后我们把数据送进来。但是它和C语言的函数有下面不同:
1、数据传送没有顺序的区分,比如我们写成这样没有问题。
2、数据的个数不受限制(这个后面讨论)。
所以我们是类比C语言的函数,而不是说它俩是一样的。数据进来之后组件会查看一下,比如第一个我们传送的是name="bob",那么组件就看看自己肚子里面有没有名叫name的变量(或者说prop),如果正好有,然后再看看类型是否匹配?就像C语言的int,char一样,这个我们也在后面再展开说。假如一切都通过检查,那么内部的name就得到了值bob。其余以此类推。
所以prop是以名字做为唯一标识的,最终结果如下:
和C语言函数不同的是,组件prop的数据类型检查似乎没有那么严格,因为我们都知道JS的数据是乱套的,串转值、值转串之类的自动转换经常发生,所以我们就不能苛求它像C那样做严格的检查,比如:
明明组件定义的age类型为Number,但是我们传给它的却是一个字符串,结果它只能警告一下。所以prop的数据类型定义只是要求我们尽量遵守,我们需要知道这个事情。
再比如,还有一种情况:
//创建一个组件的构造函数var user = Vue.extend({template: '#user',data {return {}},props: ['name','age','address']})这个props的定义只有一个名字,更省略了,言外之意就是不管啥数据只要名字一样就可以了,也就是说连数据类型也不检查了。
当然,除了这个,还有其它各种用法,大家可以自行看文档,我们在这里主要是讨论原理,只要理解原理,其它的用法就能懂,所以不再多写。
比如我们除了传prop,还多传了一个phone:
组件并不会因为它不是prop而不接收,而是照单接收。但是做为attrs来存储了,如下所示:
因此,我们需要理解什么是attrs。
attrs就是标签的属性,也就是说凡是标签上面挂着的都算是属性,比如:
在这个标签上挂着的所有xx=yy都算是属性,还包括有名无值的xx。
因此,组件在接收数据的时候,会把传过来的数据全部看作是attrs(我的理解),只是这些属性中恰好有几个和它内部定义的props的名字是一样的,于是组件拿到值之后顺带过滤掉了,而没有过滤的自然就存储到组件的attrs中,这样逻辑上能通顺。
下面我们先看最终的结果:
可以看到它把多余的属性设置到组件的根标签上了。如果不想让它挂上去可以吗?也是可以的,组件提供一个下面的属性:
//创建一个组件的构造函数var user = Vue.extend({template: '#user',data {return {}},inheritAttrs:false,props: {name: String,age: Number,address: String}})将inheritAttrs设置为false,那么就不再设置HTML标签的attributes。
根据上面的分析,我们知道如果数据没有被prop处理,就会转到组件的$attrs中,假如我们把这个$attrs绑定着继续往下传,就可以将祖先的数据一直往下传,就像穿透了组件一样。
分析:数据传下来时,先被props过滤,剩下的存储到this.$attrs,接着我们把$attrs继续往下传,下方组件依然先props过滤,接着再存储....以此类推。这样就实现了一个简单的组件数据通信。
{{name}}{{age}}{{address}}点我//1.定义手机组件var phone = Vue.extend({template: '{{phone}}
',props: {phone: String}})Vue.component('phone', phone)//2.定义user组件var user = Vue.extend({template: '#user',data {return {}},inheritAttrs: false,props: {name: String,age: Number,address: String},methods: {handleClick {console.log(this.$attrs)}}})Vue.component('user', user)//3.定义根实例var vm = new Vue({el: '#app',data {return {}}})分析:在组件中使用
组件,并且使用v-bind="$attrs",直接将整个$attrs绑进去了。在vue中如果v-bind绑定的是对象,就表示把该对象内部的字段挨个绑定,这样我们就将$attrs整个的传给phone组件。
不知不觉又写了一大篇,其实1/10都没写到。太长读者会很累,这篇我们就截止到这里。有兴趣的同学可以跟着动手做一遍。
在本篇中,重点说明了props是怎么来的,以及如何传数据的,顺带实现了一个简单的通信。
来源:恋爱脑一点号