初識ReactNative

公司打算用react-native開發APP,初始RN遇到了很多坑,搭建了一個小的項目框架,結合redux根據公司現成的接口寫了幾個小demo,接觸前端半年多,之前是做iOS的,感覺轉起來很困難,希望有大神可以給出些優化的建議!

項目介紹

  • 這是一個利用react-native配合redux開發的小框架。
  • 項目主要結構
  • android 安卓原生代碼
  • ios iOS原生代碼
  • src RN代碼
    • actions 處理事件,是把數據從應用(譯者注:這里之所以不叫 view 是因為這些數據有可能是服務器響應,用戶輸入或其它非 view 的數據 )傳到 store 的有效載荷。它是 store 數據的唯一來源。
    • components 子組件
    • constants 常量,actions中用到的狀態,action 內必須使用一個字符串類型的 type 字段來表示將要執行的動作。多數情況下,type 會被定義成字符串常量。當應用規模越來越大時,建議使用單獨的模塊或文件來存放 action
    • containers 父組件,類似于MVC中的控制器,
    • reducers 改變狀態,action描述事件發生,通過reducers改變狀態state
    • statics資源(例如圖片)
    • utils工具方法
    • store統一管理狀態
    • index.htmlhtml加載DOM
    • root.jsRN入口
  • index.android.js 安卓程序的入口
  • index.ios.js 蘋果程序入口
  • package.json 配置文件

項目啟動流程

  • index.html通過加載<div id="react-root"></div>進入root.js
<!DOCTYPE html>
<meta charset="utf-8">
<title>React Native for Web</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<div id="react-root"></div>
<script src="/bundle.js"></script>
  • root.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore';
class Root extends Component {
  render() {
    return (
      <Provider store={configureStore()}>
        <App />
      </Provider>
    );
  }
}
export default Root;
  • App.js設置一些啟動時需要做的事情,renderScene添加路由
import React, {Component} from 'react';
import { Navigator } from 'react-native';
import MainTabsView from './MainTabsView';
import BroswerView from './BroswerView';
import LoginView from './LoginView';
import CarDetailView from './CarDetailView'
import FindCarView from './FindCarView'
import FreePriceView from './FreePriceView'
import CarLifeView from './CarLifeView'
import SpecialCarView from './SpecialCarView'
const ROUTES = {
  main_tabs_view: MainTabsView,
  login_view:LoginView,
  broswer_view: BroswerView,
  car_detail_view:CarDetailView,
  find_car_view:FindCarView,
  free_price_view:FreePriceView,
  car_life_view:CarLifeView,
  special_car_view:SpecialCarView,
}
class App extends Component {
  renderScene = (route, navigator) => {
    let Scene = ROUTES[route.name];
    console.log("app renderscene");
    switch (route.name){
      case 'main_tabs_view':
        return <Scene navigator={navigator} tab={2}/>;
      case 'login_view':
        return <Scene navigator={navigator}/>;
      case 'broswer_view':
        return <Scene
          url={route.url}
          navigator={navigator}/>;
      case 'car_detail_view':
        return <Scene {...route.params} navigator={navigator}/>;
      case 'find_car_view':
        return <Scene navigator={navigator}/>;
      case 'free_price_view':
      return <Scene navigator={navigator}/>;
      case 'car_life_view':
        return <Scene navigator={navigator}/>;
      case 'special_car_view':
        return <Scene navigator={navigator}/>;
    }
  }
  configureScene = (route, routeStack) => {
    switch (route.name){
      default:
            return Navigator.SceneConfigs.PushFromRight;
    }
  }
  render() {
    return <Navigator
      initialRoute={{name: 'login_view'}}
      renderScene={this.renderScene}
      configureScene={this.configureScene}/>
  }
}
export default App;

效果展示

  • 登錄


    登錄
  • 首頁 (redux數據流程的簡單介紹)
  • 我們在組件生命周期componentDidMount()中進行數據請求。this.props.actions.getBannerSource({});根據reduxaction中處理事件。
