浅析react-native的Navigator组件02之数据传送

前言:【初级内容】之前已经展示了一个最简单的Navigator的用法,虽然的确满足了页面弹来弹去的效果。但在实际使用的时候,我们会有各种各样的额外需求,比如页面与页面之间的数据怎么传递、页面跳转动画是否可以更换等。接下来就一步步整这些额外的东西。

renderScene传数据版

demo: 链接 (基于0.17,ios)

之前的 renderScene代码是这样:

1
2
3
4
5

renderScene={(route, navigator)=> {
let Component = route.component;
return <Component navigator={navigator}/>
}

然后push下一页的代码是这样的:

1
2
3
4
5
6
const { navigator } = this.props;
if(navigator){
navigator.push({
component:Secondpage,
})
}

就简单的return了一个包含了navigator参数的组件,想要跳转页面,那么你push什么组件(页面)给我,我就跳转什么组件。但如果想要额外传数据就只能干瞪眼。想要传数据怎么办,其实只需要加很少的代码就能实现,如下:

1
2
3
4
renderScene={(route, navigator)=> {
let Component = route.component;
return <Component {...route.params} navigator={navigator}/>
}

这是直接抄用的react-native中文论坛的高手帖子的内容 链接 。虽然看似简单的点点点,牵涉的知识有点复杂,不好理解。但假如你不去理解背后的原理,就记住这个固定格式,用起来一样的非常方便。初始化代码修改完毕,push时传数据是这样的:

1
2
3
4
5
6
7
8
9
10
11
const { navigator } = this.props;
if(navigator){
navigator.push({
component:Secondpage,
params:{
id:1
info:'from firstpage'
}

})
}

添加了一个叫做params的对象,对象里面随便放东西。放的东西怎么取呢,比如之前那个是从第一页跳转到第二页时传递过来的,在第二页我们这么取:

1
2
3
this.props.id

this.props.info

就这么简单,params的对象里面用什么key存储的内容,就直接this.props.key调用就行了。在demo里面添加了从第一页跳转第三页的按钮,可以看得见,一跳三,和二跳三的时候,这个第三页展示的props.id和props.info的值是不一样的。
当然,你不想传数据,不写哪个params对象也完全没有任何问题。

三个点的故事

以下是阐述原理时间,如果只想速度知道navigator传值的用法,可以不用往下看了。想知道背后的原理,请继续(希望我能讲清楚)

其实,这里如果你把

1
return <Component {...route.params} navigator={navigator}/>

改成

1
return <Component params={route.params} navigator={navigator}/>

也是能用的,push的格式也不用变,只是在调用的时候需要多写点单词,就是 this.props.params.id和 this.props.params.info。大家可以看得出来,是为了少写单词,所以就改成了上面那个三个点的格式。看起来更简洁(装逼的说法就是更优雅),但通常这种语法理解起来会更困难。下面我尝试一下,把这个玩意儿给说清楚。

三个点是什么

首先,三个点在ES6中,有两个语法都使用了这三个点。

  • Rest parameters (剩余参数)

    如果一个函数的最后一个形参是以 … 为前缀的,则在函数被调用时,该形参会成为一个数组,数组中的元素都是传递给该函数的多出来的实参的值.

    1
    2
    3
    4
    function f(a,b,...args){
    console.log(args);
    }
    f(1,2,3,4,5,6)

输出为

1
3,4,5,6  //这个好理解,就是传进来的参数去掉前面两个代表a和b的,剩下的作为一个数组丢给args。
  • Spread operator (展开运算符)

    展开运算符允许一个表达式在某处展开,在多个参数(用于函数调用)或者多个元素(用于数组字面量)或者多个变量(用于解构赋值)的地方就会这样。
    大部分情况是展开数组

    1
    2
    3
    function f(x, y, z) { console.log(x+y+z)}
    var args = [0, 1, 2];
    f(...args) //直接把args数组展开成类似f(0,1,2)这样的东西,输出为3
1
2
var parts = ['shoulder', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes']; // lyrics的值为 ['head', 'shoulder', 'knees' , 'and', 'toes']

组件传的三个点又是啥

那么, <Component {…route.params} navigator={navigator}/>这里面的3个点,是展开运算呢还是剩余参数呢?这个就需要知道组件这种语法格式到底代表了什么。

1
<Component  navigator={navigator}/>

这样的组件语法,是个标准的JSX写法,它可以翻译为标准的JS语句,翻译过来是这样的:

1
React.createElement( Component   , { navigator :  navigator })  //

又如

1
2
<Component  navigator={navigator}  params={route.params} />   //包含了两个参数的组件
React.createElement( Component , { navigator : navigator, params:route.params }) //这是翻译过来的JS语句

也就是说, createElement的第二个参数就是一个对象,里面有一堆键值对,每个键值对在具体组件里面就能使用”this.props.键名”来调用所对应的值。
然后我们的重点:

1
<Component {...route.params}  navigator={navigator}/>

翻译过来是这样:

1
React.createElement(Component, Object.assign({},  route.params , {  navigator : navigator  }));

后面这句 Object.assign({}, route.params , { navigator : navigator }),它的意思就是把route.params这个对象的内容,还有后面那个 { navigator : navigator }给组合到一起,形成一个新对象。
比如 route.params的对象为{id:1,info:”from first page”}。那么上面那句话再翻译一遍,就是:

1
React.createElement(Component, { id:1, info:"from first page", navigator :  navigator } );

这下明白了吧,为什么跳转之后,组件可以直接用props.id的形式,拿到上一个组件push过来的route.params中的id的值。

那么,回答问题,组件里面这三个点是扩展运算呢还是剩余参数呢?答案都不是,是JSX看到ES6的三个点的用法之后,自己实现的一个叫做Spread Attributes(展开属性)的语法。是不是有种被骗的感觉……嗯,我当时谷歌了半天的扩展运算和剩余参数,最后在React的JSX文档上看到这个解释,就是这种感觉。反正再复习(学习)一遍ES6的语法也不是坏事,是吧 :)。

唔,又查了一会资料,组件三个点扩展对象不是JSX的发明,而是ES7的提案,叫做rest/spread Properties,专门用于对象的。在RN上可以用这个语法。用法和上面针对数组的rest和spread差不多,仅仅是把外面的中括号(数组表示)变成大括号(对象表示)而已。比如

1
2
3
4
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };///rest
x; // 1
y; // 2
z; // { a: 3, b: 4 }
1
2
let n = { x, y, ...z };//spead
n; // { x: 1, y: 2, a: 3, b: 4 }

不过有点不太一样的是,数组扩展的时候,是不会有重复的,它的下标会都不一样。但对象扩展的时候会遇到key重复的情况。这种情况,语法处理结果是后面的key的值覆盖前面的。如:

1
2
let n = {x:1, y:2, z:3}
let m ={...n, z:4} // m={x:1, y:2, z:4}

参考链接:
展开运算符
剩余参数
JSX之扩展属性