RN项目第二节 -- 首页实现

一、微组件的封装

每个页面的导航都会有不同的样式或者图片,为了实现代码的复用性,可以将导航统一封装成一个微小组件。
封装的Item需要有可点击事件,需要显示文字和图片。

import React, { PureComponent } from 'react'
import { View, Text, StyleSheet, TouchableOpacity, Image } from 'react-native'

export default class NavigationItem extends PureComponent {
    render() {
    // 图片和文字时从外界传入的 所以用到props
        let icon = this.props.icon &&
            <Image style={[styles.icon, this.props.iconStyle]} source={this.props.icon} />

        let title = this.props.title &&
            <Text style={[styles.title, this.props.titleStyle]}>{this.props.title}</Text>
        return (
            <TouchableOpacity style={styles.container} onPress={this.props.onPress}>
                {icon}
                {title}
            </TouchableOpacity>
        );
    }
}

// 样式
const styles = StyleSheet.create({
    container: {
        flex:1,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
    },
    icon: {
        width: 27,
        height: 27,
        margin: 8,
    },
    title: {
        fontSize: 15,
        color: '#333333',
        margin: 8,
    }
});

再来分析每个页面都有不同的字体出现,比如说标题、段落、小标题。所以也可以将文字封装成单独的组件。

import React from 'react';
import ReactNative, { StyleSheet, Dimensions, Text ,ReactElement} from 'react-native'
import color from './color'

export function Heading1({style, ...props}) {
    return <Text style={[styles.h1, style]} {...props} />
}

export function Heading2({style, ...props}) {
    return <Text style={[styles.h2, style]} {...props} />
}

export function Paragraph({style, ...props}) {
    return <Text style={[styles.p, style]} {...props} />
}

//设置样式
const styles = StyleSheet.create({
    h1: {
        fontSize: 15,
        fontWeight: 'bold',
        color: '#222222',
    },
    h2: {
        fontSize: 14,
        color: '#222222',
    },
    p: {
        fontSize: 13,
        color: '#777777',
    },
});

将整个页面分为两部分,页面最下方是一个列表,可以当成是iOS中的tableView,而页面上方可以看做是头部的View,这个View里面存放了各种模块。模块之间会有分割线,可以将分割线也封装成组件。

import React, { PureComponent } from 'react'
import { View, Text, StyleSheet } from 'react-native'

import color from './color'

export default class SpacingView extends PureComponent {
    render() {
        return (
            <View style={styles.container}>
            </View>
        );
    }
}

// define your styles
const styles = StyleSheet.create({
    container: {
        height: 14,
        backgroundColor: color.background,
    },
});

在设置样式的时候,会用到全屏,因此可以把全屏幕的尺寸也封装起来。

import { Dimensions, Platform, PixelRatio } from 'react-native'

export default {
    width: Dimensions.get('window').width,
    height: Dimensions.get('window').height,
    onePixel: 1 / PixelRatio.get(),   //在iphone4+中代表一个像素点
    statusBarHeight: (Platform.OS === 'ios' ? 20 : 0)  //iOS平台状态栏默认为0,安卓平台默认为20
}

二、首页封装

1) 首页导航的实现
同样也是在navigationOptions调用箭头函数。返回标题、图片。当然左右两边可以返回已经封装好的NavigationItem属性。

static navigationOptions = ({ navigation }) => ({
        headerTitle: (
            <TouchableOpacity style={styles.searchBar}>
                <Image source={require('../../img/Home/search_icon.png')} style={styles.searchIcon} />
                <Paragraph>一点点</Paragraph>
            </TouchableOpacity>
        ),
        headerRight: (
            <NavigationItem
                icon={require('../../img/Home/icon_navigationItem_message_white.png')}
                onPress={() => {

                }}
            />
        ),
        headerLeft: (
            <NavigationItem
                title='福州'
                titleStyle={{ color: 'white' }}
                onPress={() => {

                }}
            />
        ),
        headerStyle: { backgroundColor: color.theme },
    })

2)列表的实现

首页要实现列表功能,采用组件FlatList。

  • 引用必要的组件
