開源意見征集 |Taro React Native 提案

導(dǎo)讀

58同城前端通道與京東凹凸實(shí)驗(yàn)室達(dá)成 Taro 項(xiàng)目開源合作,針對(duì) Taro 3 React Native 支持方案,做了較大程度的重構(gòu)。

經(jīng)過較長(zhǎng)時(shí)間的方案討論,現(xiàn)已正式對(duì)外公布 RFC 提案,并征集社區(qū)意見。開源社區(qū)參與者可通過 https://github.com/NervJS/taro-rfcs/pull/8 或點(diǎn)擊閱讀原文參與提案討論,期待您的參與。

提案詳情

概述

在 Taro 中實(shí)現(xiàn)使用?React?開發(fā)?React Native。

動(dòng)機(jī)

Taro 升級(jí)到 3 以后,React Native 無法使用,社區(qū)仍然有使用 Taro 開發(fā) React Native 的需求。

使用案例

在頁(yè)面組件和自定義組件開發(fā)中,與編寫 React 項(xiàng)目遵循一致的規(guī)范。

入口組件生命周期、頁(yè)面組件生命周期及頁(yè)面事件處理函數(shù),支持度與小程序相比會(huì)有差異,參考詳細(xì)設(shè)計(jì)運(yùn)行時(shí)部分。

API、組件支持度與小程序相比有些取舍,參考詳細(xì)設(shè)計(jì) API 及組件部分。

詳細(xì)設(shè)計(jì)

本提案以 Taro 3 定義的標(biāo)準(zhǔn)為基礎(chǔ),完成 React Native 端的升級(jí)改造,同時(shí)在編譯打包方案、API 及組件支持、React Native 項(xiàng)目接入靈活性等方面做了較大重構(gòu)。

設(shè)計(jì)總結(jié)

將編譯打包方案統(tǒng)一為 metro ,更貼合 React Native 生態(tài)體系。

相比 webpack + metro 的方式,可提升編譯速度,整體方案也更為清晰。

相比 webpack 多 entry 模式,可降低包大小。同時(shí)解決多 entry 模式存在的一些問題,如?#7512。

提供更好的 sourcemap 支持,優(yōu)化開發(fā)體驗(yàn)。

與 React Native APP 項(xiàng)目自身的 metro 配置,如分包等,可靈活合并。

運(yùn)行時(shí)模塊,按照 Taro 3 定義的標(biāo)準(zhǔn)進(jìn)行改造。

與小程序及 H5 內(nèi)部的寫法保持基本一致,通過 metro transformer (類似 taro loader) 生成入口及頁(yè)面代碼,通過 taro-runtime-rn 包提供的方法包裝入口組件及頁(yè)面組件。

提升對(duì)頁(yè)面事件處理函數(shù)的支持度。

增加對(duì) Tab Bar 相關(guān) API 的封裝。

提升 API 及組件的支持度。

按社區(qū)調(diào)研結(jié)果優(yōu)先級(jí)及難易程度進(jìn)行支持,仍然以 expo 體系為主。

API 及組件可按需集成,對(duì)于依賴原生的 API 和組件,提供完整的原生集成文檔。

視工作量情況,逐步提升支持度,同時(shí)歡迎社區(qū)貢獻(xiàn)。

提供更靈活的 React Native APP 接入方案。

不再鎖定 React Native 版本,用戶可在項(xiàng)目中自行安裝 >=0.60 版本的 React Native,對(duì)于 >=0.57 && <=0.59 版本的支持將在后續(xù)推出。

除 React Navigation 外不強(qiáng)制依賴其他 Native Modules,用戶可按需引入所需的原生依賴。相應(yīng)原生依賴未安裝時(shí),相關(guān)接口不可用,但不會(huì)阻斷程序運(yùn)行。同時(shí)用戶可根據(jù)自身應(yīng)用情況,對(duì)所需 API 接口或組件進(jìn)行替換。

不需要靈活定制的用戶,仍可以使用我們提供的包含所需原生依賴殼工程項(xiàng)目,快速開始開發(fā)。

整體設(shè)計(jì)圖示

大致流程如下:

? ? ?0. @taro/cli 中通過registerPlatform 注冊(cè) rn 平臺(tái);

