3.DOM DOM 是前端的一个概念,暂时可以粗略理解为一个页面的树形结构。 React 生命周期的三大阶段
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
在每个阶段都有相应的状态和与之对应的回调函数,具体可以看下图:
上图来自:贾鹏辉的技术博客:React Native之React速学教程(中)
集成与管理
1.指定版本初始化 在终端输入react-native demo --version 0.40.0命令以后,就会初始化一个React Native版本为0.40.0的项目。这个最初项目里面直接就包含了iOS和Android的工程文件夹,可以用对应的IDE打开后编译运行。
在新建一个React Native项目之后的根目录结构是这样的:
2.使用 Cocoapods 管理 ReactNative Podfile 文件格式:
pod 'React', :path => './node_modules/react-native', :subspecs => [ 'Core', 'RCTText', 'RCTImage', 'RCTActionSheet', 'RCTGeolocation', 'RCTNetwork', 'RCTSettings', 'RCTVibration', 'RCTWebSocket', ]
ReactNative 0.42.0 以上版本需在 Podfile 配置 yoga:
# 如果你的RN版本 >= 0.42.0,请加入下面这行 pod "yoga", :path => "./node_modules/react-native/ReactCommon/yoga"
输入react-native run-ios或者react-native run-android指令, 就会自动打开模拟器运行项目(前提是安装了相应的开发环境)。
但是一个比较完整的项目仅仅有这些类别的文件是不够的,还需要一些工具类,模型类,资源等文件。为了很好地区分它们,使项目结构一目了然,需要组织好项目文件夹以及类的命名,下面是我将教程里的文件夹命名和结构稍加修改后的一个方案,可供大家参考:
布局约束
采用Flex布局的元素,被称为Flex container,其所有子元素被称为Flex item;容器默认存在两个轴,分别是主轴(main axis)和垂直的交叉轴(cross axis),主轴开始的位置叫做main start,结束的位置叫main end;交叉轴的开始位置叫做cross start,结束的位置叫做cross end;单个item占据的主轴空间叫做main size,占据的交叉轴控件叫做cross size。
如下图所示:
组件化驱动下,搜索结果页中展示的 Cell 与之前的列表页 Cell 可以重用:
我们把该组件定名为:RespositoryCell,结合代码来看一下具体的实现:
export default class RepositoryCell extends Component { constructor(props) { super(props); this.state = { isFavorite: this.props.projectModel.isFavorite, favoriteIcon: this.props.projectModel.isFavorite ? require('../../res/images/ic_star.png') : require('../../res/images/ic_unstar_transparent.png'), }; } componentWillReceiveProps(nextProps) { this.setFavoriteState(nextProps.projectModel.isFavorite) } setFavoriteState(isFavorite) { this.props.projectModel.isFavorite = isFavorite; this.setState({ isFavorite: isFavorite, favoriteIcon: isFavorite ? require('../../res/images/ic_star.png') : require('../../res/images/ic_unstar_transparent.png') }) } onPressFavorite() { this.setFavoriteState(!this.state.isFavorite) this.props.onFavorite(this.props.projectModel.item, !this.state.isFavorite) } render() { let item = this.props.projectModel.item? this.props.projectModel.item:this.props.projectModel; let favoriteButton=this.props.projectModel.item? <TouchableOpacity style={{padding:6}} onPress={()=>this.onPressFavorite()} underlayColor='transparent'> <Image ref='favoriteIcon' style={[{width: 22, height: 22,},this.props.theme.styles.tabBarSelectedIcon]} source={this.state.favoriteIcon}/> </TouchableOpacity>:null; return ( <TouchableOpacity onPress={this.props.onSelect} style={styles.container} > <View style={styles.cell_container}> <Text style={styles.title}>{item.full_name}</Text> <Text style={styles.description}>{item.description}</Text> <View style={styles.row}> <View style={styles.row}> <Text>Author:</Text> <Image style={{height: 22, width: 22}} source={{uri: item.owner.avatar_url}} /> </View> <View style={{justifyContent: 'space-between', flexDirection: 'row'}}> <Text>Star:</Text> <Text>{item.stargazers_count}</Text> </View> {favoriteButton} </View> </View> </TouchableOpacity> ) } }
- 这里声明了RespositoryCell组件,它继承于Component,也就是组件类,即是说,声明组件的时候必须都要继承与这个类。
- 集中看一下该组件的render方法,它返回的是该组件的实际布局:在语法上使用JSX,类似于HTML的标签式语法,很清楚地将cell的层级展现了出来:
- 最外层被一个View组件包裹着,里面第一层有三个子组件:两个Text组件和一个作为底部背景的View组件。
- 底部背景的View组件又有三个子组件:View组件(显示作者信息),View组件(显示star信息),收藏按钮。
结构分解图:
组件封装
对于“我的页面”和“个人中心”这类结构相似的页面,建议进行组件封装,封装后的 AboutPage 实现代码简洁如下:
export default class AboutPage extends Component{ constructor(props) { super(props); this.aboutCommon=new AboutCommon(props,(dic)=>this.updateState(dic),FLAG_ABOUT.flag_about,config); this.state = { projectModels: [], author:config.author } } componentDidMount() { this.aboutCommon.componentDidMount(); } componentWillUnmount() { this.aboutCommon.componentWillUnmount(); } updateState(dic){ this.setState(dic); } onClick(tab) { let TargetComponent, params = {...this.props,menuType:tab}; switch (tab) { case MORE_MENU.About_Author: TargetComponent = AboutMePage; break; case MORE_MENU.Website: TargetComponent = WebViewPage; params.title='GitHubPopular'; var url='https://reversescale.github.io'; params.url=url; break; case MORE_MENU.Feedback: var url='mailto://reversescale@icloud.com'; Linking.canOpenURL(url).then(supported => { if (!supported) { console.log('Can\'t handle url: ' + url); } else { return Linking.openURL(url); } }).catch(err => console.error('An error occurred', err)); break; case MORE_MENU.Share: break; } if (TargetComponent) { this.props.navigator.push({ component: TargetComponent, params: params, }); } } render() { let content=<View> {this.aboutCommon.renderRepository(this.state.projectModels)} {ViewUtils.getSettingItem(()=>this.onClick(MORE_MENU.Website), require('../../../res/images/ic_computer.png'), MORE_MENU.Website, this.props.theme.styles.tabBarSelectedIcon)} <View style={GlobalStyles.line}/> {ViewUtils.getSettingItem(()=>this.onClick(MORE_MENU.About_Author), require('../my/img/ic_insert_emoticon.png'), MORE_MENU.About_Author, this.props.theme.styles.tabBarSelectedIcon)} <View style={GlobalStyles.line}/> {ViewUtils.getSettingItem(()=>this.onClick(MORE_MENU.Feedback), require('../../../res/images/ic_feedback.png'), MORE_MENU.Feedback, this.props.theme.styles.tabBarSelectedIcon)} </View> return this.aboutCommon.render(content, { 'name': 'GitHub Popular', 'description': '这是一个用来查看GitHub最受欢迎与最热项目的App,它基于React Native支持Android和iOS双平台。', "avatar": "http://og1yl0w9z.bkt.clouddn.com/18-3-28/61685877.jpg", "backgroundImg": "http://og1yl0w9z.bkt.clouddn.com/18-3-28/37407402.jpg", }); } }