componentDidMount() {
    this.props.actions.getBannerSource({});
    this.props.actions.getHotCarSource({});
    this.props.actions.getFreePriceListSource({});
    this.props.actions.getCarShowBannerSource({});
    this.props.actions.getSpecialCarSource({});
  }
  • 此時數據流將會來到action中,根據狀態常量DATA_BANNER,處理事件,action中只處理事件,要想改變狀態,通過receiveBannerPosts(banner, json)方法去reducers中改變狀態
//輪播圖
export function getBannerSource(item) {
    console.log("getBannerSource");
    return dispatch => {

        //dispatch(requestPosts(item));
        return fetch(API_BASE_URL+'/mobile/ad/findAllShowAdByMobile', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'sessionid': "hanwuqia"
            },
            body: JSON.stringify({"query":{"pagenum":10,"page":1}})
        })
            .then((response) => response.json())
            .then((responseJson) => {
                //console.log(responseJson);
                if(responseJson.code == 0){
                    dispatch(receiveBannerPosts(responseJson.data.rows));
                }

            })
            .catch((error) => {
                console.error(error);
            });
    }
}
function receiveBannerPosts(banner, json) {
    return {
        type: DATA_BANNER,
        banner,
    }
}
  • 當數據流來到reducer時會通過剛才的狀態常量給狀態banner賦值,這樣請求下來的數據進入到狀態樹中。
export default function secondView(state ={
    banner: [],
}, action) {
    switch (action.type) {
        case types.DATA_BANNER :
            return Object.assign({}, state, {
                banner: action.banner
            });
        default:
            return state
    }
}
  • 這時在組件中得知狀態改變從而重修刷新render方法,填充數據,這樣就獲得了輪播圖的數據,這里輪播圖我用了一個叫做react-native-viewpager 的第三方組件
function mapStateToProps(state) {
  return {
    banner: state.secondView.banner,
  };
}
function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Actions, dispatch)
  }
}
export default connect(
    mapStateToProps,
    mapDispatchToProps
)(SecondView);
  • RN 中所有布局全部包在一個大的View中就好像h5中的盒子布局的div類似,如果有兩個大盒子會報錯const { } = this.props;是es6語法獲得到這個組件的狀態,通過這樣的方法<Banner banner={banner}></Banner>傳給子組件,每當狀態改變時會自動刷新,進而子組件內容也進行刷新。
render() {
    const {  banner ,hotCarList,freePriceList,carShowBanner,specialCar,navigator} = this.props;
    return (
        <View style={{ flex: 1, backgroundColor:'#EBEBEB'}}>
            <TitleBar title="react-native-mobile"></TitleBar>
            <ScrollView >
                <Banner banner={banner}></Banner>
                <ToolBar navigator={navigator}></ToolBar>
                <Text style={styles.title}>免費看低價</Text>
                <FreePriceList  navigator={navigator} freePriceList={freePriceList}></FreePriceList>
                <Text style={styles.title}>車主秀</Text>
                <CarShowBanner carShowBanner={carShowBanner}></CarShowBanner>
                <Text style={styles.title}>熱門推薦</Text>
                <HotCarList hotCarList={hotCarList} navigator={navigator}></HotCarList>
            </ScrollView>
        </View>
    )
  }
首頁

首頁
  • 找車模塊
  • 找車的熱門選車模塊用到了RN中核心組件ListView的使用,這是一個分組的listView, dataSource就是列表的數據源,根據官方文檔不同樣式的列表有不同的初始化傳參方式,這里只介紹分組的列表,項目中用到了很多各種各樣的listView列表!所以說他是核心組件。renderRow返回每行樣式,renderSectionHeader返回每組組頭,renderHeader返回列表表頭,像我一樣做過iOS開發的朋友可能會覺得不得理解,就像我們的UITableView中的各種代理方法一樣,官網還有很多,很實用。
render(){
        const { brand} = this.props;
        var  Arr = brand ,
            sectionIDs =[],//所有區ID的數組
            rowIDs =[];//行ID數組
        for (let i in brand ) {
            sectionIDs.push(i);
            rowIDs.push(brand[i])
        }
        return(
                <ListView//創建表,并設置返回section和cell的方法
                    dataSource={this.dataSource.cloneWithRowsAndSections(Arr,sectionIDs,rowIDs)}
                    renderRow={this.renderRow}
                    renderSectionHeader={this.renderSectionHeader}
                    renderHeader={this.renderHeader }
                />
        )
    }