yarn dev:rn 獲得編譯配置,轉(zhuǎn)為 babel.config.js 及 metro.config.js 配置;

所有 React Native 不支持的語(yǔ)法及配置,通過編譯配置支持;

通過編譯配置與?@tarojs/taro-rn-transformer 生成 React Native 的入口文件 index.ts;

入口文件引入?@tarojs/taro-runtime-rn 使用createReactNativeApp進(jìn)行包裝;

頁(yè)面文件引入?@tarojs/taro-runtime-rn 使用createPageConfig 進(jìn)行包裝;

啟動(dòng) metro bunlder;

在 React Native Shell 工程中運(yùn)行 react-native run-ios 或 react-native run-android 加載?index.bundle。

本次新增及修改的包列表

包名 (packages下文件夾名)包功能

*taro-cli1. 獲取公用編譯配置及 RN 特殊配置,啟動(dòng) rn-runner。

2. 檢查依賴包是否正常安裝:包括(react-native、component-rn、runtime-rn等)

*taro-component-rn1. 增加對(duì)虛擬列表的支持。

2. 增加對(duì)舊版本不支持的組件的支持,詳見組件改造小節(jié)。

*taro-rn1. 增加對(duì)舊版本不支持的接口的支持,詳見API改造小節(jié)。

taro-rn-runner1. 與用戶自定義的 metro 配置做合并。

2. 生成babel配置。

3. 引入style transformer、taro-rn-transformer 等包生成metro配置。

4. 啟動(dòng)bundler。

5. 支持通用編譯配置,包括 defineConstants、copy、alias、sass、env 等。

6. 支持RN特殊配置,如樣式縮放開關(guān)。

7. 引入 babel plugin

transform-jsx-to-stylesheet以支持 className 寫法。

8. 支持優(yōu)先加載.rn.文件,Android加載 .android.文件,iOS加載 .ios.文件。

9. 編譯平臺(tái)包替換及 alias 配置。

10. 打包壓縮、sourcemap 等能力支持。

taro-rn-transformer1. 攔截入口文件 index.js/index.ts,根據(jù)配置,生成入口文件代碼。

2. 根據(jù) app.config 的 pages 配置,攔截頁(yè)面文件,根據(jù)配置,生成頁(yè)面文件代碼。

3. 功能類似小程序及h5的 taro-loader。

taro-style-transformer1. 支持 sass、less、stylus、postcss文件的引入,轉(zhuǎn)化為 styleObject。

2. 支持 sass 的全局引入配置

3. 引入 postcss-pxtransform 以支持 React Native 單位轉(zhuǎn)化。

taro-asset-transformer1. 支持引入靜態(tài)資源,轉(zhuǎn)化圖片等資源為 base64。

babel-plugin-transform-jsx-to-stylesheet1. jsx 支持 className 屬性,將各種語(yǔ)法轉(zhuǎn)化為 styleObject[‘className’]等。

bable-preset-taro-rn1. 將所需 babel plugin 進(jìn)行合并,統(tǒng)一管理。

taro-runtime-rn1. 提供 app 包裝方法。

2. 提供 page 包裝方法。

3. 支持 pulldownrefresh 等頁(yè)面事件處理函數(shù)支持。

4. 支持頁(yè)面配置,title、navigatebar等。

5. 提供 Taro3 新增的全局對(duì)象 Current 等。

taro-router-rn1. 封裝所有的路由處理。

2. 支持新增的TabBar相關(guān)API。

3. 支持tabbar相關(guān)配置。

4. 支持icon配置。

注:帶*為做修改的包,不帶為本次新增或基本重構(gòu)的包。

編譯打包方案改造

對(duì)于通用配置的支持情況

配置是否支持方案

sourceRoot支持-

outputRoot支持-

designWidth支持-

defineConstants支持使用?babel-plugin-transform-inline-environment-variables?加入到運(yùn)行環(huán)境中。

alias支持使用?babel-plugin-module-resolver?支持。

env支持使用?babel-plugin-transform-inline-environment-variables加入到運(yùn)行環(huán)境中。

包含

process.env.TARO_ENV?值為?rn?。

copy不支持-

plugins不支持-

presets不支持-

terser支持-

csso支持transformer 引入?csso。

sass支持-

webpack loader 配置替代方案