import color from '../../widget/color'
import NavigationItem from '../../widget/NavigationItem'
import SpacingView from '../../widget/SpacingView'
import screen from '../../common/screen'
import api from '../../api'
import { Heading1, Heading2, Paragraph } from '../../widget/Text'
  • 在render方法中返回FlatList并设置它的属性
render() {
        return (
            <View style={styles.container}>
                <FlatList
                    data={this.state.dataList}   //请求的数据
                    keyExtractor={this.keyExtractor} //设置每个item唯一的key
                    onRefresh={this.requestData}   //刷新的操作
                    refreshing={this.state.refreshing}   //刷新状态
                    ListHeaderComponent={this.renderHeader}  //头部页面展示
                    renderItem={this.renderCell}  //每个item
                />
            </View>
        );
    }
  • 存放的数据要在state中声明并指定属性。(类中)
state: {
        discounts: Array<Object>,
        dataList: Array<Object>,
        refreshing: boolean,
    }
  • 在构造函数中设置初始值,并绑定要实现的方法
constructor(props) {
        super(props)

        this.state = {
            discounts: [],
            dataList: [],
            refreshing: false,
        }

        { this.requestData = this.requestData.bind(this) }
        { this.renderCell = this.renderCell.bind(this) }
        { this.onCellSelected = this.onCellSelected.bind(this) }
        { this.keyExtractor = this.keyExtractor.bind(this) }
        { this.renderHeader = this.renderHeader.bind(this) }
        { this.onGridSelected = this.onGridSelected.bind(this) }
        { this.onMenuSelected = this.onMenuSelected.bind(this) }
    }
  1. 实现方法
  • keyExtractor设置每个item唯一的key
keyExtractor(item, index) {
        return item.id
    }
  • onRefresh对应的requestData方法做刷新的操作
    每次请求数据,都要进行刷新,所以刷新的状态要改为true。而且要刷新之后,要刷新折扣版块和列表版块的内容。为了代码简洁,将这两个功能封装成方法
requestData() {
        //每次请求数据 都要进行刷新
        this.setState({ refreshing: true })
        //调用折扣
        this.requestDiscount()
        //调用推荐方法
        this.requestRecommend()
    }
  • 实现折扣模块的方法

这里使用ES7的的异步线程语法。await表示紧跟在后面的表达式需要等待结果。也就是说当执行到awiat的时候,执行器将交给其他线程,等执行权返回再从暂停的地方往后执行。
这里做的是请求数据的操作,用fetch函数传入api得到全部的折扣数据结果。然后再转化为json数据,接着把json中的data赋值给discounts数组。

 async requestDiscount() {
        try {
            let response = await fetch(api.discount)
            let json = await response.json()
            this.setState({ discounts: json.data })
        } catch (error) {
            alert('错误信息:'+error)
        }
    }
  • 实现推荐列表的方法


上图是从API中的recommend的url解析出来的json数据。选取需要的数据。在代码中用fetch将数据解析成json格式,取出data集合中的数据传入箭头函数中,一一赋值给指定变量之后返回给数组dataList。如此一来,就可以用setState方法改变数组的数据。

async requestRecommend(){
        try{
            let response = await fetch(api.recommend)
            let json = await response.json()
            let dataList = json.data.map(
                (info)=>{
                    return{
                        id:info.id,
                        imageUrl:info.squareimgurl,
                        title:info.mname,
                        subtitle:`$[{info.range}]${info.title}`,
                        price:info.price
                    }
                }
            )
            this.setState({
                dataList:dataList,
                refreshing:false,
            })
        }catch (error){
            this.setState({
                refreshing:false
            })
        }
    }

为了不影响渲染效果,可以将请求数据的方法放在componentDidMount方法中

componentDidMount() {
        this.requestData()
    }
  • 处理列表

在iOS或者其他编程语言中,会采用各类框架来防止代码冗余。最常用的是MVC模式。在本项目中,为了返回列表,可以先将列表的UI封装起来。