//返回頭部視圖
    renderHeader(){
        var rowWidth = screenWidth/5;
        return(
            <View  style={{width:screenWidth,flexDirection:'row',flexWrap:'wrap',height:160}}>
                {
                    this.hotBrndArr.map((dic, i) => <TouchableOpacity key={i} style={{width:rowWidth,height:80}} onPress={() => {
                                    this.selectBrand(dic)}}>
                        <View  style={{backgroundColor:'white',justifyContent:'center',alignItems:'center',width:rowWidth,height:80}}>
                            <Image style={styles.imageStyle} source={{ uri: dic[1]}}/>
                            <Text>{dic[2]}</Text>
                        </View>
                    </TouchableOpacity>)  // 單行箭頭函數無需寫return
                }
            </View>
        )
    }
    //返回cell的方法
    renderRow(rowData,sectionID,rowID,highlighRow){
        return(
            <TouchableOpacity key={rowID} onPress={() => {
                                    this.selectBrand(rowID)}}>
                <View style={styles.cellStyle}>
                    <Image style={styles.imageStyle} source={{ uri: rowID[2]}}/>
                    <Text style={{marginLeft:20}}>{rowID[1]}</Text>
                </View>
            </TouchableOpacity>

        )
    }

    //返回section的方法
    renderSectionHeader(sectionData,sectionID){

        return(
            <View style={styles.sectionStyle}>
                <Text style={{marginLeft:10}}>{sectionID}</Text>
            </View>
        )
    }
找車
  • 篩選的時候有個一個點擊側滑彈出的功能,這是糾結我最久的一塊,一開始我選擇使用了官方API中的Animated,動畫實現方式很簡單,但是動畫加上大量的數據請求,大量的UI操作,導致程序在安卓機上卡的一逼,畢竟只有半年前端經驗,js更是菜的自己都不想說,谷歌了好幾天,安卓機的性能優化的總是不理想,沒辦法,只能采用第三方組件react-native-drawer。使用后再無性能問題。
<Drawer
                    side="right"
                    type="overlay"
                    ref={(ref) => this._drawer = ref}
                    content={...}
                    tapToClose={true}
                    openDrawerOffset={0.2}
                    tweenHandler={(ratio) => ({main: { opacity:(2-ratio)/2 }})}
                    onClose={()=>{this.maskDidClose()}}
                    styles={{
                            drawer: { shadowColor: '#000000', shadowOpacity: 0.5, shadowRadius: 3},
                            main: {backgroundColor:"#EBEBEB"},
                    }}
                >
  • react-native-drawer文檔中給出了各種方向,各種樣子的側滑,感覺非常好用,下面是代碼滑進滑出的方法
closeControlPanel = () => {
        this._drawer.close()
    };
    openControlPanel = () => {
        this._drawer.open()
    };
找車篩選
  • 免費看低價
  • 這個模塊中讓我也遇到了很多的困難,首先類似UICollectionView中的布局其實也是個listView,通過設置contentContainerStyle讓他變成方形向下平鋪,運行時總是報警告說我的組頭是空的,然后enableEmptySections = {true}設置這個屬性解決,最蛋疼的是有時候數據雖然請求下來但是現實空白頁,用手碰一下就會現實數據,通過設置這個屬性來解決removeClippedSubviews={false}
render(){

        const { car,isRefreshing} = this.props;

        return(
            <ListView //創建ListView
                dataSource={this.dataSource.cloneWithRows(car)} //設置數據源
                renderRow={this.renderRow} //設置cell
                contentContainerStyle={styles.listViewStyle}//設置cell的樣式
                onEndReached={ this._toEnd }
                onEndReachedThreshold={10}
                renderFooter={ this._renderFooter }
                enableEmptySections = {true}
                removeClippedSubviews={false}
                refreshControl={
                        <RefreshControl
                            refreshing={isRefreshing}
                            onRefresh={this._onRefresh}
                            tintColor="gray"
                            title="Loading..."
                            titleColor="gray"
                            colors={['#ff0000', '#00ff00', '#0000ff']}
                            progressBackgroundColor="#ffff00"
                        />}
            />)
    }
  • 這個是RN自帶的下拉刷新,通過設置title,colors等設置下拉刷新的樣子,通過設置refreshing這個bool屬性來控制刷新開始結束
