-1004 reactnative

2017-03-28 19:48:04 sinat_17775997 阅读数 1511

网络封装

/**
 * 网络请求工具类
 * Songlcy create
 * params:请求参数
 * successCallback:成功回调
 * failCallback:失败回调
 */

import RootToast from '../pages/RootToast/RootToast'

var host = 'https://appft.ecpic.com.cn/cmps/access/doSubmit.do';
import ShowProgress from '../pages/ProgressHUD/ShowProgress';
let showProgress = new ShowProgress;

let Network = {

    postNetwork: (params,successCallBack, failCallBack)=> {

        if(params.isProgress === '1' ){
            showProgress.show();
        }

        console.log('***********************网络请求报文******************************');
        console.log( JSON.stringify(params));
        console.log('================================================================');

        paramStr = 'transCode=' + params.transCode + '&requestBodyJson=' + JSON.stringify(params.request);

        fetch(host,
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    "Cookie":JSON.stringify({})
                },
                body: paramStr
            }
        ).then((response) => {
            showProgress.hidden();
            if (response.ok) {
                return response.json();
            } else {
                RootToast.show('服务器连接异常');
            }
        }).then((response) => {
            console.log('***********************成功返回****************************');
            console.log( JSON.stringify(response));
            console.log('================================================================');
            if (response.responseBody == null) {
                RootToast.show(response.errorMsg);
                if (failCallBack && typeof(failCallBack) == 'function'){
                    failCallBack(response);
                }
            } else {
                successCallBack(response);
            }
        }).catch((error) => {
            showProgress.hidden();
            console.log(error.message);
            if (failCallBack && typeof(failCallBack) == 'function') {
                failCallBack(error);
            }
        })
    }
}
export default Network;

提示

/**
 * RootToast.js
 */

import Toast from 'react-native-root-toast';
let RootToast = {
    toast:null,
    show:(msg)=>{
      this.toast = Toast.show(msg,{
            position:0,
            duration:1500
        })
    }
}
export default RootToast;
/**
 * ShowProgress.js
 */


import React,{Component} from 'react'

import RootSiblings from 'react-native-root-siblings';

import ProgressHUD from './ProgressHUD';

let sibling = null;

export default class ShowProgress{

    show(){

        sibling  = new RootSiblings(<ProgressHUD />);

    }

    hidden(){

        if(sibling instanceof RootSiblings){
            sibling.destroy();
        }

    }

}

环形提示

/**
 * ProgressHUD.js
 */
import React, {Component} from 'react';

import {

    View,
    Dimensions,
    StyleSheet,
    Platform
}from 'react-native';

import  * as Progress from 'react-native-progress'
const {width, height} = Dimensions.get('window')
export default class ProgressHUD extends Component {
    render(){
        return(
            <View style={styles.baseViewStyle}>
                <View style={styles.backViewStyle}>
                    <Progress.Circle size={width/8} indeterminate={true} />
                </View>
            </View>
        );
    }
}

const styles = StyleSheet.create(
    {
        baseViewStyle:{
            position:'absolute',
            top:(Platform.OS=='ios')?64:44,
            height:(Platform.OS=='ios')?height-64:height-44,
            width:width,
            alignItems:'center',
            backgroundColor:'rgba(0,0,0,0.2)'
        },
        backViewStyle:{
            backgroundColor:'white',
            width:width/4,
            height:width/4,
            borderRadius:10,
            justifyContent:'center',
            alignItems:'center',
            marginTop:height/4
        }
    }
);

本地存储

/**
 * LocalStorage.js
 */
let LocalStorage = {

    /**
     * 保存数据到本地
     * key:数据存储key
     * params:请求参数
     */

    saveDataToLocal: (key,params)=> {

        global.localStorage.save({
            key: key,
            rawData: params,
            expires: null
        });

    },

    /**
     * 从本地读取数据
     * key:数据存储key
     * successCallback:成功回调方法
     * failCallback:失败回调方法
     */
    loadDataFromLocal:(key,successCallback,failCallback)=>{

        global.localStorage.load({
            key: key,

        }).then(ret => {

            successCallback(ret);

        }).catch(err => {

            switch (err.name) {
                case 'NotFoundError':
                    // TODO;
                    successCallback("");
                    break;
                case 'ExpiredError':
                    // TODO
                    break;
            }
        })
    }
}

export default LocalStorage;

调用网络请求

/**
 * Main.js
 */

import React, {Component} from 'react';
import {
    StyleSheet,
    Text,
    View,
    Image,
    Navigator,
    AsyncStorage
} from 'react-native';

