本篇为翻译文章,原文地址这里
React/React Native一个很受欢迎的地方在于它能够在不同平台之间共享业务逻辑,在看完 Desktop, Mobile, and Web app in one project这个项目之后笔者就开始思考应该如何组织项目结构,能够使其在web、desktop(Electron)以及mobile应用之间尽可能地共用相同的代码基础,并且保证能在以后的项目中扩展到其他平台。
文件索引
首先需要认识到在mobile项目与web/desktop项目中最大的代码区别在render()函数中,换言之,我们所需要做的工作就是将render函数抽象出来,以允许具体平台的工程可以使用正确的代码。
要做到这一点,最简单的方式就是利用React Native的一个内部特性,即在使用import/require导入文件时:
import File from './File';
React Native的打包器会首先寻找 File.<platform>.js
文件,然后再去寻找File.js
。这就允许我们将Android平台的代码放置到 File.android.js
, iOS 的放入到File.ios.js
, 以及Web平台的代码放入到 File.js
, 而不需要改变导入的声明 ./File
。
Render独立于Component
这种方式适合于可以在多个平台之间共享复杂状态与逻辑代码的情况下,当然,在该示例中有点过于复杂了。不过笔者认为在实际项目中的作用还是不言自明的。
基础的组件如下所示:
class Hello extends Component {
constructor(props) {
super(props);
}
render() {
// Code to render a container with
// "Hello, {props.name}" inside of it
}
}
在web项目中,Render函数如下所示:
render() {
return (<div>Hello, {this.props.name}</div>);
}
而在移动端项目中,Render函数可能如下所示:
render() {
return (<View>
<Text>Hello, {this.props.name}</text>
</View>);
}
那么整个项目的结构如下所示:
- index.js
- index.ios.js
- index.android.js
- src/
-- Hello.js
-- HelloRender.js
-- HelloRender.android.js
接下来将各个独立的渲染函数和它们的依赖放置到各自的文件中:
// HelloRender.js
import React from 'react';
export default function (props) {
// we would like a pure function, for easy testing
return (<div>Hello, {props.name}</div>);
}
// HelloRender.android.js
import React, {View, Text} from 'react-native';
export default function (props) {
return (<View>
<Text>Hello, {props.name}</text>
</View>);
}
最终在我们的Hello.js文件中:
// This will import our platform-specific render function
import Render from './HelloRender';
class Hello extends Component {
constructor(props) {
super(props);
}
render() {
// Now we call our render function,
// bound to the context of our component
// In this case we're only using component props,
// but later we may desire to access the state or
// other methods on the component.
return Render.call(this, this.props, this.state);
}
}
注意,虽然我们是针对不同平台使用了不同的渲染函数,但是还是可以共享很多的逻辑控制代码。
Component与模块冲突
上文中一直没有提及一件事,就是应该在哪边引入React / React Native提供的官方的组件,譬如通用的Component这个类。在最新的React v0.14中是分成了react与react-dom两个部分,它的设计思想在于通常的应用只要导入react的模块即可,而在特殊需要的时候可以选择性地导入react-native的模块,不过笔者在自己的试验中没发现这样的解决方法,因此还是决定用类似于上文提及的抽象render函数的方法。我们需要创建两个新的文件:
- index.js
- index.android.js
- src/
-- Component.js
-- Component.android.js
-- Hello.js
-- HelloRender.js
-- HelloRender.android.js
接下来,我们需要根据平台正确地导入不同的组件,即在新文件中:
// Component.js
export {Component} from 'react';
// Component.android.js
export {Component} from 'react-native';
最后,在我们的Hello.js这个统一的文件中:
// import platform-specific component class
import {Component} from './Component';
class Hello extends Component ...
好了,到这里一个完整的跨平台的构建应该是完成了。