ReactNative 學(xué)習(xí)記錄(一)

RN 的環(huán)境搭建和基礎(chǔ)操作參照官方文檔:查看

學(xué)習(xí)做一個 Movie Fetcher


俗話說前人栽樹,后人乘涼.我們在沒有什么 RN 基礎(chǔ)的時候,跟著前輩已有的項目教程學(xué)著寫代碼顯然是個不錯的選擇.

參考項目地址:查看 參考源碼:查看

我使用RN 版本的是目前的最新版0.39

(一)第一部分:初學(xué)

1. 模擬數(shù)據(jù)(Mocking data)

在我們寫代碼去獲取加載真實(shí)的數(shù)據(jù)之前,我們先來模擬一下數(shù)據(jù)。一般我們會聲明一些常量在JS文件的頭部,僅僅在imports語句下面。當(dāng)然了,你可以添加在其他一些地方,只要你喜歡即可。下面是index.ios.js以及index.android.js需要添加的代碼:

var MOCKED_MOVIES_DATA = [
  {title: 'Title', year: '2015', posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'}},
];

2.渲染一條電影數(shù)據(jù)(Redner a movie)

接下來我們渲染顯示電影的標(biāo)題,年份以及電影的縮略圖。縮略圖是React Native中的Image組件進(jìn)行顯示,然后我們需要導(dǎo)入Image組件

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

我們修改render()方法來進(jìn)行渲染該條電影數(shù)據(jù):

render() {
    var movie = MOCKED_MOVIES_DATA[0];
    return (
      <View style={styles.container}>
        <Text>{movie.title}</Text>
        <Text>{movie.year}</Text>
        <Image source={{uri: movie.posters.thumbnail}} />
      </View>
    );
  }

}
然后我們打開開發(fā)者菜單/點(diǎn)擊Reload JS,你可以看到"Title"和"2015"這兩個數(shù)據(jù),當(dāng)然你會注意到Image沒有任何渲染。這是因為你的Image組件沒有指定寬和高。這個你可以通過定義Style實(shí)現(xiàn),接下來我們來寫一個樣式。

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
});

然后再添加樣式:

       <Image
         source={{uri: movie.posters.thumbnail}}
         style={styles.thumbnail}
       />

渲染之后可能圖片不會出現(xiàn)
可以用官方樣例測試一下

<Image source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}
       style={{width: 400, height: 400}} />

如果官方圖片可以顯示 說明可能是墻的問題
建議 把圖片存到本地 通過其他方法調(diào)用


index.ios.js
/**
 * Sample React Native App
 */

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

var MOCKED_MOVIES_DATA = [
  {title: 'Title', year: '2015'},
];

export default class MovieFetcher extends Component {
  render() {
    var movie = MOCKED_MOVIES_DATA[0];
    return (
      <View style={styles.container}>
        <Text style={styles.title}>{movie.title}</Text>
        <Text style={styles.year}>{movie.year}</Text>
        <Image source={require('./img/UePbdph.jpg')} 
               style={styles.thumbnail} />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },

});

AppRegistry.registerComponent('MovieFetcher', () => MovieFetcher);


這是目前的效果

我們來分析一下代碼:

1.樣式
const styles = StyleSheet.create({})

這個樣式部分是與原生 js 最接近的 也很好理解
有種 html 里面加了 className 然后在 css 里面加屬性的感覺

你只需要通過JavaScript定義應(yīng)用的樣式即可。
所有核心的組件都有style的屬性。
該樣式的名稱和屬性值幾乎和Web端的CSS樣式差不多,不過需要修改成駝峰命名法,例如:這邊使用backgroudColor代替background-color。

下面兩段代碼的作用是一致的:

...
<Image source={require('./img/UePbdph.jpg')} 
               style={styles.thumbnail} />
...
 
const styles = StyleSheet.create({
  thumbnail: {
    width: 53,
    height: 81,
  },
});
<Image source={require('./img/UePbdph.jpg')} 
               style={ width: 53, height: 81} />

而前者 建立樣式表的方式 便于管理和復(fù)用

2.import
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
} from 'react-native';

這個部分主要添加必要的模塊

3.定義
  • 常量
var MOCKED_MOVIES_DATA = [
  {title: 'Title', year: '2015'},
];

這個也沒什么好說的
定義了一個數(shù)組對象 里面有一個 json json 作為一個輕量的存儲方式很適合用來存一些數(shù)據(jù)

export default class MovieFetcher extends Component {}

這里定義了一個叫做MovieFetcher的類
export 可以將該類導(dǎo)出 以便于其他 js 文件導(dǎo)入使用
為方便理解,可以把類的定義看做 函數(shù)定義 ,當(dāng)然這兩者不等同
default 一個文件中只能用一次