refreshControl={
                        <RefreshControl
                            refreshing={isRefreshing}
                            onRefresh={this._onRefresh}
                            tintColor="gray"
                            title="Loading..."
                            titleColor="gray"
                            colors={['#ff0000', '#00ff00', '#0000ff']}
                            progressBackgroundColor="#ffff00"
                        />}
  • 當你發現你的cell點擊事件無效果時,或者你的下拉刷新的方法沒有走,是因為你賦給ListView的方法需要聲明一下,這樣才能進入到點擊事件中如:
constructor(props) {
        super(props);
        //返回cell樣式的方法
        this.renderRow = this.renderRow.bind(this);
        //下拉刷新的方法
        this._onRefresh = this._onRefresh.bind(this);
        //上滑刷新的方法
        this._toEnd = this._toEnd.bind(this);
        //返回底部視圖的方法
        this._renderFooter = this._renderFooter.bind(this);
      //數據源初始化
        this.dataSource = new ListView.DataSource({
            rowHasChanged: (row1, row2) => row1 !== row2
        });
    }
免費看低價

免費看低價篩選
  • 車生活
  • 這個模塊是我們很常用的一個架構,類似與網易新聞架構,是APP開發中非常常見的的一中架構,首先當然我還是嘗試著自己寫,根據按鈕點解設置scrollViewcontentOffset,根據contentOffset設置應該選中的按鈕。以及根據切換設置橙色view線的滑動,結果遇到了兩個問題,一個是向上面那樣,大量數據加載,大量的UI創建導致界面卡頓非常厲害,當然是在安卓機上,我大蘋果肯定沒有這個問題,還有一個就是有些方法只有iOS可以試用,想寫一個兩端通用的更是難上加難,所以我引入了react-native-scrollable-tab-view這個第三方組件
render(){
        const {data,refresh,actions}=this.props;

        return(

            <ScrollableTabView  style = {{width:screenWidth,height:screenHeight-64,backgroundColor:'#EBEBEB'}}
                                initialPage={0}
                                tabBarTextStyle={{fontSize: 14}}
                                tabBarUnderlineStyle={{backgroundColor: 'orange'}}
                                tabBarInactiveTextColor = "#999999" tabBarBackgroundColor = "white" tabBarActiveTextColor = "#333333"
                                onChangeTab={(obj) => this.changeTab(obj)}
            >
                <CarLifeList tabLabel='全部' actions={actions} pid={2} index={0} refresh ={refresh} dataArr={data}></CarLifeList>
                <CarLifeList tabLabel='新車' actions={actions} pid={2} index={1} refresh ={refresh} dataArr={data}></CarLifeList>
                <CarLifeList tabLabel='裝飾' actions={actions} pid={2} index={2} refresh ={refresh} dataArr={data}></CarLifeList>
                <CarLifeList tabLabel='改裝' actions={actions} pid={2} index={3} refresh ={refresh} dataArr={data}></CarLifeList>
                <CarLifeList tabLabel='自駕' actions={actions} pid={2} index={4} refresh ={refresh} dataArr={data}></CarLifeList>

            </ScrollableTabView>
        )
           
    }
  • 在這個界面我還遇到了一個我至今還無法相同的問題,我用redux管理狀態,我把一個數組的數據當成一個狀態,每次請求數據的時候對數組進行操作,上滑的時候向數組中添加數據,我認為數組中數據增加了就是狀態改變了,render理應重新渲染才對,但是顯然不是這樣的,我感覺有時候數組作為狀態發生改變時,redux并不能檢測到,通過打印日志,數據確實發生改變了,但是確實沒有重新刷新。之前我做vue使用vuex時也遇到過。我采用的方法不知道是不是規范的方法,我是在狀態中又添加了一個布爾的loading,刷新時設置為ture請求我數據設置為false,通過這個布爾值刷新列表進行重新渲染。