import React,{Component} from 'react'
import {View,Text,StyleSheet,TouchableOpacity,Image} from 'react-native'
import {Heading1,Paragraph}from '../../widget/Text'
import screen from '../../common/screen'
import color from '../../widget/color'
export default class GroupPurchaseCell extends Component{
    render(){
        let {info} = this.props
        let imageUrl = info.imageUrl.replace('w.h','160.0')
        return(
            <TouchableOpacity style={style.container} onPress = {()=>this.props.onPress(info)}>
                <Image source={{uri:imageUrl}} style={styles.icon}/>
                <View style={styles.rightContainer}>
                    <Heading1>{info.title}</Heading1>
                    <View>
                    </View>
                    <Paragraph numberOfLines={0} style={{ marginTop: 8 }}>{info.subtitle}</Paragraph>
                    <View style={{ flex: 1, justifyContent: 'flex-end' }}>
                        <Heading1 style={styles.price}>{info.price}元</Heading1>
                    </View>

                </View>
            </TouchableOpacity>
        )
    }
}
const styles = StyleSheet.create({
    container: {
        flexDirection: 'row',
        padding: 10,
        borderBottomWidth: screen.onePixel,
        borderColor: color.border,
        backgroundColor: 'white',
    },
    icon: {
        width: 80,
        height: 80,
        borderRadius: 5,
    },
    rightContainer: {
        flex: 1,
        paddingLeft: 20,
        paddingRight: 10,
    },
    price: {
        color: color.theme
    }
});
  • 到首页中调用
    引入框架
import GroupPurchaseCell from '../GroupPurchase/GroupPurchaseCell'

将数据传给cell并调用

renderCell(info) {
        return (
            <GroupPurchaseCell
                info={info.item}
                onPress={this.onCellSelected}
            />
        )
    }

实现cell的onPress方法

onCellSelected(info) {
        //把状态栏的样式给成暗色
        StatusBar.setBarStyle('default', false)
        //跳转到详情页面,并把本页信息传递给详情页
        this.props.navigation.navigate('GroupPurchase', { info: info })
    }

当点击cell的时候,导航会跳转到详情页面,那么就要把要跳转页面的名称传入到navigate中。

  • 为了测试,先简单封装详情页
    建立一个.js文件GroupPurchaseScene,用来测试
import React, { Component } from 'react'
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ListView, Image, InteractionManager } from 'react-native'

export default class GroupPurchaseScene extends Component {
    render(){
    return(
        <View>
            <Text>
                详情页面
            </Text>>
        </View>
    )
}
}
const Styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:'pink'
    }
})
  • 测试
    在RootScene中引用详情页,并把它加入到导航当中
import GroupPurchaseScene from './scene/GroupPurchase/GroupPurchaseScene'