下面這個類也是合法形式

class Project extends Component{}
4.render()

這個是一個渲染器
原生 js 運(yùn)行時由瀏覽器進(jìn)行渲染 RN 里面通過虛擬 Dom進(jìn)行渲染

render() 里面是什么呢 是核心組件Core Components
我們再來看一眼上面的內(nèi)容

export default class MovieFetcher extends Component {}
  • 這里就有個 component
    核心組件介紹: 查看
    像一些 Text 組件. Image 組件 TextInput 組件等等

要注意的是: 每次使用組件都要在文件頭部看一下有沒有引入相應(yīng)的模塊



如果這里沒有引入 Image 模塊
我們剛剛的代碼就要出錯啦

然后改一改布局



把內(nèi)容顯示變成這樣

1.圖片和文字作為整體垂直居中
2.文字部分占據(jù)橫向空余空間

(二)第二部分:添加初始化的過程

平時我們在看到內(nèi)容加載的時候 會有進(jìn)度條或者轉(zhuǎn)動的圓圈顯示在界面上 接下里就要做這個效果

按照教程敲完代碼

可以實(shí)現(xiàn) 圖片還是老問題

我們先不管圖片 來看一下代碼

/**
 * Sample React Native App
 */

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


var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';

class MovieFetcher extends Component {
    constructor(props) {
      super(props);
      this.state = {
        movies: null,
      };
     }
    componentDidMount() {
      this.fetchData();
    }
    fetchData() {
      fetch(REQUEST_URL)
        .then((response) => response.json())
        .then((responseData) => {
          this.setState({
            movies: responseData.movies,
          });
        })
      .done();
    }

    render() {
        if (!this.state.movies) {
          return this.renderLoadingView();
        }
     
        var movie = this.state.movies[0];
        return this.renderMovie(movie);
      }
     
      renderLoadingView() {
        return (
          <View style={styles.container}>
            <Text>
              Loading movies...
            </Text>
          </View>
        );
      }
     
      renderMovie(movie) {
        return (
          <View style={styles.container}>
            <Image
              source={{uri: movie.posters.thumbnail}}
              style={styles.thumbnail}
            />
            <View style={styles.rightContainer}>
              <Text style={styles.title}>{movie.title}</Text>
              <Text style={styles.year}>{movie.year}</Text>
            </View>
          </View>
        );
      }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  rightContainer: {
    flex: 1,
  },
  title: {
    fontSize: 30,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    fontSize: 18,
    textAlign: 'center',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },

});

AppRegistry.registerComponent('MovieFetcher', () => MovieFetcher);

樣式就不說了
來說一說 Props(屬性)與State(狀態(tài))

  • 屬性 props

上面的 image 組件

            <Image
              source={{uri: movie.posters.thumbnail}}
              style={styles.thumbnail}
            />

這里的 source就是一個屬性 用來選擇圖片的來源
除了自定義常量可以給屬性賦值外
自定義的組件也是可以使用props的

...
class Greeting extends Component {
  render() {
    return (
      <Text>Hello {this.props.name}!</Text>
    );
  }
}
class LotsOfGreetings extends Component {
  render() {
    return (
      <View style={{alignItems: 'center'}}>
        <Greeting name='Rexxar' />
        <Greeting name='Jaina' />
        <Greeting name='Valeera' />
      </View>
    );
  }
我們來改變index.ios.js 跑一下加深一下對 prop 的理解
/**
 * Sample React Native App
 */

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

class Greeting extends Component {
  render() {
    return (
      <Text>Hello {this.props.name}!</Text>
    );
  }
}
 
class MovieFetcher extends Component {
  render() {
    return (
      <View style={{alignItems: 'center',marginTop: 100,}}>
        <Greeting name='Rexxar' />
        <Greeting name='Jaina' />
        <Greeting name='Valeera' />
      </View>
    );
  }
}

AppRegistry.registerComponent('MovieFetcher', () => MovieFetcher);

看一下效果


猜測 this.props 里面存了所有的屬性值 調(diào)用 name 顯示 name 值

