Vue 的组件作用域都是孤立的,不允许在子组件的模板内直接引用父组件的数据。必须使用特定的方法才能实现组件之间的数据传递。
首先用脚手架工具 vue-cli 创建一个项目,其中 App.vue 是父组件,components 文件夹下都是子组件。
在 Vue 中,父组件可以使用 props 向子组件传递数据。
子组件部分:
这是 header.vue 的 HTML 部分,logo 是在 data 中定义的变量。
如果需要从父组件获取 logo 的值,就需要使用props: ['logo']。
在 props 添加了元素之后,就不需要在 data 中再添加变量了。
父组件部分:
在调用组件的时候,使用 v-bind 将 logo 的值绑定为 App.vue 中定义的变量 logoMsg。
然后就能将App.vue中 logoMsg 的值传给 header.vue 了:
子组件主要通过事件传递数据给父组件。
子组件部分:
这是 login.vue 的 HTML 部分,当<input>的值发生变化的时候,将 username 传递给 App.vue。
首先声明一个方法setUser,用change事件来调用 setUser。
在 setUser 中,使用了 $emit 来遍历 transferUser 事件,并传参this.username。
其中transferUser是一个自定义的事件,功能类似于一个中转,this.username将通过这个事件传递给父组件。
父组件部分:
在父组件 App.vue 中,声明了一个方法 getUser,用 transferUser 事件调用 getUser 方法,获取到从子组件传递过来的参数 username。
getUser方法中的参数 msg 就是从子组件传递过来的参数username。
Vue 没有直接子对子传参的方法,建议将需要传递数据的子组件,都合并为一个组件。如果一定需要子对子传参,可以先从子组件传到父组件,再传到子组件。
为了便于开发,Vue 推出了一个状态管理工具 Vuex(详参博文《Vue进阶(五):与 Vuex 的第一次接触》),可以很方便实现组件之间的参数传递。
子组件用Props接收父组件传来的消息有多种形式:
1.数组形式
props: [data1, data2]数组形式相当于直接接收消息,不做任何校验,一般来说,不太建议使用数组形式。
2.简单对象形式
props: { data1: String, data2: Array}简单对象形式对父组件传递的值进行了类型校验,如果传过来的值类型不一致,控制台会报错。
3.复杂对象形式
props: {
data1: {
type: String, //设定类型
required: true, //是否必须
default: 'default value', //默认值
validator (value) { return (value.length < 5) } }, //校验规则
data2: {
type: Array,
required: true,
default: () => ['', '', '']
}}复杂对象形式的情况下,作为对象属性参数可以写为对象形式,参数对象含有4个属性,
注意:prop 会在一个组件实例创建之前进行验证,所以实例的属性 (如 data、computed 等) 在 default 或 validator 函数中是不可用的。
type 可以是下列原生构造函数中的一个:
String、Number、Boolean、Array、Object、Date、Function、Symbol
type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。
例如,给定下列现成的构造函数:
function Person (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}你可以使用:
Vue.component('blog-post', {
props: {
author: Person
}
})来验证 author prop 的值是否是通过 new Person 创建的。
言
我们可能听过C语言中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了。
传值
我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的:
#include<stdio.h>
void swap(int a,int b)
{
int temp = a;
a = b;
b = temp;
printf("swap a = %d,b = %d\n",a,b);
}
int main(void)
{
int a = 10;
int b = 20;
printf("before swap:a = %d,b = %d\n",a,b);
swap(a,b);
printf("after swap:a = %d,b = %d\n",a,b);
return 0;
}
运行结果如下:
before swap:a = 10,b = 20 internal swap a = 20,b = 10 after swap:a = 10,b = 20
可以看到,a和b的值最终并没有被交换。开始时a,b的值为10,20,而最终还是同样的值。
为什么呢?因为函数参数在传递的时候,都是传原数据的副本,也就是说,swap内部使用的a和b只是最初始a和b的一个副本而已,所以无论在swap函数内部对a和b做任何改变,都不会影响初始的a和b的值。
正因如此,我们常常被告知,不要把直接把结构体直接作为参数,这样效率会很低。由于结构体本身占用字节数较大,如果直接作为参数,那么将会产生一个较大的”副本“,如此一来,效率也就很低了。
我们再结合下面的图来理解:
值传递
首先图中方框中的上部分a和b代表了main函数中的a和b,即原始数据,而方框中的下部分a和b代表了函数的参数a和b,即原始数据的“副本”。(后面的图都是如此,上部分代表原始值,下部分代表函数参数值)。
调用swap函数前后的情形如下:
调用swap前后
由于在swap中永远只是对a和b的副本进行操作,因此完全不影响原始的a和b的值。最终也不可能达到交换a和b的值的目的。
传指针
那么为解决上面的问题,我们知道,需要传指针。其代码如下:
#include<stdio.h>
void swap(int *a,int *b)
{
int temp = *a;
*a = *b;
*b = temp;
printf("swap a = %d,b = %d\n",*a,*b);
}
int main(void)
{
int a = 10;
int b = 20;
printf("before swap:a = %d,b = %d\n",a,b);
swap(&a,&b);
printf("after swap:a = %d,b = %d\n",a,b);
return 0;
}
运行结果:
before swap:a = 10,b = 20 swap a = 20,b = 10 after swap:a = 20,b = 10
可以看到在这种情况下,a,b的值才是真正交换了。
为什么又有传值,又有传指针
看到这里,不知道你是否会疑惑,为什么给函数传递参数的时候,一会是传值,一会是传指针呢?为什么传指针就能改变参数的值呢?实际上,C语言里,参数传递都是值传递!也就是说,你认为的传指针也是传值,只不过它的值是指针类型罢了。
我们再通过图来理解前面为什么传指针就可以交换a,b的值:
传指针
从图中可以看出,虽然传递给函数的是指向a和b的指针的副本,但是它的副本同样也是指向a和b,因此虽然不能改变指针的指向,但是能改变参数a和b指向的内容,即改变原始a和b的值。
再看传指针
如果是为指针p申请一段内存,下面的代码能达到目的吗?
#include<stdio.h>
#include<stdlib.h>
void getMemery(int *p)
{
/*申请1024个int大小*/
p = malloc(sizeof(int)*1024);
if(NULL == p)
{
printf("malloc failed\n");
p = NULL;
}
}
int main(void)
{
int *p = NULL;
getMemery(p);
printf("address of p is %p\n",p);
return 0;
}
通过前面的内容分析,肯定是达不到预期效果的。
运行结果:
address of p is (nil)
这是为什么呢?我们还是利用前面所知来分析,由于传递给getMemory函数的参数都是一个副本,因此函数内的p也是外部p的一个副本,因此即便在函数内部,将p指向了一块新申请的内存,仍然不会改变外面p的值,即p还是指向NULL。
、
如何修改呢?我们需要传入p的地址,即指向int类型指针的指针。
#include<stdio.h>
#include<stdlib.h>
void getMemery(int **p)
{
/*申请1024个int大小*/
*p = malloc(sizeof(int)*1024);
if(NULL == *p)
{
printf("malloc failed\n");
*p = NULL;
}
}
int main(void)
{
int *p = NULL;
getMemery(&p);
printf("address of p is %p\n",p);
free(p);
p = NULL;
return 0;
}
运行结果如下:
address of p is 0x144f010
从运行结果可以看到,p的值被改变了。
可配合下面的图进行理解:
总结
本文总结如下:
本文原地址:https://www.yanbinghu.com/2019/06/20/53981.html
思考
微信公众号【编程珠玑】:专注但不限于分享计算机编程基础,Linux,C语言,C++,数据结构与算法,工具,资源等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源。欢迎一起交流学习,一起修炼计算机“内功”,知其然,更知其所以然。
JavaScript 类似,要在 TypeScript 中将函数作为参数传递,定义一个函数期望接收回调函数的参数,然后在父函数内部触发回调函数。 例如,请注意我们在以下示例中调用 foo 函数时如何将 bar 作为回调函数传递:
但是,此代码存在问题。如果复制此代码到 JavaScript 文件中,不会出现任何语法错误,因为我们没有使用任何 TypeScript 类型。此外,我们遇到了传递任何参数值的可能性,这意味着我们可以提供非函数的任意类型作为 foo 的参数,而不会“出错”,直到代码执行。因为这里callback被默认解释为any类型。
使用Function定义函数参数类型
现在我们把 callback 指定为函数类型,
如果我们再传入非函数,就会报错了!
定义从 Function 类型返回的期望值
使用 Function 类型是防止由于 JavaScript 中缺少类型定义而导致的常见错误的一大步。 但是,使用 Function 类型仍然会为逻辑中的潜在错误留下空间。
如果接收回调函数的函数使用回调函数的返回值来运行额外的进程怎么办? 为了使这个假设更清楚,让我们对 foo 函数进行更改。
注意 foo 函数不仅调用回调函数,而且还使用回调函数返回的值来获取该值的平方根,假设回调函数返回的值是一个数字。如果你回顾返回函数的定义,如果这个函数没有返回任何东西,默认返回值是undefined。
它返回undefined
因此,如果我们将 bar 作为 foo 的回调函数传递,在运行时可能可能会出现意外行为。
所以,我们可以通过定义回调函数的返回值的类型来防止 TypeScript 中出现这种意外行为。 于是我们就不能在这里继续使用Function类型。 相反,使用返回类型的箭头函数表达式来提供有效的类型定义。 让我们再修改一下 foo 函数的定义来理解这个概念,
现在我们定义了函数参数的返回值类型,所以现在的bar函数将不再满足,因为它返回是undefined, 自然它就会报错。
我们修改一下bar函数,让它满足callback的函数签名,就可以解决了。现在运行代码就安全多了。
*请认真填写需求信息,我们会在24小时内与您取得联系。