//通過loading 的改變刷新
case types.CAR_SHOW_FETCH :
            return Object.assign({}, state, {
                carShowDataArr: action.carShowDataArr,
                loading:action.loading
            });
  • 這個模塊還遇到個性能問題是有時候狀態太多,有的狀態改變并不需要刷新,這時候基礎很重要了,組件的生命周期有這么個方法shouldComponentUpdate(nextProps, nextState) {}當這個方法的返回值是YES時會刷新,NO時不會刷新,render每次刷新之前都會調用這個方法,只有返回值是YES時才會刷新,這個方法中我們可以做很多事情,解決很多性能問題
    車生活
  • 車款詳情
  • 這個界面算是比較復雜的界面,想想要是讓我用原生寫,我估計要好久可能也寫不好,尤其是下面顏色選擇,這就體現出H5布局的優勢了,簡單,快,react-native使用flexBox布局,真心簡單方便,不過這個界面也遇到了一點性能的小問題,每當每個色塊點擊時要重新請求數據,改變上面的圖片,也就是車的顏色,當我的安卓機每次點擊切換時,TouchableOpacity按鈕自帶的淡入淡出的效果變得非常卡頓了,在onPress執行了一個setState的操作,這個操作需要大量計算工作并且導致了掉幀。對此的一個解決方案是將onPress處理函數中的操作封裝到requestAnimationFrame中:
requestAnimationFrame(() => {
            this.setState({
                selectInteriorColor :index,
            });
            const {  interiorColorList,exteriorColorList,actions ,id,detailType} = this.props;
            let inId = interiorColorList[index].id;
            let outId = exteriorColorList[this.state.selectExteriorColor].id;
            actions.getCarInfo({outId,inId,id,detailType});
 });
車款詳情
  • RN 中加載web
  • 這是個webView,這是我純屬顯得蛋疼想用reactNative加載個h5頁面看看啥效果,請見諒


    WebView

性能優化

  • 學習了一段時間馬上就要開發了,感覺當前對于我來說最大的問題還是關于性能的問題,因為前端基礎比較差,很多很簡單的東西需要好久才能找到合適的解決方案,總結了幾個常見的性能問題,之后仔細又閱讀了一遍官方的文檔,發現很多問題文檔上都有講,所以文檔還是要常看啊
  • console.log語句
    • 在運行打好了離線包的應用時,控制臺打印語句可能會極大地拖累JavaScript線程
    • 解決方案發,自己封裝一個打印語句,根據debug是0還是1選擇是否打印,方便我們上線時把打印全都去掉,推薦一個好用的第三方redux-logger
  • 開發模式和生產模式是大不相同的,有時候開發模式下會很卡,但生產模式就不一樣了。
  • Navigator導航切換,這個是非常常見的卡頓,可以選擇的解決方法也有很多,這是我的解決方法,利用官方推薦的這個組件InteractionManager
  • 首先設置這樣一個狀態
onstructor(props) {
        super(props);
        this.state={

            renderPlaceholderOnly:true,
        }
    }
  • render方法中進行判斷,renderPlaceholderOnly為ture時給他一個加載頁面顯示false時顯示界面
render() {
        if (this.state.renderPlaceholderOnly) {
              //loading頁
            return this._renderPlaceholderView();
        }
        return (     
               //界面
        )
    }
_renderPlaceholderView() {
        return (
            <View style={{flex:1,backgroundColor:'white',justifyContent:'center',alignItems:'center',}}>
                <Text>Loading...</Text>
            </View>
        );
    }
  • 最后將請求數據的方法封裝到下面的方法中InteractionManager.runAfterInteractions是在js動畫結束時會走里面的回調。
