3.1 创建新的电商App
之前创建了一个简单的电商项目,本节来实现对该项目的重构。
3.1.1 移植旧电商项目
(1)先创建React Native项目并安装依赖包。
react-native init ch04 // 新建React Native项目ch04
cd ch04
npm install // 或者使用cnpm安装:cnpm install
?小知识:npm install命令还可以简写成npm i,更多说明可以使用npm help install查看帮助文档。
(2)将第2章ch03项目中的index.ios.js、index.android.js以及app.js文件都复制到ch04文件夹中。完成文件复制和覆盖之后,ch04项目的结构如图3.1所示。
但是,如果这时候直接运行App,会发生应用ch04没有注册的错误,如图3.2所示。
发生错误的AppRegistry.registerComponent函数在第1章的例子中就遇到过,那么它到底有什么作用呢?
图3.1 ch04项目的结构 图3.2 应用ch04没有注册的错误
原来,AppRegistry是所有React Native应用的入口,应用的根组件通过AppRegistry. registerComponent方法注册自己,然后原生系统才可以加载React Native应用的代码并运行React Native应用。流程如图3.3所示。
图3.3 AppRegistry.registerComponent注册React Native应用的根组件
(3)了解AppRegistry的原理之后,就可以轻松地找到错误的原因,index.ios.js和index.android.js文件中注册的应用名称不正确,React Native应用中注册的应用是ch03,而react-native命令行工具生成的原生代码运行的应用是ch04,产生错误的代码如下:
// index.ios.js和index.android.js文件
AppRegistry.registerComponent('ch03', () => app); // React Native注册ch03
// iOS原生项目中的AppDelegate.m文件
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"ch04" // 原生代码运行ch04
initialProperties:nil
launchOptions:launchOptions];
// Android原生项目中的MainActivity.java文件
@Override
protected String getMainComponentName() {
return "ch04"; // 原生代码运行ch04
}
?提示:这里是我们第一次接触到React Native开发中的原生代码,通常情况下不需要修改原生代码,只要简单了解即可。
(4)将React Native代码中注册的应用名称ch03修改为ch04即可,修改index.ios.js和index.android.js代码如下:
01 AppRegistry.registerComponent('ch04', () => app);
?提示:由于iOS App和Android App的入口文件不同,分别为index.ios.js和index.android.js,所以ch04项目中的这两个文件都需要按照上述代码进行修改。
重新加载应用,效果如图3.4所示。
图3.4 注册正确的应用名称后的运行效果
3.1.2 重构现有的代码
在成功移植ch03项目的实现到新建的ch04项目后,我们就可以开动了:完善应用。但是,笔者在实际开发过程中总结的经验是,完善的第一步往往是从梳理和重构现有代码开始,而非立即添加新代码或功能。只有不断地重构然后添加新功能,代码的可维护性才会越好,这也是应用稳定性和扩展性的重要保证。
重新审视app.js文件的代码,从中不难发现这样的问题:ScrollView组件下所有子组件的样式都是类似的,导致很多冗余代码。ScrollView组件现有代码如下:
01 export default class app extends Component {
02 // 这里省略了没有修改的代码
03
04 render() {
05 return (
06
07 // 这里省略了没有修改的代码
08
09 10 horizontal={true}
11 showsHorizontalScrollIndicator={false}
12 pagingEnabled={true}>
13 Alert.alert
('你单击了轮播图', null, null)}>
14 15 width: Dimensions.get('window').width,
16 height: 180,
17 backgroundColor: 'gray'
18 }}>广告1
19
20 Alert.alert
('你单击了轮播图', null, null)}>
21 22 width: Dimensions.get('window').width,
23 height: 180,
24 backgroundColor: 'orange'
25 }}>广告2
26
27 Alert.alert
('你单击了轮播图', null, null)}>
28 29 width: Dimensions.get('window').width,
30 height: 180,
31 backgroundColor: 'yellow'
32 }}>广告3
33
34
35
36 // 这里省略了没有修改的代码
37
38 );
39 }
40
41 // 这里省略了没有修改的代码
42 }
如果想更新轮播广告的高度的话,就需要修改多处代码heigh: 180。因此,可以尝试将重复的样式代码抽离出来,代码效果如下:
01 export default class app extends Component {
02 // 这里省略了没有修改的代码
03
04 render() {
05 return (
06
07 // 这里省略了没有修改的代码
08
09 10 horizontal={true}
11 showsHorizontalScrollIndicator={false}
12 pagingEnabled={true}>
13 Alert.alert
('你单击了轮播图', null, null)}>
14 15 styles.advertisementContent, {
16 backgroundColor: 'gray'
17 }
18 ]}>广告1
19
20 Alert.alert
('你单击了轮播图', null, null)}>
21 22 styles.advertisementContent, {
23 backgroundColor: 'orange'
24 }
25 ]}>广告2
26
27 Alert.alert
('你单击了轮播图', null, null)}>
28 29 styles.advertisementContent, {
30 backgroundColor: 'yellow'
31 }
32 ]}>广告3
33
34
35
36 // 这里省略了没有修改的代码
37
38 );
39 }
40
41 // 这里省略了没有修改的代码
40 }
其中,添加的样式advertisementContent定义如下:
01 const styles = StyleSheet.create({
02 // 这里省略了没有修改的代码
03 advertisementContent: {
04 width: Dimensions.get('window').width,
05 height: 180
06 },
07 // 这里省略了没有修改的代码
08 });
这样,当要修改轮播广告页面的高度时,只需要修改样式advertisementContent这一处,所有广告页面的样式都可以同时更新。
虽然,样式代码做到了复用,但是TouchableHighlight组件和单击事件等还是重复的,对此,可以使用JavaScript数组的map()方法来做进一步优化,修改app.js代码如下:
01 export default class app extends Component {
02 constructor(props) {
03 super(props);
04 this.state = {
05 advertisements: [ // 轮播广告数组
06 { // 数组中的每个成员描述了广告的详细信息
07 title: '广告1',
08 backgroundColor: 'gray'
09 }, {
10 title: '广告2',
11 backgroundColor: 'orange'
12 }, {
13 title: '广告3',
14 backgroundColor: 'yellow'
15 }
16 ],
17 };
18 }
19
20 // 这里省略了没有修改的代码
21
22 render() {
23 return (
24
25 // 这里省略了没有修改的代码
26
27 28 horizontal={true}
29 showsHorizontalScrollIndicator={false}
30 pagingEnabled={true}>
31 {this.state.advertisements.map((advertisement,
index) => {
32 return (
33
{() => Alert.alert('你单击了轮播图', null,
null)}>
34 35 styles.advertisementContent, {
36 backgroundColor: advertisement.
backgroundColor
37 }
38 ]}>{advertisement.title}
39
40 );
41 })}
42
43
44 // 这里省略了没有修改的代码
45
46 );
47 }
48
49 // 这里省略了没有修改的代码
50 }
此时,如果想要修改、添加或删除广告页,对于重构后的代码就非常简单,只需要编辑this.state.advertisements数组即可。重新加载应用,效果如图3.5所示。