通過 webpackChain 修改配置已不再支持,對(duì)樣式編譯配置的修改使用如下代替配置。

rn: {

sass: {

options: ...,// https://github.com/sass/node-sass#options

additionalData: ...,// {String|Function} 注入到所有 sass 文件中

sourceMap: boolean,// 不做支持

},

less: {},

stylus: {},

postcss: {},

}

平臺(tái)差異化文件引用支持

通過配置 babel plugin,支持編譯 React Native 平臺(tái)時(shí),優(yōu)先使用?*.rn.*,編譯 Android 平臺(tái)優(yōu)先使用?*.android.*,編譯 iOS 平臺(tái)優(yōu)先使用?*.ios.*。

參考 babel-plugin-react-native-platform-specific-extensions 實(shí)現(xiàn)。

編譯平臺(tái)包替換及 alias 配置

將?@tarojs/taro-components 替換為?@tarojs/taro-components-rn,將?@tarojs/taro 替換為?@tarojs/taro-rn。

安裝 babel-plugin-module-resolver,配置 babel.config.js。

{

plugins: [

[

'module-resolver',

{

alias: {

'@tarojs/components':'@tarojs/components-rn',

'@tarojs/taro':'@tarojs/taro-rn',

},

},

]

}

用戶 metro 配置自定義合并

taro-rn-runner 將所需的 metro 配置作為 defaultConfig,項(xiàng)目中的 metro.config.js 作為配置載入。

Metro.loadConfig({}, defaultConfig);

sourcemap 支持

基于 metro 本身的 sourcemap,可自行調(diào)整配置。

TypeScript 支持

metro-react-native-babel-transformer 使用的 metro-react-native-babel-preset 默認(rèn)支持 TypeScript。

樣式語(yǔ)法支持

統(tǒng)一使用 transformer 實(shí)現(xiàn)

className 語(yǔ)法支持

通過操作 visitor 的 JSXOpeningElement,ImportDeclaration,Program.exit,實(shí)現(xiàn) className 的語(yǔ)法轉(zhuǎn)換。

參考 babel-plugin-transform-jsx-stylesheet 包做一些改進(jìn)。

支持包括如下場(chǎng)景:

class string 與 多class string;

對(duì)象?{ active: this.props.isActive };

數(shù)組?['header1 header2', 'header3', { active: this.props.isActive }];

三元運(yùn)算this.props.visible ? 'show' : 'hide';

自定義函數(shù)?getClassName();

多個(gè)樣式文件,對(duì)象進(jìn)行合并;

層疊樣式表預(yù)編譯語(yǔ)言支持(sass less stylus postcss)

通過配置 metro 的?transformer.babelTransformerPath?以支持 css 預(yù)編譯語(yǔ)言,將編譯結(jié)果導(dǎo)出為 styleObject。

參考如下實(shí)現(xiàn):

react-native-sass-transformer

react-native-typed-postcss-transformer

react-native-less-transformer

react-native-stylus-transformer

sass.resource 支持全局

在使用 metro 的 sass transformer 時(shí),將?sass.resource?配置的全局 sass 文件注入到樣式文件頭部。

單位轉(zhuǎn)化

同 2.x 通過 postcss-pxtransform 插件,在使用 metro 的 postcss transformer 時(shí),引入該插件。該功能支持關(guān)閉。

樣式文件中跨平臺(tái)支持

如:

/*? #ifdef? %PLATFORM%? */

樣式代碼

/*? #endif? */

仍然通過?postcss-pxtransform?支持。

全局的 app.css 支持

將 app.jsx 中對(duì)樣式的引用,合并至頁(yè)面的 styleObject 中。

media-query-processor 及 viewport-units 支持

參考 react-native-dynamic-style-processor 實(shí)現(xiàn)。

Taro3標(biāo)準(zhǔn)運(yùn)行時(shí)支持

使用方法及實(shí)現(xiàn)方案,與小程序和h5保持一致。

使用 taro-rn-transformer/app.ts 生成入口文件

metro transformer 中判斷是否為 index.js,是的話,使用 taro-rn-transformer/app 傳入編譯配置,進(jìn)行轉(zhuǎn)換。

transformer 偽代碼如下:

varupstreamTransformer =require("metro-react-native-babel-transformer");

const{ transform } =require("@tarojs/taro-rn-transformer/app");

const{ pages } =require('./config');

module.exports.transform =function({ src, filename, options }){

if(filename ==='index.js') {

returnupstreamTransformer.transform(transform({ src, filename, {pages, ...options}}));

}

returnupstreamTransformer.transform({src, filename, options });

};

生成后入口文件的偽代碼如下:

importAppfrom'./src/App';

import{AppRegistry}from'react-native';

import{createReactNativeApp}from'@tarojs/taro-runtime-rn';

importconfigfrom'./src/App.config';

// 遍歷配置生成的頁(yè)面路由表

importPagesIndexIndexScreenfrom'./src/pages/index/index';

importPagesIndexAboutScreenfrom'./src/pages/index/about';

constrouters = [{

name:'PagesIndexIndex',

component: PagesIndexIndexScreen

}, {

name:'PagesIndexAboutScreen',

component: PagesIndexAboutScreen

}];

constbuildConfig = {};

AppRegistry.registerComponent('appName', () => createReactNativeApp(App, config, routers, buildConfig));

再經(jīng)過 metro-react-native-babel-transformer 進(jìn)行二次轉(zhuǎn)化。

頁(yè)面文件生成 taro-rn-transformer/page.ts

metro transformer 中判斷是否為頁(yè)面文件,是的話,使用 taro-rn-transformer/page 傳入編譯配置,進(jìn)行轉(zhuǎn)換。

生成后頁(yè)面文件的偽代碼如下:

import{createPageConfig}from'@tarojs/taro-runtime-rn';

importconfigfrom'./pagename.config';

// 原有的代碼

importComponetfrom'./pagename';

exportdefaultcreatePageConfig(Component, config);

再經(jīng)過 metro-react-native-babel-transformer 進(jìn)行二次轉(zhuǎn)化。

createReactNativeApp

暴露給 @tarojs/taro-rn-transformer/app 調(diào)用,在 React Native 應(yīng)用入口文件中調(diào)用,創(chuàng)建一個(gè) React Native App 構(gòu)造函數(shù)接受小程序應(yīng)用規(guī)范對(duì)象。

createPageConfig

暴露給 @tarojs/taro-rn-transformer/page 調(diào)用,在 React Native 應(yīng)用頁(yè)面文件中調(diào)用,創(chuàng)建一個(gè) React Native App 構(gòu)造函數(shù)接受小程序頁(yè)面規(guī)范對(duì)象。

Current

暴露給開發(fā)者的 Taro 全局變量,目前有三個(gè)屬性:

Current.app,返回當(dāng)前小程序應(yīng)用實(shí)例,非小程序端返回小程序規(guī)范應(yīng)用實(shí)例,可通過此實(shí)例調(diào)用小程序規(guī)范生命周期。

Current.page,返回當(dāng)前小程序頁(yè)面實(shí)例,非小程序端返回小程序規(guī)范頁(yè)面實(shí)例,可通過此實(shí)例調(diào)用小程序規(guī)范生命周期。

Current.router,返回當(dāng)前小程序路由信息,非小程序端返回小程序規(guī)范路由信息。

針對(duì) React Navigation 做一樣的封裝。

頁(yè)面事件處理函數(shù)支持

函數(shù)

支持方案

onPullDownRefresh基于 scrollView 的 refreshControll。

onReachBottom監(jiān)聽 onMomentumScrollEnd。

onPageScroll基于 scrollView。

onResize基于 Dimensions。

onTabItemTap基于 React Navigation tabPress 事件。

自定義 hooks 支持

useDidShow,useDidHide,usePullDownRefresh等,參考 H5 React 實(shí)現(xiàn)。

路由支持從外部直接帶參數(shù)進(jìn)入指定頁(yè)面

基于 React Navigation Deep linking 進(jìn)行實(shí)現(xiàn)。

API改造

API 改造

組件包 taro-rn fork 自 2.x 版本,增加對(duì)如下API的支持。

API

實(shí)現(xiàn)方案

tabBar相關(guān)API基于 React Navigation 進(jìn)行封裝。

VideoContext基于 expo-av 進(jìn)行封裝。

CameraContext基于 expo-camera 進(jìn)行封裝。

AudioContext基于 expo-av 進(jìn)行封裝。

* Animation基于 Animated 進(jìn)行封裝。

掃碼基于 expo-barcode-scanner 進(jìn)行封裝。

File相關(guān)API基于 expo-file-system 進(jìn)行封裝

* Canvas相關(guān)API基于 react-native-canvas 封裝。

網(wǎng)絡(luò)請(qǐng)求(downloadFile,uploadFile)基于 fetch 及 expo-file-system 進(jìn)行封裝。

Resize 及 Keyboard 事件監(jiān)聽Resize 基于 Dimensions,Keyboard基于

WebSocket基于 React Native WebSocket Support 進(jìn)行封裝。

EventChannel,Events,eventCenter基于 DeviceEventEmitter 進(jìn)行封裝。

RequestTask相關(guān)API-

注:帶*為評(píng)估優(yōu)先級(jí)較低。

組件改造

組件包 taro-component-rn fork 自 2.x 版本,增加對(duì)如下組件的支持。

組件

實(shí)現(xiàn)方案

VirtualList封裝 FlatList。

MovableView 及 MovableArea封裝 Animated 及 PanResponder。

Label封裝 View 當(dāng)點(diǎn)擊時(shí),會(huì)觸發(fā)對(duì)應(yīng)的控件更新。

PickerView 及 PickerViewColumn基于 @ant-design/react-native 封裝。

Navigator封裝對(duì)路由的操作。

Audio基于 expo-av 進(jìn)行封裝。

Video基于 expo-av 進(jìn)行封裝。

Camera基于 expo-camera 進(jìn)行封裝。

*Canvas基于 react-native-canvas 封裝。

注:帶*為評(píng)估優(yōu)先級(jí)較低。

APP接入方式改造

appName 支持配置

因不同 APP 有不一樣的 appName 配置,app.config.ts 增加配置項(xiàng) appName,該參數(shù)用于注冊(cè)入口組件。

import{ appName }from'./app.config';

AppRegistry.registerComponent(appName, () => App);

React Native 多版本支持

一期兼容 >= 0.60 版本,初始化會(huì)默認(rèn)選擇最新的穩(wěn)定版本,用戶根據(jù)需要可自行選擇可兼容的版本進(jìn)行安裝。同時(shí)殼工程或自身APP需要使用相應(yīng)的版本。

不強(qiáng)制依賴所有的 Native Modules

當(dāng) APP 中一些 Native Modules 不存在時(shí),部分 API 不可用,此時(shí)彈出警告,而不是阻塞運(yùn)行。

其他

狀態(tài)管理支持

同 Taro 3 方案,由使用方自行處理。

SubPackages 分包

暫不支持分包,分包的配置的 subPackages 會(huì)被合并進(jìn)入 pages。

殼工程

保留在原工程倉(cāng)庫(kù) taro-native-shell 中,增加新的分支 0.63.0,0.62.0,0.61.0,0.60.0,均對(duì)應(yīng) Taro 3版本。

缺陷

0. 不支持使用 vue 或者 jQuery 框架編寫 React Native。

1. 整體設(shè)計(jì)與小程序和 H5 有些差異。

2. 采用了 metro 編譯打包方案,與 webpack 的方案有較大不同,需及時(shí)同步新版本迭代的更新,以支持一些新寫法。

適配策略

0. 使用 Taro React 開發(fā)的代碼,對(duì)不支持的組件及接口做兼容,安裝有相應(yīng)依賴后,即可運(yùn)行于 React Native 環(huán)境。

1. 含有部分 React Native 的原生依賴,需要增加到各自的原生倉(cāng)庫(kù)里,或使用殼項(xiàng)目進(jìn)行。

2. React Native 實(shí)現(xiàn)的樣式是 css 的子集,無法做到完全兼容,需要由業(yè)務(wù)自行兼容,做跨端項(xiàng)目建議 React Native first。

作者簡(jiǎn)介:

陳志慶:58同城 前端架構(gòu)師,技術(shù)委員會(huì)委員。

推薦閱讀:

開源 | WPaxos:生產(chǎn)級(jí)Paxos算法實(shí)現(xiàn)解析

開源 | Umajs:輕量級(jí) Node.js Web 框架

開源 | Picasso:sketch設(shè)計(jì)稿智能解析工具

開源|Magpie可視化圈選埋點(diǎn)實(shí)踐

閱讀原文

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