React Native开发小贴士

1. 屏幕适配

RN布局使用的单位是dp,而开发人员从设计稿最方便获取的是px,所以需要一个工具类把px转成dp,下面以宽度为375px的设计稿为例:

const deviceWidthDp = Dimensions.get('window').width;
const uiWidthPx = 375;
export default function pxtodp(uiElementPx) {
    return Platform.OS === 'ios' ? uiElementPx *  deviceWidthDp / uiWidthPx : Math.floor(uiElementPx *  deviceWidthDp / uiWidthPx)
}

在bug修复阶段,发现一个常见的bug,多组件传值时,出现了多次的p2dp嵌套,导致了值被转换多次,不符预期,所以写组件的时,应该规定好是最底层使用p2dp,还是传入的参数使用p2dp。

2. 样式管理

  • RN的样式可以是数组,类似css中定义多个class;
style={[styles.A,styles.B]}
  • RN的样式没有继承嵌套这类的功能,为了方便、高效使用样式,我们使用了一个样式的工具类,写入常用的样式、样式组合,便于页面调用。但每个页面调用都要引入工具类太麻烦,考虑注册到全局变量,这时候发现了一个问题,RN的global(全局变量)只能作用于Component,在StyleSheet无法识别,难道是根据某种上下文关系存在的?最后找到一个解决方案是我们写一个函数,在函数内是就能访问全局变量,然后把StyleSheet在函数中return出来,代码片段像这样:
const styles = () => {
  const {
    paddingLarge, paddingSmall, paddingMedium,
    fontSizeMedium, fontSizeSmall, fontWeightLight
  } = theme

  return StyleSheet.create({
    wrapper: {
      height: px2dp(106),
      marginHorizontal: paddingLarge,

3. 平台差异

使用react-native的Platform库来控制android和ios的差异

Platform.OS === 'ios' ? doios : doandroid

新版api还可以这么写:

const instructions = Platform.select({
  ios: 'Press Cmd+R to reload,\n' +
    'Cmd+D or shake for dev menu',
  android: 'Double tap R on your keyboard to reload,\n' +
    'Shake or press menu button for dev menu',
});

另外如何需要根据平台引入不同的组件,例如:

BigButton.ios.js
BigButton.android.js

你可以直接:

const BigButton = require('./BigButton');

React Native 会自动识别。

4. 点击

RN上除了Text组件(自带onPress方法),其他组件默认是不支持点击事件。所以 RN 中提供了几个直接处理响应事件的组件,基本上能够满大部分的点击处理需求TouchableHighlight, TouchableNativeFeedback, TouchableOpacityTouchableWithoutFeedback。因为这几个组件的功能和使用方法基本类似,只是 Touch 的反馈效果不一样,所以根据需求选用合适的方法使用即可。

另外,如果在Touchable中onPress执行了一个setState的操作,这个操作需要大量计算工作并且导致了掉帧,这时候可以将操作封装到requestAnimationFrame中:

handleOnPress() {
// 谨记在使用requestAnimationFrame、setTimeout以及setInterval时
// 要使用TimerMixin(其作用是在组件unmount时,清除所有定时器)
this.requestAnimationFrame(() => {
this.doExpensiveAction();
});
}

5. 手势识别

RN 提供了内置的手势识别库PanResponder,我们只需要创建一个实例,然后搭载在任意的区域,就能监听到这块区域的手势变化,代码片段如下:

componentWillMount: function() {
    this._panResponder = PanResponder.create({
      onStartShouldSetPanResponder: (evt, gestureState) => true,
      onMoveShouldSetPanResponder: (evt, gestureState) => true,
      onPanResponderGrant: this._handlePanResponderGrant,
      onPanResponderMove: this._handlePanResponderMove,
      onPanResponderRelease: this._handlePanResponderEnd,
      onPanResponderTerminate: this._handlePanResponderEnd,
    });
}

<View  
  {...this._panResponder.panHandlers}
/>

但这个手势库PanResponder有个bug,会blockTouchableWithoutFeedback/Highlight等的点击操作,解决方案是:

onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
    return Math.abs(gestureState.dx) > 5;
},

具体可以看这个issue

6. 文字行数控制

RN提供了numberOfLines方法实现行数控制,以及溢出部分的处理,同css的text-overflow

7. 样式表

RN的布局主要使用flex,而且是阉割版的flex,样式表大致只有如下属性:

"alignItems",
"alignSelf",
"backfaceVisibility",
"backgroundColor",
"borderBottomColor",
"borderBottomLeftRadius",
"borderBottomRightRadius",
"borderBottomWidth",
"borderColor",
"borderLeftColor",
"borderLeftWidth",
"borderRadius",
"borderRightColor",
"borderRightWidth",
"borderStyle",
"borderTopColor",
"borderTopLeftRadius",
"borderTopRightRadius",
"borderTopWidth",
"borderWidth",
"bottom",
"color",
"flex",
"flexDirection",
"flexWrap",
"fontFamily",
"fontSize",
"fontStyle",
"fontWeight",
"height",
"justifyContent",
"left",
"letterSpacing",
"lineHeight",
"margin",
"marginBottom",
"marginHorizontal",
"marginLeft",
"marginRight",
"marginTop",
"marginVertical",
"opacity",
"overflow",
"padding",
"paddingBottom",
"paddingHorizontal",
"paddingLeft",
"paddingRight",
"paddingTop",
"paddingVertical",
"position",
"resizeMode",
"right",
"rotation",
"scaleX",
"scaleY",
"shadowColor",
"shadowOffset",
"shadowOpacity",
"shadowRadius",
"textAlign",
"textDecorationColor",
"textDecorationLine",
"textDecorationStyle",
"tintColor",
"top",
"transform",
"transformMatrix",
"translateX",
"translateY",
"width",
"writingDirection"

8. 警告信息

在开发过程中,如果我们需要在界面中打印出信息,可以借助console.warn打印出警告信息,而console.log的信息需要开启debug模式,在控制台可见。
另外最常见的一个警告信息是提示你加上key属性,当我们遍历输出组件时,组件一定记得加上key属性,这样做能提高虚拟DOM Diff的效率。

9. 解决缓慢的导航器(Navigator)切换

Navigator的动画是由JavaScript线程所控制的。想象一下“从右边推入”这个场景的切换:每一帧中,新的场景从右向左移动,从屏幕右边缘开始(不妨认为是320单位宽的的x轴偏移),最终移动到x轴偏移为0的屏幕位置。切换过程中的每一帧,JavaScript线程都需要发送一个新的x轴偏移量给主线程。如果JavaScript线程卡住了,它就无法处理这项事情,因而这一帧就无法更新,动画就被卡住了。

长远的解决方法,其中一部分是要允许基于JavaScript的动画从主线程分离。同样是上面的例子,我们可以在切换动画开始的时候计算出一个列表,其中包含所有的新的场景需要的x轴偏移量,然后一次发送到主线程以某种优化的方式执行。由于JavaScript线程已经从更新x轴偏移量给主线程这个职责中解脱了出来,因此JavaScript线程中的掉帧就不是什么大问题了 —— 用户将基本上不会意识到这个问题,因为用户的注意力会被流畅的切换动作所吸引。

不幸的是,这个方案还没有被实现。所以当前的解决方案是,在动画的进行过程中,利用InteractionManager来选择性的渲染新场景所需的最小限度的内容。

InteractionManager.runAfterInteractions的参数中包含一个回调,这个回调会在navigator切换动画结束的时候被触发。

componentDidMount() {
    InteractionManager.runAfterInteractions(() => {
      this.setState({renderPlaceholderOnly: false});
    });
  }

10. 全局变量

RN可以通过global来设置全局变量,例如我们要把本地存储的方法挂载到全局:

global.storage = storage

之后直接使用storage即可。

11. 调试

RN在开发菜单里提供了Debug JS Remotely的选项,点击后会打开chrome,可以查看日志,断点调试。
另外还可以安装react-devtools进行样式调试。
更详细的调试文档,可以看这里

12. WebView

RN自带了WebView的支持,我们可以通过简单的封装,让它更易用,另外它除了支持url,还支持自定义的html。

13. 链接原生库

有一些库基于一些原生代码实现,你必须把这些文件添加到你的应用,否则应用会在你使用这些库的时候产生报错。

我们无需手动添加,通过react-native link命令即可完成链接原生库。

14. Component命名

react声明组件时,第一个字母必须大写。

15. 字体引入

IOS上要使用自定义的字体,必须把字体文件拖到对应的Xcode工程里面,勾选Add to targetsCreate groups,修改Info.plist文件,添加属性Fonts provided by application;
安卓上要使用自定义的字体,必须要把字体文件放在[project root]/android/app/src/main/assets/fonts/目录下才能生效

16. icon解决方案

我们使用iconfont,然后进行了简单的封装,详细见

17. 使用ListView的正确姿势

我们在一次使用ListView过程中,发现state不会改变,在GitHub上找到了同样问题的issues:this.state does't work at listView's renderRow。进而获得了一些使用ListView的正确姿势:适合动态列表数据,固化数据尽量不用,renderRow里尽量传数据,避免state判断,如需state,应该付给参数传入。

18. ScrollView

我们在使用ScrollViewonScroll方法的时候,有时会发现获取的值和我们的预期不一致,是因为ScrollView默认每帧最多调用一次此回调函数,如果要增大调用的频率,可以用scrollEventThrottle属性来控制。

19. 阴影

iOS上的阴影使用以下的属性:

shadowColor Sets the drop shadow color
shadowOffset {width: number, height: number}Sets the drop shadow offset
shadowOpacity numberSets the drop shadow opacity (multiplied by the color's alpha component)
shadowRadius numberSets the drop shadow blur radius

但注意如果给Image组件添加阴影,不能把样式写在Image的style,而需要包裹一层View来添加阴影样式。
Android上则不支持shadow*的样式,只有elevation仰角的属性来替代,但效果不太好,如果需要实现一致的效果,需要自己实现或者引入相关的库。

20. FlatList

FlatList号称是ListView的升级版,会有更好的体验、更高的效率,但目前这个组件还不稳定。使用过程有很多问题,例如首次加载会触发两次onEndReached、必须设置height属性,不然onEndReached无法触发、下拉到底仍可下拉,并出现大片白屏等。

注:官方在0.48版本开始废弃ListView,推荐使用FlatListSectionList,看来应该比较稳定了。

21. ref

任何组件都用一个ref的属性,ref是组件实例的引用,通过复制给this变量,可以在任意位置操作组件。

22. PureComponent

props或者state改变的时候,会执行shouldComponentUpdate方法来判断是否需要重新render组建,我们平时在做页面的性能优化的时候,往往也是通过这一步来判断的。Component默认的shouldComponentUpdate返回的是true,如下:

shouldComponentUpdate(nextProps, nextState) {
  return true;
}

PureComponentshouldComponentUpdate是这样的:

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}

相当于PureComponent帮我们判断如果props或者state没有改变的时候,就不重复render,这对于纯展示组件,能节省不少比较的工作。

23. babelHelpers.objectDestructuringEmpty is not a function

在某些机子上遇到过一个如题的报错,具体看issue
原因是使用了如下的语法:

const {} = result 

开发时尽量规范语法,避免写些无意义的语法。

24. IOS模拟器卡顿

别当心,很可能是你按到了快捷键,打开了慢动画的选项,关掉它就行了。


25. 快捷方式

IOS唤起调试菜单是⌘ + D,刷新是⌘ + R
Android唤起调试菜单是⌘ + M ,刷新是R+ R
在真机上可以通过摇一摇唤起调试菜单。

26. LayoutAnimation

Animated的接口一般会在JavaScript线程中计算出所需要的每一个关键帧,而LayoutAnimation则利用了Core Animation,使动画不会被JS线程和主线程的掉帧所影响。

注意:LayoutAnimation只工作在“一次性”的动画上("静态"动画) -- 如果动画可能会被中途取消,你还是需要使用Animated

27.本地存储的使用

这个问题琢磨了一段时间,还没有找到我想要的答案。情景大致是这样,一次访问某页面,通过AsyncStorage保存了数据,第二次进入页面肯定希望render中直接用AsyncStorage中的本地数据,无需二次render。但是AsyncStorage是个异步函数,所以你即便在componentWillMount调用,还是需要在render后才能拿到数据,所以就会出现二次render,即便componentWillMount中用await也无效,认真看了遍官方生命周期的文档,但并没有什么收获。目前的解决方案是用一个标志位控制,标志位为false时出loading,只有当拿到数据标志位为true时才切真正的render,但这种方案其实还是执行了两次render,不过意外的是效果不错,看不出有闪动,甚至看不出有loading过程。但如果你把关于本地存储的一系列判断逻辑是写在InteractionManager.runAfterInteractions中,就会明显的看到loading,打断点看了下,发现即便是两次render,都发生在页面过场前,也就是屏幕还在上一页面的时候就在render,而写在InteractionManager.runAfterInteractions里,正是在执行过场或者过场执行完时发现,这里面的 state变化反应到render中就会在屏幕中被看到。当然这个问题我还是想继续关注下去,react-native也有不少类似的issue,最终还是希望能找到只需要一次render的办法。
注:思路1(Redux是无视生命周期的)

28.从原生页面如何跳转到指定RN页面

这里用到方法就是发送事件到JavaScript,然后根据获取的参数,跳转相应的路由。
原生模块可以在没有被调用的情况下往JavaScript发送事件通知。最简单的办法就是通过RCTDeviceEventEmitter,这可以通过ReactContext来获得对应的引用,像这样:

    @ReactMethod
    public void goPage(int pageid) {
        System.out.println("########"+pageid+"########");
        // failedCallback.invoke();
        WritableMap params = Arguments.createMap();
        params.putInt("name", pageid);
        reactApplicationContextAction
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit("test", params);
    }

Javascript通过DeviceEventEmitter模块来监听事件,获取跳转信息,跳转至相应路由:

import {DeviceEventEmitter} from 'react-native'
componentWillMount(){
    DeviceEventEmitter.addListener("test", (result) => {
            let mainComponent = require(result.name);
            this.setState({
                content:mainComponent,
                showModule:true
            })
     })
}

render(){
    if(this.state.content){
        route.push(this.state.content)
        return null
    }else{
        return null
    }
}
    

29.字体背景色

字体的背景色是会继承它父级的backgroundColor,通常我们没有在意。但当你如果需要在字体上叠加一层蒙版也好、渐变也好,它们的颜色又恰好与你之前的背景色不一致,你就会发现字体的背景色凸显出来了,这时需要把字体的backgroundColor设置为transparent,这样才不会影响盖在它上面的层,当然遇到这样的问题还可能和你布局的先后顺序有关,通常使用absolute应该排在后面,避免被后面的元素覆盖,zIndex好像是只作用于同样是absolute定义,absoluteflex之间无法使用zIndex

30.扩展性

使用原生方法(NativeModules)

  • IOS

想要创建一个iOS模块,只需要创建一个接口,实现RCTBridgeModule协议,然后把你想在Javascript中使用的任何方法用RCT_EXPORT_METHOD包装。最后,再用RCT_EXPORT_MODULE导出整个模块即可。

// Objective-C

#import "RCTBridgeModule.h"

@interface MyCustomModule : NSObject <RCTBridgeModule>
@end

@implementation MyCustomModule

RCT_EXPORT_MODULE();

// Available as NativeModules.MyCustomModule.processString
RCT_EXPORT_METHOD(processString:(NSString *)input callback:(RCTResponseSenderBlock)callback)
{
  callback(@[[input stringByReplacingOccurrencesOfString:@"Goodbye" withString:@"Hello"]]);
}
@end
// JavaScript

import React, {
  Component,
} from 'react';
import {
  NativeModules,
  Text
} from 'react-native';

class Message extends Component {
  constructor(props) {
    super(props);
    this.state = { text: 'Goodbye World.' };
  }
  componentDidMount() {
    NativeModules.MyCustomModule.processString(this.state.text, (text) => {
      this.setState({text});
    });
  }
  render() {
    return (
      <Text>{this.state.text}</Text>
    );
  }
}
  • Android

同样的,Android也支持自定义扩展。仅仅是方法略有差异。
创建一个基础的安卓模块,需要先创建一个继承自ReactContentBaseJavaModule的类,然后使用@ReactMethod标注(Annotation)来标记那些你希望通过Javascript来访问的方法。最后,需要在ReactPackage中注册这个模块。

// Java

public class MyCustomModule extends ReactContextBaseJavaModule {

// Available as NativeModules.MyCustomModule.processString
  @ReactMethod
  public void processString(String input, Callback callback) {
    callback.invoke(input.replace("Goodbye", "Hello"));
  }
}
// JavaScript

import React, {
  Component,
} from 'react';
import {
  NativeModules,
  Text
} from 'react-native';
class Message extends Component {
  constructor(props) {
    super(props);
    this.state = { text: 'Goodbye World.' };
  },
  componentDidMount() {
    NativeModules.MyCustomModule.processString(this.state.text, (text) => {
      this.setState({text});
    });
  }
  render() {
    return (
      <Text>{this.state.text}</Text>
    );
  }
}

使用原生页面(requireNativeComponent)

  • IOS

若想自定义iOS View,可以这样来做:首先继承RCTViewManager类,然后实现一个-(UIView *)view方法,并且使用RCT_EXPORT_VIEW_PROPERTY宏导出属性。最后用一个Javascript文件连接并进行包装。

// Objective-C

#import "RCTViewManager.h"

@interface MyCustomViewManager : RCTViewManager
@end

@implementation MyCustomViewManager

RCT_EXPORT_MODULE()

- (UIView *)view
{
  return [[MyCustomView alloc] init];
}

RCT_EXPORT_VIEW_PROPERTY(myCustomProperty, NSString);
@end
// JavaScript

import React, { 
  Component,
} from 'react';
import PropTypes from 'prop-types';
import { requireNativeComponent } from 'react-native';

var NativeMyCustomView = requireNativeComponent('MyCustomView', MyCustomView);

export default class MyCustomView extends Component {
  static propTypes = {
    myCustomProperty: PropTypes.oneOf(['a', 'b']),
  };
  render() {
    return <NativeMyCustomView {...this.props} />;
  }
}
  • Android

创建自定义的Android View,首先定义一个继承自SimpleViewManager的类,并实现createViewInstancegetName方法,然后使用@ReactProp标注导出属性,最后用一个Javascript文件连接并进行包装。

// Java

public class MyCustomViewManager extends SimpleViewManager<MyCustomView> {
  @Override
  public String getName() {
    return "MyCustomView";
  }

  @Override
  protected MyCustomView createViewInstance(ThemedReactContext reactContext) {
    return new MyCustomView(reactContext);
  }

  @ReactProp(name = "myCustomProperty")
  public void setMyCustomProperty(MyCustomView view, String value) {
    view.setMyCustomProperty(value);
  }
}
// JavaScript

import React, {
  Component,
  requireNativeComponent 
} from 'react-native';

var NativeMyCustomView = requireNativeComponent('MyCustomView', MyCustomView);

export default class MyCustomView extends Component {
  static propTypes = {
    myCustomProperty: React.PropTypes.oneOf(['a', 'b']),
  };
  render() {
    return <NativeMyCustomView {...this.props} />;
  }
}

更多(使用原生UI、同个页面RN与Native的相互嵌套等)

更多和原生通信的内容可以看官网文档:英文中文

31.IOS真机打包

如何没有IOS开发者账号,一个项目只允许最多在三台设备上打包,而且过期时间只有7天,另外无法移除打包过的机子的mac地址,意思就是我在A机子装过,就用掉一个名额,没法把这个名额让出来了。这样导致我们在铺开测试、给大家体验时遇到了瓶颈。这时候最好的方案是有一个企业账号,可以打出一个企业包,在任何机子安装,如果只有开发者账号,那也只能在100台设备安装,开启和关闭权限都需要到开发者网站操作,收回权限还需要给Apple发邮件。打完包,我推荐用fir平台托管应用,只要把生成的页面或者二维码发给大家即可,方便、快捷,另外还支持权限、密码的设置,实名制后每天有一百次的下载额度,其实也是足够用了。

32.如何实现回退后刷新上个页面

刷新上个页面,说白了就是传参。目前用的navigator,只有push能传参,pop并没有,这样如何做到页面回退能让上一个页面感知呢?我尝试了几个办法:

  • Redux
    通过reduxstore,简单粗暴,没啥好说
  • DeviceEventEmitter
    第一个页面监听,回退的时候触发,其实就是个简单的观察者模式,代码大致如下:
//A页面
import {
     AppRegistry,
     StyleSheet,
     Text,
     View,
     DeviceEventEmitter
 } form 'react-native';
componentDidMount() {
     this.subscription = DeviceEventEmitter.addListener('userNameDidChange',(userName) =>{
          console.warn(userName);
     })
}
componentWillUnmount() {
    // 移除
    this.subscription.remove();
}
//B页面,在回退前
DeviceEventEmitter.emit('userNameDidChange', '通知来了');
  • callback
    在A界面跳到B界面时,带上回调参数,如:
this.props.navigator.push({‘id’:’b’,’callback’:this.refreshAAvatar}

然后在你回退前执行callback即可

33. 在初始化bundle时如何传参

在注册bundle时传参有什么用呢?可以实现跳转到特定页面。
来看看IOS和Android分别是怎么实现:

//IOS initialProps就是给RN的参数
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"MyAwesomeApp"
                                               initialProperties:initialProps
                                                   launchOptions:launchOptions];
//Android
Bundle initialProps = new Bundle();
initialProps.putString("myKey", "myValue");

mReactRootView.startReactApplication(mReactInstanceManager, "MyAwesomeApp", initialProps);

34. 使用React Navigation实现App唤醒功能

详见官网文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容