import TabNavigator from 'react-native-tab-navigator';
import HomeView from './Home.js';
import EnjoyView from './Enjoy.js';
import LifeView from './Life.js';
import Storage from 'react-native-storage';

import LocalStorage from '../../LocalStorage/LocalStorage';
import Network from '../../Network/Network'

export default class Main extends Component {
    constructor(props) {
        super(props);
        this.state = {selectedTab: 'home'};
    }

    componentDidMount() {
        var storage = new Storage({
            size: 1000,
            storageBackend: AsyncStorage,
            defaultExpires:null,
            enableCache:true,
            sync : {
                // we'll talk about the details later.
            }
        });

        global.localStorage = storage;

        //本地存储accessToken
        var accessToken = '0af25eb6666aebf2a3f561b7a174311453204fb9c789a64b3934975cb6b5d13b9166f614785a0b41';
        LocalStorage.saveDataToLocal('accessToken', accessToken);

        //查询用户信息
        let params = {
            transCode: 'T1004',
            request: {
                loginName: '13020130525',
                accessToken: accessToken
            }
        };
        Network.postNetwork(params, this.successCallBack, this.failCallBack);
    }

    successCallBack(data) {

        LocalStorage.saveDataToLocal('userInfo', data.responseBody.user);

        LocalStorage.saveDataToLocal('myCarList', ['footer']);

        /*
        userInfo的格式
        {"loginName":"13020130525",
         "userName":"周舫旭",
         "idType":"1",
         "idNumber":"410381199004214033",
         "telphone":"13020130525",
         "email":"2222@163.com",
         "recommendMobile":null,
         "recommendTimes":0,
         "certFlag":"1",
         "headPicPath":null,
         "nickName":null,
         "gender":"0",
         "userId":27377,
         "address":"2222222",
         "branchCompany":"3010100",
         "postcode":"123545",
         "province":"上海&2",
         "city":"上海&201",
         "area":"长宁区&20101",
         "registerFlag":"1",
         "headImgBase64":null}
         */
    }

    failCallBack(data) {
        //console.log(data);
    }

    render() {
        return (
            <TabNavigator >
                {this.creatTabItem('home', '优保障',
                    require('../../image/icon/iconnavi1.png'),
                    require('../../image/icon/icon-navi-1-cur.png'),
                    styles.homeSelectStyle,
                    <HomeView navigator={this.props.navigator}/>
                )}
                {this.creatTabItem('enjoy', '享服务',
                    require('../../image/icon/icon-navi-2.png'),
                    require('../../image/icon/icon-navi-2-cur.png'),
                    styles.enjoySelectedStyle,
                    <EnjoyView navigator={this.props.navigator}/>
                )}
                {this.creatTabItem('life', '汇生活',
                    require('../../image/icon/icon-navi-3.png'),
                    require('../../image/icon/icon-navi-3-cur.png'),
                    styles.lifeSelectStyle,
                    <LifeView navigator={this.props.navigator}/>
                )}
            </TabNavigator>
        );
    }

    creatTabItem(selected, title, renderIcon, selectedIcon, selectedTitleStyle, childView) {

        return (
            <TabNavigator.Item selected={this.state.selectedTab === selected}
                               title={title}
                               renderIcon={() => <Image source={renderIcon} style={styles.iconStyle}/>}
                               renderSelectedIcon={() => <Image source={selectedIcon} style={styles.iconStyle}/>}
                               selectedTitleStyle={selectedTitleStyle}
                               onPress={() => this.setState({selectedTab: selected})}>
                {childView}
            </TabNavigator.Item>
        );
    }
}

const styles = StyleSheet.create({
    homeSelectStyle: {
        color: '#005bac'
    },
    enjoySelectedStyle: {
        color: '#009ddc'
    },
    lifeSelectStyle: {
        color: 'orange'
    },
    iconStyle: {
        width: 25,
        height: 30,
        resizeMode: 'contain'
    }
});
2017-02-21 17:41:33 sara_xuehua 阅读数 1016

【React Native开发】React Native 进阶之原生混合与数据通信开发详解-适配iOS开发(61)

尊重版权,未经授权不得转载