const Navigator = StackNavigator(
    {
        Tab: { screen: Tab },  //框架的页面
        // Web: { screen: WebScene }, //webview的页面
        GroupPurchase: { screen: GroupPurchaseScene },//详情页
    },

为了测试,先把首页的renderHeader()方法添加进去

renderHeader(){
        return(
            <View>
                <Text style={{fontSize:24}}>头部啊头部ddddddddddnihaoaha</Text>
            </View>
        )
    }

测试结果如下:


点击详情页也能跳转


  • 首页导航实现
static navigationOptions = ({ navigation }) => ({
        headerTitle: (
            <TouchableOpacity style={styles.searchBar}>
                <Image source={require('../../img/Home/search_icon.png')} style={styles.searchIcon} />
                <Paragraph>一点点</Paragraph>
            </TouchableOpacity>
        ),
        headerRight: (
            <NavigationItem
                icon={require('../../img/Home/icon_navigationItem_message_white.png')}
                onPress={() => {

                }}
            />
        ),
        headerLeft: (
            <NavigationItem
                title='福州'
                titleStyle={{ color: 'white' }}
                onPress={() => {

                }}
            />
        ),
        headerStyle: { backgroundColor: color.theme },
    })
  • 添加样式
searchBar: {
        width: screen.width * 0.7,
        height: 30,
        borderRadius: 19,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: 'white',
        alignSelf: 'center',
    },
searchIcon: {
        width: 20,
        height: 20,
        margin: 5,
    }
  • 封装头部

头部分为2个部分,一个是滚动的部分,另一个则是格子的部分。并且这两部分底部都有一个分割线。现在来封装这两个部分。
先封装最上方的部分,新建一个HomeMenuView.js。而这个部分又是由许多小的view组成的。所以将这些小的view也封装起来。新建一个文件HomeMenuItem。而这个类返回的只要是一个图片和文字即可。

import React, {Component } from 'react'
import { View, Text, StyleSheet, Image, TouchableOpacity } from 'react-native'
import { Heading2 } from '../../widget/Text'
import screen from '../../common/screen'
export default class HomeMenuItem extends Component{
    render(){
        return(
            <TouchableOpacity
                style={styles.container}
                onPress = {this.props.onPress}
            >
                <Image source={this.props.icon} resizeMode='contain' style={styles.icon}/>
                <Heading2>
                    {this.props.title}
                </Heading2>

            </TouchableOpacity>
        );
    }

}

const styles = StyleSheet.create({
    container: {
        justifyContent: 'center',
        alignItems: 'center',
        width: screen.width / 5,
        height: screen.width / 5,
    },
    icon: {
        width: screen.width / 9,
        height: screen.width / 9,
        margin: 5,
    }
});

现在来封装HomeMenuView

这个View的主体是一个ScrollView和一个PageControl,当然RN中是没有特定的页面控制器的,所以该PageControl是需要自己封装的。把它放入widget组件中。

现在考虑PageControl需要什么属性。属性可以用propTypes来规定,然而最近React组件已经把PropTypes组件移除了React库,所以需要引入prop-types。import PropTypes from 'prop-types';

numberofPage:首先是各个组件能够分为多少页,这个是必须要传入的。规定为number类型。可以用isRequired来约束。
currentPage:当前页用来控制亮点会出现在哪一页,number类型。
hidesForSinglePage:当页面为1时,是否隐藏控制器,bool类型。
pageIndicatorTintColor:控制器上点的颜色,string类型。
currentPageIndicatorTintColor:当前亮点的颜色,string类型。
indicatorSize:指示器的大小,规定为object类型,
indicatorStyle:指示器的样式,它是View规定的类型,所以规定为View.propTypes.style类型
currentIndicatorStyle:当前指示器的样式。同上
onPageIndicatorPress:点击指示器的处理时间,func类型。

引入头文件

import React, {Component } from 'react'
import { View, StyleSheet, TouchableWithoutFeedback } from 'react-native'
import assign from 'object-assign';
import PropTypes from 'prop-types';

在类中规定属性类型

 static propTypes = {
        numberOfPages: PropTypes.number.isRequired,
        currentPage: PropTypes.number,
        hidesForSinglePage: PropTypes.bool,
        pageIndicatorTintColor: PropTypes.string,
        currentPageIndicatorTintColor: PropTypes.string,
        indicatorSize: PropTypes.object,
        indicatorStyle: View.propTypes.style,
        currentIndicatorStyle: View.propTypes.style,
        onPageIndicatorPress: PropTypes.func
    }

给属性设置为默认值

static defaultProps = {
        numberOfPages: 0,
        currentPage: 0,
        hidesForSinglePage: false,
        pageIndicatorTintColor: 'gray',
        currentPageIndicatorTintColor: 'white',
        indicatorSize: { width: 8, height: 8 },
        indicatorStyle: {},
        currentIndicatorStyle: {},
        onPageIndicatorPress: function () { }
    }

实现方法

 onPageIndicatorPress(index) {
        this.props.onPageIndicatorPress(index);
    }

实现render方法

render() {
//解构赋值,取出所有的属性
        var { style, ...props } = this.props;
//给指示器设置默认的属性,备用
        var defaultStyle = {
            height: this.props.indicatorSize.height
        };
//设置每个小点的样式
        var indicatorItemStyle = {
            width: this.props.indicatorSize.width,
            height: this.props.indicatorSize.height,
            borderRadius: this.props.indicatorSize.height / 2,
            marginLeft: 5,
            marginRight: 5
        };
//指示器整体样式
        var indicatorStyle = assign({}, indicatorItemStyle, this.props.indicatorStyle, {
            backgroundColor: this.props.pageIndicatorTintColor
        });
//选中指示器的样式
        var currentIndicatorStyle = assign({}, indicatorItemStyle, this.props.currentIndicatorStyle, {
            backgroundColor: this.props.currentPageIndicatorTintColor
        });
//创建一个数组,将小点添加到pages里面。
        var pages = [];
        for (var i = 0; i < this.props.numberOfPages; i++) {
            pages.push(i);
        }
     //页面隐藏设置为真并且页面长度<=1时,返回null,否则返回一个个的小点。
        return (
            this.props.hidesForSinglePage && pages.length <= 1 ? null : <View style={[styles.container, defaultStyle, style]}>
            
 {/*从小点的集合中取出每个点,如果是当前的点就返回当前点样式,否则返回普通样式*/}
                {pages.map((el, i) => <TouchableWithoutFeedback key={i} onPress={this.onPageIndicatorPress.bind(this, i)}>
                        <View style={i == this.props.currentPage ? currentIndicatorStyle : indicatorStyle} />
                    </TouchableWithoutFeedback>
                )}
            </View>
        )
    }

现在可以做封装HomeMenuView的操作了
先把render函数要返回的东西梳理清楚

return (
 
  //scrollView里面要包含多个Item。将数组menuView添加到view里面
            <View style={styles.container}>
                <ScrollView contentContainerStyle={styles.contentContainer}
                    horizontal
                    showsHorizontalScrollIndicator={false}
                    pagingEnabled
                    onScroll={(e) => this.onScroll(e)}
                >
                    <View style={styles.menuContainer}>
                        {menuViews}
                    </View>
                </ScrollView>


                <PageControl
                    style={styles.pageControl}
                    numberOfPages={pageCount}
                    currentPage={this.state.currentPage}
                    hidesForSinglePage
                    pageIndicatorTintColor='gray'
                    currentPageIndicatorTintColor={color.theme}
                    indicatorSize={{ width: 8, height: 8 }}
                />
            </View>
        );

scrollview中包含的是menuView,也就是每个设置好样式的items。
在render方法的return方法之前,创建一个数组,该数组用来存放每个item。

render() {
        //取出属性中的menuinfos和onMenuSelected
        let { menuInfos, onMenuSelected } = this.props
        // 将属性传给每个HomeMenuitem
        let menuItems = menuInfos.map(
            (info, i) => (
                <HomeMenuItem
                    key={info.title}
                    title={info.title}
                    icon={info.icon}
                    onPress={() => {
                        onMenuSelected && onMenuSelected(i)
                    }} />
            )
        )
        //创建一个menuViews数组,用来表示每一页
        let menuViews = []
        //像上取整得到页数
        let pageCount = Math.ceil(menuItems.length / 10)

        for (let i = 0; i < pageCount; i++) {
            //slice() 方法可从已有的数组中返回选定的元素。start和end,表示一页放十个
            let items = menuItems.slice(i * 10, i * 10 + 10)
            // 一整页的
            let menuView = (
                <View style={styles.itemsView} key={i}>
                    {items}
                </View>
            )
            //加入到所有页面的数组
            menuViews.push(menuView)
        }
        //此处是省略的return 在上面已经写了
        }

上面return方法中要返回的PageControl要将currentPage传入进去,所以在MenuView应该先有一个状态机以便在用户滚动页面的时候实施修改。

 state: {
        currentPage: number
    }

    constructor(props) {
        super(props)

        this.state = {
            currentPage: 0
        }
    }

实现滚动的方法

onScroll(e) {
            //拿到x的偏移量
        let x = e.nativeEvent.contentOffset.x
              //用偏移量/宽度得到当前页数
        let currentPage = Math.round(x / screen.width)

        console.log('onScroll  ' + e.nativeEvent.contentOffset.x + '  page ' + currentPage + '  current ' + this.state.currentPage)
        if (this.state.currentPage != currentPage) {
                    //改变状态机

            this.setState({
                currentPage: currentPage
            })
        }
    }

设置样式

const styles = StyleSheet.create({
    container: {
        backgroundColor: 'white',
    },
    contentContainer: {
    },
    menuContainer: {
        flexDirection: 'row',
    },
    itemsView: {
        flexDirection: 'row',
        flexWrap: 'wrap',
        width: screen.width,
    },
    pageControl: {
        margin: 10,
    }
});
  • 到首页中修改readerHeader方法
    引入头文件
import HomeMenuView from './HomeMenuView'

将HomeMenuView添加到方法中去

renderHeader() {
        return (
            <View>
                <HomeMenuView menuInfos={api.menuInfo} onMenuSelected={this.onMenuSelected} />
                <SpacingView />
                <View style={styles.recommendHeader}>
                    <Heading2>猜你喜欢</Heading2>
                </View>
            </View>
        )
    }

设置样式

recommendHeader: {
        height: 35,
        justifyContent: 'center',
        borderWidth: screen.onePixel,
        borderColor: color.border,
        paddingVertical: 8,
        paddingLeft: 20,
        backgroundColor: 'white'
    },

接下来封装头部的第二部分
原理同第一部分类似,建立第二个部分的js文件HomeGridView和第二部分的每一小项HomeGridItem。

HomeGridItem:

export default class HomeGridItem extends PureComponent {
    render() {
        let info = this.props.info

        let title = info.maintitle
        let color = info.typeface_color
        let subtitle = info.deputytitle
        let imageUrl = info.imageurl.replace('w.h', '120.0')

        return (
            <TouchableOpacity style={styles.container} onPress={this.props.onPress}>
                <View>
                    <Heading1 style={{ color: color, marginBottom: 10 }}>{title}</Heading1>
                    <Heading2 >{subtitle}</Heading2>
                </View>

                <Image style={styles.icon} source={{ uri: imageUrl }} />
            </TouchableOpacity>
        );
    }
}

// define your styles
const styles = StyleSheet.create({
    container: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        width: screen.width / 2 - screen.onePixel,  //一行放两个,减去一个像素点
        height: screen.width / 4,
        backgroundColor: 'white',
        borderBottomWidth: screen.onePixel,
        borderRightWidth: screen.onePixel,
        borderColor: color.border
    },
    icon: {
        width: screen.width / 5,
        height: screen.width / 5,
    }
});

