React Native 实例 - BBC新闻客户端

song4r4u 8年前
   <p> </p>    <p>关于 React Native 的实例, BBC新闻客户端. 通过访问BBC的公开网络接口, 获取新闻内容, 也可以根据类型显示. 在编写代码中, 学习RN的知识, 源码是使用ES6的规范编写, 符合非死book的RN代码最新规范.</p>    <p>欢迎Follow我的GitHub: <a href="/misc/goto?guid=4959652813041333444" rel="nofollow,noindex">https://github.com/SpikeKing</a></p>    <p><img src="https://simg.open-open.com/show/b7b369d87c0cae56632756968a0f40da.png"></p>    <p>主要技术</p>    <ol>     <li>访问网络请求, 过滤内容, 获取数据.</li>     <li>显示多媒体页面, 图片, 视频, 链接等.</li>    </ol>    <p>本文源码的GitHub <a href="/misc/goto?guid=4959672535252309475" rel="nofollow,noindex">下载地址</a></p>    <h2>配置项目</h2>    <p>初始化项目 <strong>WclBBCNews</strong> , 修改 package.json , 添加依赖库.</p>    <p>Html解析库: htmlparser , 时间处理库: moment , 线性梯度库: <a href="/misc/goto?guid=4959671528953312129" rel="nofollow,noindex"> react-native-linear-gradient </a> , 视频库: react-native-video .</p>    <pre>  <code class="language-javascript">"dependencies": {    "react": "^0.14.8",    "react-native": "^0.24.1",      "htmlparser": "^1.7.7",    "moment": "^2.11.1",      "react-native-linear-gradient": "^1.4.0",    "react-native-video": "^0.6.1"  }  </code></pre>    <p>目前, React Native禁止使用 - 初始化项目名称, 最好使用驼峰式.</p>    <p>初始化主模块 index.ios.js , 使用 NavigatorIOS 导航页面, 首页组件 <strong>Feed模块</strong> .</p>    <pre>  <code class="language-javascript">render() {    return (      <NavigatorIOS   style={{flex:1}}   translucent={false}   barTintColor={'#BB1919'}   titleTextColor={'white'}   tintColor={'white'}   initialRoute={{   component: Feed,   title: "Feed",   passProps: {}   }}/>   );  }  </code></pre>    <p>渲染使用动态加载组件, StatusBar使用浅色样式.</p>    <pre>  <code class="language-javascript">_renderScene(route, navigator) {    var Component = route.component;    StatusBar.setBarStyle('light-content');    return (      <Component   {...route.props}   changeNavBarHeight={this.changeNavBarHeight}   navigator={navigator}   route={route}/>   );  }  </code></pre>    <p><a href="/misc/goto?guid=4959672535365246235" rel="nofollow,noindex">StatusBar</a> 样式只有两种, 默认 default , 字是黑色; 可选 light-content , 字是白色.</p>    <h2>新闻列表</h2>    <p>Feed页面, 主要以列表形式, 即 ListView 标签, 显示新闻. 未加载完成时, 调用页面加载提示符 ActivityIndicatorIOS , 显示动画.</p>    <pre>  <code class="language-javascript">render() {    // 未加载完成时, 调用加载页面    if (!this.state.loaded) {      return this._renderLoading();    }    // ...  }    _renderLoading() {    return (      <View style={{flexDirection: 'row', justifyContent: 'center', flex: 1}}>   <ActivityIndicatorIOS   animating={this.state.isAnimating}   style={{height: 80}}   size="large"/>   </View>   );  }  </code></pre>    <p>加载完成后, 调用 ListView 显示页面, renderRow 渲染每一行, refreshControl 加载页面的过场.</p>    <pre>  <code class="language-javascript">return (    <ListView      testID={"Feed Screen"}      dataSource={this.state.dataSource}      renderRow={this._renderStories.bind(this)}      style={{backgroundColor: '#eee'}}      contentInset={{top:0, left:0, bottom: 64, right: 0}}      scrollEventThrottle={200}      {...this.props}      refreshControl={        <RefreshControl          refreshing={this.state.isRefreshing}          onRefresh={this._fetchData.bind(this)}          tintColor='#BB1919'          title="Loading..."          progressBackgroundColor="#FFFF00"        />}      />  );  </code></pre>    <p>每一行使用 Story 模块渲染.</p>    <pre>  <code class="language-javascript">_renderStories(story) {    return (      <Story story={story} navigator={this.props.navigator}/>   );  }  </code></pre>    <p>启动页面的时候, 使用 fetch 方法加载数据.</p>    <pre>  <code class="language-javascript">componentDidMount() {    this._fetchData();  }  </code></pre>    <p>通过访问BBC的网络请求, 异步获取数据. 使用 _filterNews 过滤需要的数据, 把数据设置入每一行, 修改状态 setState , 重新渲染页面.</p>    <pre>  <code class="language-javascript">_fetchData() {    this.setState({isRefreshing: true});    fetch(`http://trevor-producer-cdn.api.bbci.co.uk/content${this.props.collection || '/cps/news/world'}`)      .then((response) => response.json())      .then((responseData) => this._filterNews(responseData.relations))      .then((newItems) => {        this.setState({          dataSource: this.state.dataSource.cloneWithRows(newItems),          loaded: true,          isRefreshing: false,          isAnimating: false        })      }).done();  }  </code></pre>    <p>列表项提供分类显示功能, 点击类别, 可以重新加载所选类型的新闻, 把 Feed 页面再次添加至导航 navigator , 即页面栈.</p>    <pre>  <code class="language-javascript">_pressedCollection(collection) {    this.props.navigator.push({      component: Feed,      title: collection.content.name,      passProps: {        collection: collection.content.id,        navigator: this.props.navigator      }    });  }  </code></pre>    <p>点击列表项, 跳转至详情页面 StoryDetail .</p>    <pre>  <code class="language-javascript">_pressedStory(story) {    this.props.navigator.push({      component: StoryDetail,      title: this._truncateTitle(story.content.name),      passProps: {story, navigator: this.props.navigator}    });  }  </code></pre>    <p><img src="https://simg.open-open.com/show/a2be796f248441b5dad4f8ba40b0a21a.png"></p>    <h2>新闻详情</h2>    <p>主要是解析HTML页面, 加载并显示, 除了文字之外, 会显示图片\视频\超链接等样式. 渲染使用动态元素, 状态 state 的 elements 属性.</p>    <pre>  <code class="language-javascript">render() {    if (this.state.loading) {      return (        <Text>Loading</Text>      );    }    return this.state.elements;  }  </code></pre>    <p>页面启动时, 加载数据. 在 _fetchStoryData 方法中, 进行处理, 使用回调返回数据. 主要内容 body 与多媒体 media 通过滚动视图 ScrollView 的形式显示出来.</p>    <pre>  <code class="language-javascript">componentDidMount() {    this._fetchStoryData(      // media表示视频或图片.      (result, media) => {        const rootElement = result.find(item => {          return item.name === 'body';        });          XMLToReactMap.createReactElementsWithXMLRoot(rootElement, media)          .then(array => {            var scroll = React.createElement(ScrollView, {              contentInset: {top: 0, left: 0, bottom: 64, right: 0},              style: {flex: 1, flexDirection: 'column', backgroundColor: 'white'},              accessibilityLabel: "Story Detail"            }, array);              this.setState({loading: false, elements: scroll});          });      }    );  }  </code></pre>    <p>处理数据, 使用 fetch 方法, 分离视频与图片, 还有页面, 通过回调 cb(callback) 的处理返回数据.</p>    <pre>  <code class="language-javascript">_fetchStoryData(cb) {    // 提取数据, 转换JSON格式, 图片过滤, 视频过滤, 组合relations, 解析.    fetch(`http://trevor-producer-cdn.api.bbci.co.uk/content${this.props.story.content.id}`)      .then((response) => response.json())      .then((responseData) => {        const images = responseData.relations.filter(item => {          return item.primaryType === 'bbc.mobile.news.image';        });        const videos = responseData.relations.filter(item => {          return item.primaryType === 'bbc.mobile.news.video';        });        const relations = {images, videos};        this._parseXMLBody(responseData.body, (result) => {          cb(result, relations);        });      }).done();  }  </code></pre>    <p>使用 Tautologistics 解析 dom 数据与 body 数据. DOM, 即Document Object Model, 文件对象模型.</p>    <pre>  <code class="language-javascript">_parseXMLBody(body, cb) {    var handler = new Tautologistics.NodeHtmlParser.DefaultHandler(      function (error, dom) {        cb(dom)      }, {enforceEmptyTags: false, ignoreWhitespace: true});      var parser = new Tautologistics.NodeHtmlParser.Parser(handler);    parser.parseComplete(body);  }  </code></pre>    <p>XML解析类 XMLToReactMap 比较复杂, 不做过多介绍, 参考源码.</p>    <p><img src="https://simg.open-open.com/show/b8e45e39dd16cf1970f84e19528bb3cc.png"></p>    <p>通过编写新闻类应用, 学习使用网络请求和解析HTML格式的文本. 多编码多思考, 不断学习, React Native 是非常有意思的开发语言.</p>    <p> </p>    <p>OK, that’s all! Enjoy it!</p>    <p>来自: https://github.com/SpikeKing/WclBBCNews</p>