本文来自:江清清的技术专栏(http://www.lcode.org)

(一)前言

今天我们来看一下React Native iOS开发中,React界面和原生iOS混合开发以及数据相互传输详解。

刚创建的React Native交流6群:426762904,欢迎各位大牛,React Native技术爱好者加入交流!同时博客右侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

[特别提醒].在看本文章之前,首先确定自己已经学过我的博客另外两节内容了:

1.1.React Native进阶之原生模块封装基础篇详解-适配iOS开发

1.2.React Native进阶之原生模块封装特性篇详解-适配iOS开发

从现阶段RN的发展程度来看,RN和原生混合开发模式是比较理想的,所以我们非常有必要讲解一下RN和原生混合的开发模式,同时里边有关的数据交互通信知识点也有必要讲解一下啦。那么现在我们来一起看一下该怎么样去实现。本篇主要会讲解如下几部分内容:①.React Native访问调用iOS方法,②.iOS访问调用React Native,③.原生界面跳转RN界面。本文章讲解是是适配iOS平台开发的,如果你需要看Android部分的请移步:http://www.lcode.org/?p=1689

本文章实例项目地址:https://github.com/jiangqqlmj/hunheDemo

(二).原生界面跳转RN界面

现阶段混合开发中,一般就是在原有原生项目基础上面添加RN开发的页面。那么这边我们讲解一下从原生界面跳转到RN页面的方法。其实是非常简单的,就是普通push一个ViewController即可,在新打开的ViewController中加入RCTRootView视图,具体承载RN页面的控制器的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#import "TwoViewController.h"
#import "RCTRootView.h"
#import "ThreeViewController.h"
@implementation TwoViewController
- (void)viewDidLoad {
  [super viewDidLoad];
  self.title=@"RN界面";
  NSURL *jsCodeLocation=[NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"hunheDemo"
                                               initialProperties:nil
                                                   launchOptions:nil];
  self.view=rootView;
}
@end
(三).React Native访问调用iOS原生方法

要实现这个功能,我们首先需用创建一个实现"RCTBridgeModule"协议的RNBridgeModule桥接类,看一下RNBridgeModule.h文件

?
1
2
3
4
5
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"
@interface RNBridgeModule : NSObject<RCTBridgeModule>
 
@end

接着我们需要在RNBridgetModule的实现类中,实现RCT_EXPORT_MODULE()宏定义,括号参数不填为默认桥接类的名称,也可以自定义填写。该名称用来指定在JavaScript中访问这个模块的名称。

3.1.使用Callback进行回调

接下来我们在RNBridgeModule.m文件中添加如下的方法:

?
1
2
3
4
5
6
//RN传参数调用原生OC,并且返回数据给RN  通过CallBack
RCT_EXPORT_METHOD(RNInvokeOCCallBack:(NSDictionary *)dictionary callback:(RCTResponseSenderBlock)callback){
   NSLog(@"接收到RN传过来的数据为:%@",dictionary);
   NSArray *events = [[NSArray alloc] initWithObjects:@"张三",@"李四", nil];
   callback(@[[NSNull null], events]);
}

根据之前的文章讲过,如果原生的方法要被JavaScript进行访问,那么该方法需要使用RCT_EXPORT_METHOD()宏定义进行声明。该声明的RNInvokeOCCallBack方法有两个参数:第一个参数代表从JavaScript传过来的数据,第二个参数是回调方法,通过该回调方法把原生信息发送到JavaScript中。其中上面的callback方法中传入一个参数数组,其实该数组的第一个参数为一个NSError对象,如果没有错误返回null,其余的数据作为该方法的返回值回调给JavaScritp。

最后我们需要在JavaScript文件中进行定义导出在原生封装的模块,然后调用封装方法访问即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var { NativeModules } = require('react-native');
var RNBridgeModule=NativeModules.RNBridgeModule;
 
RNBridgeModule.RNInvokeOCCallBack(
            {'name':'jiangqq','description':'http://www.lcode.org'},
            (error,events)=>{
                if(error){
                  console.error(error);
                }else{
                  this.setState({events:events});
                }
          }
        )
}

3.2.使用Promise进行回调

上面讲解过了JavaScript调用iOS原生方法,但是数据回调是通过Callback的,这边我在讲解一下使用Promise进行回调的方法。来我们在

RNBridgetModule的实现类添加如下的方法代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
//RN传参数调用原生OC,并且返回数据给RN  通过Promise
RCT_EXPORT_METHOD(RNInvokeOCPromise:(NSDictionary *)dictionary resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject){
   NSLog(@"接收到RN传过来的数据为:%@",dictionary);
   NSString *value=[dictionary objectForKey:@"name"];
   if([value isEqualToString:@"jiangqq"]){
     resolve(@"回调成功啦,Promise...");
   }else{
     NSError *error=[NSError errorWithDomain:@"传入的name不符合要求,回调失败啦,Promise..." code:100 userInfo:nil];
     reject(@"100",@"传入的name不符合要求,回调失败啦,Promise...",error);
   }
}