HomeGridView:

import React, { PureComponent } from 'react';
import { View, Text, StyleSheet } from 'react-native'
import color  from '../../widget/color'
import  screen from '../../common/screen'
import HomeGridItem from './HomeGridItem'

export default class HomeGridView extends PureComponent {
    //设定一个数组,用来接收数据
    static defaultProps = {
        infos: []
    }

    render() {
        return (
            <View style={styles.container}>
            //将infos赋值给Item
                {this.props.infos.map((info, index) => (
                    <HomeGridItem
                        info={info}
                        key={index}
                        onPress={() => this.props.onGridSelected(index)} />
                ))}
            </View>
        );
    }
}

// define your styles
const styles = StyleSheet.create({
    container: {
        flexDirection: 'row',
        flexWrap: 'wrap',
        justifyContent: 'space-between',
        borderTopWidth: screen.onePixel,
        borderLeftWidth: screen.onePixel,
        borderColor: color.border
    },
});

在HomeScene中
引入头文件

import HomeGridView from './HomeGridView'

修改renderHeader方法,将gridView加入进去

<HomeGridView infos={this.state.discounts} onGridSelected={(this.onGridSelected)} />
 <SpacingView />

到构造函数中绑定onGridSelected和onMenuSelected

 { this.onGridSelected = this.onGridSelected.bind(this) }
        { this.onMenuSelected = this.onMenuSelected.bind(this) }

将这两个方法的原型写出来

onGridSelected(index) {

    }
    onMenuSelected(index) {
        alert(index)
    }

首页第一个界面完成,其余的页面放在之后讲解。

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,065评论 4 62
  • 再不写,就忘记了。 前天上午忙了一阵子,回家休息,独自一人,焦虑瞬间袭来,搁在一边儿,睡觉。 醒来下午又继续外出办...
    李金峰_吉林阅读 140评论 0 0
  • 项目管理步骤 1.需求评审:必须想尽办法把整个需求流程串联起来,形成总体认识闭环2.接口层评审:想到每个场景下,是...
    gadfly_only阅读 715评论 0 49
  • “那我过来看看她吧!”军在电话里回答这妈的话。他最怕我妈,直到婚后一直都怕。电话未挂,她妈问我要不要他过来,我点头...
    娟娟头阅读 194评论 0 0
  • 下午3時,夏日太陽正毒,空氣中夾雜著車輛捲起的扬尘,悶熱的天把市中心本來就快的節奏压得更快了。 我買...
    慈開阅读 263评论 3 1