  • state

不知道大家有沒有了解過狀態(tài)機(jī)



狀態(tài)機(jī) 就是一個狀態(tài)執(zhí)行完就等待觸發(fā)然后跳到下一個狀態(tài)執(zhí)行的一種機(jī)制.

props是在父組件中進(jìn)行設(shè)置,只要設(shè)置完成那么該在組件的聲明周期中就定死了,不會發(fā)生改變。所以針對數(shù)據(jù)變化修改的情況,我們需要使用state屬性。

一般情況下,我們需要在constructor方法中進(jìn)行初始化state,然后在你想要修改更新的時候調(diào)用setState方法即可。

state 的改變充當(dāng)了觸發(fā)條件
render()充當(dāng)了執(zhí)行過程
render() 里面的內(nèi)容就是需要執(zhí)行的內(nèi)容

例如:我們現(xiàn)在需要制作一段不斷進(jìn)行閃動的文字效果,文字內(nèi)容當(dāng)組件創(chuàng)建好的時候就已經(jīng)指定了。文字內(nèi)容通過prop展現(xiàn)。但是通過時間控制文字閃動的狀態(tài)通過state實(shí)現(xiàn)。一起來看一下如下的代碼:

改變 index.ios.js
/**
 * Sample React Native App
 */

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

 
class Blink extends Component {
  constructor(props) {
    super(props);
    this.state = {showText: true};
 
    // Toggle the state every second
    setInterval(() => {
      this.setState({ showText: !this.state.showText });
    }, 1000);
  }
 
  render() {
    let display = this.state.showText ? this.props.text : ' ';
    return (
      <Text>{display}</Text>
    );
  }
}
 
class MovieFetcher extends Component {
  render() {
    return (
      <View style={{flex: 1,alignItems: 'center',justifyContent: 'center',}}>
        <Blink text='I love to blink' />
        <Blink text='Yes blinking is so great' />
        <Blink text='Why did they ever take this out of HTML' />
        <Blink text='Look at me look at me look at me' />
      </View>
    );
  }
}
 

AppRegistry.registerComponent('MovieFetcher', () => MovieFetcher);

我們來看一下代碼部分
為了使用 <Blink text='I love to blink' />
自定義了一個Blink類

class Blink extends Component {}

首先初始化 state

  • props來自于父組件或者自身getDefaultProps
    這里就傳入了 <Blink text='I love to blink' />里面的 text 屬性
  • 接著初始化了一個state的showText為true的state對象
constructor(props) {
    super(props);
    this.state = {showText: true};
    ...
}
  • 接下來的定時器的作用是每隔一秒鐘使得 state 對象里的 showText 布爾值取反 設(shè)置 state 需要用到 setState 方法
    ...
    setInterval(() => {
      this.setState({ showText: !this.state.showText });
    }, 1000);
    ...
  • 渲染虛擬 Dom: 當(dāng)showText布爾值為 true 的時候顯示 反之不顯示
  render() {
    let display = this.state.showText ? this.props.text : ' ';
    return (
      <Text>{display}</Text>
    );
  }

看到這里大家對 prop 和 state 都有一定了解了

  • 我們再回過頭去看之前的代碼
(一) 首先定義一個全局常量 一個 json 對象
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';
(二)然后定義MovieFetcher類
class MovieFetcher extends Component {}
(三) 初始化 state: this.state.movie 設(shè)為 null
    ...
    constructor(props) {
      super(props);
      this.state = {
        movies: null,
      };
     }
    ...
(四) 當(dāng)調(diào)用componentDidMount的時候執(zhí)行this.fetchData()方法

componentDidMount:在render渲染之后,React會根據(jù)Virtual DOM來生成真實(shí)DOM,生成完畢后會調(diào)用該函數(shù)。在瀏覽器端(React),我們可以通過this.getDOMNode()來拿到相應(yīng)的DOM節(jié)點(diǎn)。然而我們在RN中并用不到,在RN中主要在該函數(shù)中執(zhí)行網(wǎng)絡(luò)請求,定時器開啟等相關(guān)操作
componentDidMount方法方法只會在組件完成加載的時候調(diào)用一次。

  ...
  componentDidMount() {
      this.fetchData();
  }
  ...
(五) this.fetchData()

接下來我們添加一個fetchData方法來加載處理數(shù)據(jù)。我們需要在數(shù)據(jù)加載成功之后調(diào)用this.setState({moves:data}),要知道該setState方法會觸發(fā)控件重新渲染,同時也會注意到this.state.moves不會一直未null。該方法最后我們調(diào)用done()方法,我們需要確保最后調(diào)用done()方法,這樣有任何異常我們可以攔截到并且處理。