这边定义了RNInvokeOCPromise方法,该会有三个参数:

  • dictionary:JavaScript传入的数据
  • resolve:成功,回调数据
  • reject:失败,回调数据

其中resove方法传入具体的成功信息即可,但是reject方法必须传入三个参数分别为,错误代码code ,错误信息message以及NSError对象。最终看一下JavaScript中的调用方式:

?
1
2
3
4
5
6
7
8
9
10
11
12
var { NativeModules } = require('react-native');
var RNBridgeModule=NativeModules.RNBridgeModule;
 
//获取Promise对象处理
  async _updateEvents(){
    try{
        var events=await RNBridgeModule.RNInvokeOCPromise({'name':'jiangqqlmj'});
        this.setState({events});
    }catch(e){
        this.setState({events:e.message});
    }
  }
(四).iOS原生访问调用React Native

如果我们需要从iOS原生方法发送数据到JavaScript中,那么可以使用eventDispatcher。首先我们需要在RCTBridgeModule实现中中引入:

?
1
2
3
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
@synthesize bridge = _bridge;

接下来通过iOS OC原生代码进行访问JavaScript了

?
1
self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"成功"}];

看上面的sendAppEventWithName方法包含二个参数:

  • 第一个参数:EventReminder自定义一个事件名称
  • 第二个参数:具体传入JavaScrtipt的数据信息

下面来看一下该调用的方法的具体代码:

?
1
2
3
4
5
6
7
8
9
//OC调用RN
RCT_EXPORT_METHOD(VCOpenRN:(NSDictionary *)dictionary){
  NSString *value=[dictionary objectForKey:@"name"];
  if([value isEqualToString:@"jiangqq"]){
    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"成功"}];
  }else{
    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"输入的name不是jiangqq"}];
  }
}

最后在JavaScript端进行调用的方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { NativeAppEventEmitter } from 'react-native';
 
componentDidMount(){
    console.log('开始订阅通知...');
    subscription = NativeAppEventEmitter.addListener(
         'EventReminder',
          (reminder) => {
            let errorCode=reminder.errorCode;
            if(errorCode===0){
               this.setState({msg:reminder.name});
            }else{
               this.setState({msg:reminder.msg});
            }
 
          }
         );
  }
  componentWillUnmount(){
     subscription.remove();
  }

如上的代码,首先通过导入NativeAppEventEmitter模块,使用该模块在JavaScript代码中进行注册订阅相应的事件。然后我们在生命周期方法componentDidMount()方法中使用addListener()方法进行添加订阅事件。有订阅当然有取消订阅,所以我们在componentWillUnmount()方法中使用remove()方法进行取消即可。

(五).实战实例

上面我们已经把JavaScript和iOS原生代码的相互访问调用以及原生界面打开RN界面都初步的讲解了一遍,下面我们来通过具体实例来演示一下效果(这边也只是贴出来主要代码,详细代码大家可以移步:https://github.com/jiangqqlmj/hunheDemo):

5.1.原生封装模块RCTBridgeModule.m代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//
//  RNBridgeModule.m
//  hunheDemo
//
//  Created by 江清清 on 16/6/5.
//  Copyright © 2016年 Facebook. All rights reserved.
//
#import "RNBridgeModule.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
 
@implementation RNBridgeModule
 
@synthesize bridge = _bridge;
 
RCT_EXPORT_MODULE(RNBridgeModule)
 
//RN传参数调用原生OC,并且返回数据给RN  通过CallBack
RCT_EXPORT_METHOD(RNInvokeOCCallBack:(NSDictionary *)dictionary callback:(RCTResponseSenderBlock)callback){
   NSLog(@"接收到RN传过来的数据为:%@",dictionary);
   NSArray *events = [[NSArray alloc] initWithObjects:@"张三",@"李四", nil];
   callback(@[[NSNull null], events]);
}
//RN传参数调用原生OC,并且返回数据给RN  通过Promise
RCT_EXPORT_METHOD(RNInvokeOCPromise:(NSDictionary *)dictionary resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject){
   NSLog(@"接收到RN传过来的数据为:%@",dictionary);
   NSString *value=[dictionary objectForKey:@"name"];
   if([value isEqualToString:@"jiangqq"]){
     resolve(@"回调成功啦,Promise...");
   }else{
     NSError *error=[NSError errorWithDomain:@"传入的name不符合要求,回调失败啦,Promise..." code:100 userInfo:nil];
     reject(@"100",@"传入的name不符合要求,回调失败啦,Promise...",error);
   }
}
//RN跳转原生界面
//RCT_EXPORT_METHOD(RNOpenVC:(NSString *)msg){
//   NSLog(@"RN传入原生界面的数据为:%@",msg);
//  [[NSNotificationCenter defaultCenter]postNotificationName:@"RNOpenVC" object:nil];
//}
 
//OC调用RN
RCT_EXPORT_METHOD(VCOpenRN:(NSDictionary *)dictionary){
  NSString *value=[dictionary objectForKey:@"name"];
  if([value isEqualToString:@"jiangqq"]){
    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"成功"}];
  }else{
    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"输入的name不是jiangqq"}];
  }
}
 