componentDidMount() {

        InteractionManager.runAfterInteractions(() => {
            this.setState({renderPlaceholderOnly: false});
            this.props.actions.getBrandSource({});
            this.props.actions.getCarTypesource();

        });

    }
  • 關于listView的性能優化
  • initialListSize 這個屬性定義了在首次渲染中繪制的行數。如果我們關注于快速的顯示出頁面,可以設置initialListSize為1,然后我們會發現其他行在接下來的幀中被快速繪制到屏幕上。而每幀所顯示的行數由pageSize所決定
  • pageSize在初始渲染也就是initialListSize被使用之后,ListView將利用pageSize來決定每一幀所渲染的行數。默認值為1 —— 但是如果你的頁面很小,而且渲染的開銷不大的話,你會希望這個值更大一些。稍加調整,你會發現它所起到的作用。
  • scrollRenderAheadDistance在將要進入屏幕區域之前的某個位置,開始繪制一行,距離按像素計算。如果我們有一個2000個元素的列表,并且立刻全部渲染出來的話,無論是內存還是計算資源都會顯得很匱乏。還很可能導致非常可怕的阻塞。因此scrollRenderAheadDistance允許我們來指定一個超過視野范圍之外所需要渲染的行數。
  • removeClippedSubviews當這一選項設置為true的時候,超出屏幕的子視圖(同時overflow
    值為hidden)會從它們原生的父視圖中移除。這個屬性可以在列表很長的時候提高滾動的性能。默認為false。這是一個應用在長列表上極其重要的優化。Android上,overflow值總是hidden的,所以你不必擔心沒有設置它。而在iOS上,你需要確保在行容器上設置了overflow: hidden。
  • 如果你正在使用一個ListView,你必須提供一個rowHasChanged函數,它通過快速的算出某一行是否需要重繪,來減少很多不必要的工作。如果你使用了不可變的數據結構,這項工作就只需檢查其引用是否相等。同樣的,你可以實現shouldComponentUpdate函數來指明在什么樣的確切條件下,你希望這個組件得到重繪。
  • 關于動畫Animated的使用,盡量使用LayoutAnimation,Animated的接口一般會在JavaScript線程中計算出所需要的每一個關鍵幀,而LayoutAnimation則利用了Core Animation,使動畫不會被JS線程和主線程的掉幀所影響。尤其是在動畫過程中有大量的數據請求,狀態改變。
  • 當具有透明背景的文本位于一張圖片上時,或者在每幀重繪視圖時需要用到透明合成的任何其他情況下,這種現象尤為明顯。設置shouldRasterizeIOS或者renderToHardwareTextureAndroid屬性可以顯著改善這一現象。 注意不要過度使用該特性,否則你的內存使用量將會飛漲。在使用時,要評估你的性能和內存使用情況。如果你沒有需要移動這個視圖的需求,請關閉這一屬性。
  • 在iOS上,每次調整Image組件的寬度或者高度,都需要重新裁剪和縮放原始圖片。這個操作開銷會非常大,尤其是大的圖片。比起直接修改尺寸,更好的方案是使用transform: [{scale}]的樣式屬性來改變尺寸。比如當你點擊一個圖片,要將它放大到全屏的時候,就可以使用這個屬性。
  • Touchable系列組件不能很好的響應
  • 有些時候,如果我們有一項操作與點擊事件所帶來的透明度改變或者高亮效果發生在同一幀中,那么有可能在onPress函數結束之前我們都看不到這些效果。比如在onPress執行了一個setState的操作,這個操作需要大量計算工作并且導致了掉幀。對此的一個解決方案是將onPress
    處理函數中的操作封裝到requestAnimationFrame
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容

  • 開發環境配置 參照這里 頁面渲染 組件的渲染需要在自定義 class 中進行,每個自定義視圖class中都包含一個...
    cocoawork丶閱讀 345評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,681評論 25 708
  • 本人小白,也是最近才開始學習ReactNative,只是想記錄自己的學習歷程,供日后查閱復習,文中有任何錯誤或者不...
    小唐羽鋒閱讀 1,355評論 0 51
  • 初識React Native 2015年3月26日,FaceBook公司正式對外發布了React Native 更...
    koala_閱讀 3,112評論 0 7
  • 事情沒到最后呢,不要想得太多,說不定是好事呢! 每一件事都有兩面性,塞翁失馬焉知非福,因為一個學生我竟然跟鵬哥討論...
    曉玖的碎碎念閱讀 192評論 0 0