    ...
    fetchData() {
       //fetch ->get方法,只填寫url參數(shù)
      fetch(REQUEST_URL)
       //上面一行會返回響應(yīng)對象,即response
        .then((response) => response.json())
       //response.json()將返回一個json類型對象
        .then((responseData) => {
          this.setState({
            movies: responseData.movies,
          });
        })
      //注意我們在Promise調(diào)用鏈的最后調(diào)用了done() —— 這樣可以拋出異常而不是簡單忽略。
      .done();
    }
    ...
(六) render()

當(dāng)moves數(shù)據(jù)為空的時候顯示一個正在加載的視圖
不為空的時候加載 json 里面的數(shù)據(jù)

render() {
    if (!this.state.movies) {
      return this.renderLoadingView();
    }
    var movie = this.state.movies[0];
    return this.renderMovie(movie);
  }
  renderLoadingView() {
    return (
      <View style={styles.container}>
        <Text>
          Loading movies...
        </Text>
      </View>
    );
  }
  renderMovie(movie) {
    return (
      <View style={styles.container}>
        <Image
          source={{uri: movie.posters.thumbnail}}
          style={styles.thumbnail}
        />
        <View style={styles.rightContainer}>
          <Text style={styles.title}>{movie.title}</Text>
          <Text style={styles.year}>{movie.year}</Text>
        </View>
      </View>
    );
  }
(七)試著用豆瓣電影的 API 來改寫上面的代碼
//地址
"https://api.douban.com/v2/movie/subject/1764796"

改動:

效果:

(三)第三部分:添加 ListView列表組件

先來看一下 ListView的官方例子

改變 index.ios.js
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
  ListView,
} from 'react-native';
var MovieFetcher = React.createClass({
    getInitialState: function() {
      var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
        return {
          dataSource: ds.cloneWithRows(['row 1', 'row 2','row 3','row 4','row 5','row 6','row 7','row 8']),
        };
    },
    render: function() {
      return (
        <ListView
          dataSource={this.state.dataSource}
          renderRow={(rowData) => <Text>{rowData}</Text>}
          style={{marginTop: 20}}
        />
      );
    }
});
AppRegistry.registerComponent('MovieFetcher', () => MovieFetcher);

效果:


  • getInitialState()

它的 getInitialState方法用于定義初始狀態(tài),也就是一個對象,這個對象可以通過 this.state屬性讀取。當(dāng)用戶點(diǎn)擊組件,導(dǎo)致狀態(tài)變化,this.setState方法就修改狀態(tài)值,每次修改以后,自動調(diào)用 this.render方法,再次渲染組件。

  • rowHasChanged

rowHasChanged(prevRowData, nextRowData):指定我們更新row的策略,一般來說都是prevRowData和nextRowData不相等時更新row
rowHasChanged函數(shù)可以告訴ListView它是否需要重繪一行數(shù)據(jù)。

  • cloneWithRows(dataBlob, rowIdentities)

該方法接收兩個參數(shù),dataBlob是原始數(shù)據(jù)源。在沒有section,傳入一個純數(shù)組的時候使用此方法。rowIdentities為可選類型,為數(shù)據(jù)源的每一項指明一個id。默認(rèn)的id為字符串'0','1','2'...dataBlob.count。

  • dataSource ListViewDataSource 設(shè)置ListView的數(shù)據(jù)源

dataSource :該屬性,用于為ListView指定當(dāng)前的數(shù)據(jù)源

  • renderRow function 方法

(rowData,sectionID,rowID,highlightRow)=>renderable 該方法有四個參數(shù),其中分別為數(shù)據(jù)源中一條數(shù)據(jù),分組的ID,行的ID,以及標(biāo)記是否是高亮選中的狀態(tài)信息。
renderRow :該屬性用來標(biāo)示ListView中每一行需要顯示的樣子。參數(shù)表示當(dāng)前行需要顯示的數(shù)據(jù)

開始添加 ListView
  • import ListView組件
import {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} from 'react-native';

然后我們修改render方法,使用ListView渲染加載電影數(shù)據(jù)而不是單條記錄

render() {
  if (!this.state.loaded) {
    return this.renderLoadingView();
  }
  return (
    <ListView
      dataSource={this.state.dataSource}
      //調(diào)用 renderMovie方法
      renderRow={this.renderMovie}
      style={styles.listView}
    />
  );
}

dataSource是一個Listview的接口用來確定在數(shù)據(jù)更新過程中那些行發(fā)生了變化。
看上面的代碼你也會注意到通過this.state來訪問dataSource,那么接下來就需要在constructor()構(gòu)造方法中創(chuàng)建一個空的dataSource。
現(xiàn)在通過dataSource來進(jìn)行存儲數(shù)據(jù),那么我們需要定義一個狀態(tài):this.state.loaded來確保獲取加載數(shù)據(jù)不出現(xiàn)重復(fù)請求存儲。

constructor(props) {
   super(props);
   this.state = {
     dataSource: new ListView.DataSource({
       rowHasChanged: (row1, row2) => row1 !== row2,
     }),
     loaded: false,
   };
 }

最后我們給ListView控件添加一個Style樣式風(fēng)格

listView: {
   paddingTop: 20,
   backgroundColor: '#F5FCFF',
 },

萬里長征終于跨出了第一步~ 撒花~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內(nèi)容