@end

5.2.前端index.ios.js代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
 * Sample React Native App
 * @flow
 */
 
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TouchableHighlight
} from 'react-native';
var { NativeModules } = require('react-native');
var RNBridgeModule=NativeModules.RNBridgeModule;
import { NativeAppEventEmitter } from 'react-native';
var subscription; //订阅者
class CustomButton extends Component {
  render() {
    return (
      <TouchableHighlight
        style={styles.button}
        underlayColor="#a5a5a5"
        onPress={this.props.onPress}>
        <Text style={styles.buttonText}>{this.props.text}</Text>
      </TouchableHighlight>
    );
  }
}
class hunheDemo extends Component {
  constructor(props){
    super(props);
    this.state={
        events:'',
        msg:'',
    }
  }
  //获取Promise对象处理
  async _updateEvents(){
    try{
        var events=await RNBridgeModule.RNInvokeOCPromise({'name':'jiangqqlmj'});
        this.setState({events});
    }catch(e){
        this.setState({events:e.message});
    }
  }
  componentDidMount(){
    console.log('开始订阅通知...');
    subscription = NativeAppEventEmitter.addListener(
         'EventReminder',
          (reminder) => {
            let errorCode=reminder.errorCode;
            if(errorCode===0){
               this.setState({msg:reminder.name});
            }else{
               this.setState({msg:reminder.msg});
            }
 
          }
         );
  }
  componentWillUnmount(){
     subscription.remove();
  }
 
  render() {
    return (
      <View style={{marginTop:20}}>
        <Text style={styles.welcome}>
           混合与RN,iOS通信实例讲解
        </Text>
        <Text style={{margin:20}}>
           来自:江清清的技术专栏(http://www.lcode.org)
        </Text>
        <Text style={{margin:5}}>'返回数据为:'+{this.state.events}</Text>
        <CustomButton text='RN调用iOS原生方法_CallBack回调'
          onPress={()=>{RNBridgeModule.RNInvokeOCCallBack(
            {'name':'jiangqq','description':'http://www.lcode.org'},
            (error,events)=>{
                if(error){
                  console.error(error);
                }else{
                  this.setState({events:events});
                }
          })}}
        />
        <CustomButton text='RN调用iOS原生方法_Promise回调'
           onPress={()=>this._updateEvents()}
        />
        <Text style={{margin:20}}>
          '返回数据为:'+{this.state.msg}
        </Text>
        <CustomButton text='iOS调用访问React Native'
            onPress={()=>RNBridgeModule.VCOpenRN({'name':'jiangqqlmj'})}
        />
 
      </View>
    );
  }
}
 
const styles = StyleSheet.create({
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  button: {
    margin:5,
    backgroundColor: 'white',
    padding: 10,
    borderWidth: 1,
    borderColor: '#facece',
  },
});
 
AppRegistry.registerComponent('hunheDemo', () => hunheDemo);

5.3.运行效果图

(六)最后总结

今天我们主要学习一下React Native iOS开发中,RN和原生iOS混合开发以及数据相互传输详解。

刚创建的React Native交流6群:426762904,欢迎各位大牛,React Native技术爱好者加入交流!同时博客右侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

本文章实例项目地址:https://github.com/jiangqqlmj/hunheDemo

尊重原创,未经授权不得转载:From Sky丶清(http://www.lcode.org/) 侵权必究!

关注我的订阅号(codedev123),每天分享移动开发技术(Android/IOS),项目管理以及博客文章!(欢迎关注,第一时间推送精彩文章)

关注我的微博,可以获得更多精彩内容