Vue組件優(yōu)雅的使用Vuex異步數(shù)據(jù)

Vue組件優(yōu)雅的使用Vuex異步數(shù)據(jù)

前端:Vue+element

項(xiàng)目為前后端分離項(xiàng)目匆背,通過(guò)Ajax交換數(shù)據(jù)励饵。

更新時(shí)間:2020-09-10 19:11:42

0x1 緣起

今天在檢查代碼的時(shí)候發(fā)現(xiàn)了一個(gè)平時(shí)都忽略的問(wèn)題捏顺,就是在組件使用vuex數(shù)據(jù)時(shí)涣脚,組件使用都是同步取的vuex值思杯。關(guān)于vuex的使用可以查看官網(wǎng)文檔:https://vuex.vuejs.org/zh/ 贞铣,如果我們需要的vuex里面的值是異步更新獲取的闹啦,在網(wǎng)絡(luò)和后臺(tái)請(qǐng)求特別快的情況下不會(huì)有什么問(wèn)題。但是網(wǎng)絡(luò)慢或者后臺(tái)數(shù)據(jù)返回較慢的情況下問(wèn)題就來(lái)了辕坝。

0x2 案例

${app}代表你的項(xiàng)目根目錄窍奋,項(xiàng)目目錄結(jié)構(gòu)同大部分Vue項(xiàng)目。

需求

我需要實(shí)現(xiàn)這樣一個(gè)效果酱畅,我需要在foo.vue,bar.vue费变,兩個(gè)不同的頁(yè)面建立一個(gè)使用相同信息的socket連接,當(dāng)我離開(kāi)foo.vue頁(yè)面的時(shí)候斷開(kāi)連接圣贸,在bar.vue頁(yè)面的時(shí)候重新連接挚歧。而且我的socket連接信息(連接地址,端口等)來(lái)自于接口請(qǐng)求吁峻。

初次實(shí)現(xiàn)

App.vue初始化的時(shí)候dispatch一個(gè)action去獲取socket的連接信息滑负,然后在foo.vue或者bar.vue頁(yè)面mounted的時(shí)候進(jìn)行連接。

Vuex

${app}/src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

import api from '@/apis'
import handleError from '@/utils/HandleError'

Vue.use(Vuex)

export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production',
  state: {
    socketInfo: {
      serverName: '',
      host: '',
      port: 8080
    }
  },
  mutations: {
    // Update token
    UPDATE_SOCKET_INFO(state, { socketInfo }) {
      // state.socketInfo = socketInfo
      // Update vuex token
      Object.assign(state.socketInfo, socketInfo)
    }
  },
  actions: {
    // Get socket info
    async GET_SOCKET_INFO({ commit }) {
      // Rquest socket info
      try {
        const res = await api.Common.getSocketUrl()
        // Success
        if (res.success) {
          commit('UPDATE_SOCKET_INFO', {
            socketInfo: res.obj
          })
        }
      } catch (e) {
        // Handle api request exception
        handleError.handleApiRequestException(e)
      }
    }
  }
})

App.vue

${app}/src/App.vue

<!-- App -->
<div id="app"></div>
export default {
  name: 'App',
  mounted() {
    // Get socket info
    this.$store.dispatch('GET_SOCKET_INFO')
  }
}

foo.vue

${app}/src/views/foo/foo.vue

import io from 'socket.io-client'
export default {
  name: 'Foo',
  mounted() {
    const { serverName, host, port } = this.$store.state.socketInfo
    const socket = io(`ws://${host}:${port}`, {
      path: `/${serverName}`,
      transports: ['websocket', 'polling']
    })
  }
}

? 問(wèn)題

問(wèn)題很顯而易見(jiàn)用含,當(dāng)我直接訪問(wèn)foo.vue頁(yè)面的時(shí)候矮慕,如果我的后臺(tái)api或者網(wǎng)絡(luò)請(qǐng)求慢的情況下,我的vuexstore還未更新啄骇,也就是App.vue的請(qǐng)求還未回來(lái)痴鳄,這個(gè)時(shí)候foo.vue頁(yè)面的mounted生命周期函數(shù)已經(jīng)執(zhí)行,很顯然缸夹,我需要的socket連接信息拿不到痪寻,這個(gè)時(shí)候控制臺(tái)就會(huì)飄紅。

WebSocket connection to 'ws://%27%27/''/?EIO=3&transport=websocket' failed: Error in connection establishment: net::ERR_NAME_NOT_RESOLVED

? 第一次解決

既然是需要等到請(qǐng)求回來(lái)在連接虽惭,那么好辦了橡类,我在foo.vue頁(yè)面也獲取一次socket的連接信息獲取成功了在進(jìn)行連接,此時(shí)foo.vue代碼變成了如下這樣

foo.vue

${app}/src/views/foo/foo.vue

import io from 'socket.io-client'

import api from '@/apis'
import handleError from '@/utils/HandleError'
export default {
  name: 'Foo',
  async mounted() {
    // Rquest socket info
    try {
      const res = await api.Common.getSocketUrl()
      // Success
      if (res.success) {
        commit('UPDATE_APP_SESSION_STATUS', {
          socketInfo: res.obj
        })

        // Connect to socket
        const { serverName, host, port } = this.$store.state.socketInfo
        const socket = io(`ws://${host}:${port}`, {
          path: `/${serverName}`,
          transports: ['websocket', 'polling']
        })
      }
    } catch (e) {
      // Handle api request exception
      handleError.handleApiRequestException(e)
    }
  }
}

? 新的問(wèn)題

上一個(gè)辦法確實(shí)解決了問(wèn)題芽唇,但是新的問(wèn)題又來(lái)了顾画,我發(fā)了兩次請(qǐng)求,每個(gè)頁(yè)面都要寫(xiě)一個(gè)請(qǐng)求。仔細(xì)想想這要是個(gè)十幾二十個(gè)頁(yè)面都要用的方法研侣,那不得累死谱邪?有沒(méi)有更好的解決辦法呢?答案是有的庶诡。

? 第二次解決

既然我在foo.vue頁(yè)面需要等待vuex的更新虾标,那我監(jiān)聽(tīng)一下socketInfo的更新,有更新我在連接灌砖,然后在mounted里面判斷socketInfo是否有值再連接不就可以了嗎璧函。這個(gè)時(shí)候foo.vue頁(yè)面的代碼變成了下面這樣

foo.vue

${app}/src/views/foo/foo.vue

import io from 'socket.io-client'

import api from '@/apis'
import handleError from '@/utils/HandleError'
export default {
  name: 'Foo',
  async mounted() {
    if (this.$store.state.socketInfo.host) {
      // Handle create socket
      this.handleCreateSocket()
    }
  },
  watch: {
    '$store.state.socketInfo.host'() {
      if (this.$store.state.socketInfo.host) {
        // Handle create socket
        this.handleCreateSocket()
      }
    }
  },
  methods: {
    // Handle create socket
    handleCreateSocket() {
      // Connect to socket
      const { serverName, host, port } = this.$store.state.socketInfo
      const socket = io(`ws://${host}:${port}`, {
        path: `/${serverName}`,
        transports: ['websocket', 'polling']
      })
    }
  }
}

這里為啥監(jiān)聽(tīng)的是$store.state.socketInfo.host呢,因?yàn)槲覀兊?code>mutations里面的UPDATE_SOCKET_INFO更新socketInfo的方式是Object.assign()基显,這種更新方式的好處是蘸吓,如果api請(qǐng)求返回的字段是這樣的一個(gè)對(duì)象,少了port字段(后臺(tái)開(kāi)發(fā)更新字段很常見(jiàn))

{
    "serverName":"msgServer1",
    "host":"192.168.0.2",
}

我自己的socketInfo對(duì)象

{
    "serverName":"",
    "host":"",
    "port":"8080"
}

假如我在初始化state的時(shí)候指定一個(gè)默認(rèn)的端口撩幽,Object.assign()合并的對(duì)象库继,只會(huì)合并我沒(méi)有的,并且更新與我socketInfo鍵值對(duì)相同的鍵的值窜醉,這樣我的socketInfo對(duì)象依然是有一個(gè)默認(rèn)的端口宪萄,更新后為

{
    "serverName":"msgServer1",
    "host":"192.168.0.2",
    "port":"8080"
}

我的socket依然能夠連接上。不至于報(bào)錯(cuò)榨惰“萦ⅲ回到之前的問(wèn)題,如果我們監(jiān)聽(tīng)的是$store.state.socketInfo琅催,這是個(gè)引用類型的對(duì)象居凶,你會(huì)發(fā)現(xiàn)watch不會(huì)執(zhí)行,因?yàn)槟愕膶?duì)象沒(méi)有改變藤抡。

關(guān)于JavaScript引用數(shù)據(jù)類型和基礎(chǔ)數(shù)據(jù)類型可以查看:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_types

簡(jiǎn)單易懂的:https://segmentfault.com/a/1190000008472264

? 思考新的問(wèn)題

目前看來(lái)完成我的需求是不會(huì)有什么問(wèn)題了侠碧。但是這樣是完美的了嗎?

如果我的foo.vue頁(yè)面不只是創(chuàng)建連接的時(shí)候需要取vuex的數(shù)據(jù)缠黍,我在頁(yè)面渲染的時(shí)候弄兜,也需要vuex里面的數(shù)據(jù)。比如我的foo.vue瓷式,和bar.vue都需要顯示我的網(wǎng)站名替饿,網(wǎng)站名是通過(guò)接口拉取存在vuex的。這個(gè)時(shí)候怎么辦呢蒿往?盛垦,剛剛解決上面問(wèn)題的辦法就無(wú)能為力了湿弦。畢竟mounted不能阻止頁(yè)面渲染瓤漏。

? 最佳方案?

借用watch的方案,我在頁(yè)面判斷一下vuex的值是否更新蔬充,然后再渲染不就ok了嘛蝶俱?這也是很多網(wǎng)站骨架屏渲染的使用場(chǎng)景。

很多網(wǎng)站在剛剛打開(kāi)的一刻饥漫,數(shù)據(jù)未準(zhǔn)備好的時(shí)候是會(huì)顯示一個(gè)骨架加載的動(dòng)畫(huà)榨呆,等到加載完畢再把內(nèi)容呈現(xiàn)給用戶∮苟樱看代碼

${app}/src/views/foo/foo.vue

<div>
  <!-- 我的網(wǎng)站名 -->
  <div v-if="$store.state.webConfig.webName">{{ $store.state.webConfig.webName }}</div>
  <!-- 骨架屏 -->
  <skeleton v-else></skeleton>
</div>
import io from 'socket.io-client'

import api from '@/apis'
import handleError from '@/utils/HandleError'
export default {
  name: 'Foo',
  async mounted() {
    if (this.$store.state.socketInfo.host) {
      // Handle create socket
      this.handleCreateSocket()
    }
  },
  watch: {
    '$store.state.socketInfo.host'() {
      if (this.$store.state.socketInfo.host) {
        // Handle create socket
        this.handleCreateSocket()
      }
    }
  },
  methods: {
    // Handle create socket
    handleCreateSocket() {
      // Connect to socket
      const { serverName, host, port } = this.$store.state.socketInfo
      const socket = io(`ws://${host}:${port}`, {
        path: `/${serverName}`,
        transports: ['websocket', 'polling']
      })
    }
  }
}

? 優(yōu)化代碼

vuexsocketInfo對(duì)象加一個(gè)isUpdated字段积蜻,如果更新了,直接取值進(jìn)行我需要的操作彻消,沒(méi)更新的話就行請(qǐng)求api更新竿拆。這是目前能想到的比較優(yōu)雅的方案了。

${app}/src/views/foo/foo.vue

<div>
  <!-- 我的網(wǎng)站名 -->
  <div v-if="webConfig.isUpdated">
    {{ webConfig.webName }}
  </div>
  <!-- 骨架屏 -->
  <skeleton v-else></skeleton>
</div>
import io from 'socket.io-client'
import { mapState } from 'vuex'

import api from '@/apis'
import handleError from '@/utils/HandleError'
export default {
  name: 'Foo',
  computed: {
    ...mapState(['webConfig', 'socketInfo'])
  },
  async mounted() {
    // Handle get socket info
    this.handleGetSocketInfo()
  },
  methods: {
    // Handle create socket
    handleCreateSocket() {
      // Connect to socket
      const { serverName, host, port } = this.$store.state.socketInfo
      const socket = io(`ws://${host}:${port}`, {
        path: `/${serverName}`,
        transports: ['websocket', 'polling']
      })
    },
    // Handle get socket info
    handleGetSocketInfo() {
      if (this.socketInfo.isUpdated) {
        // Handle create socket
        this.handleCreateSocket()
      } else {
        this.$store.dispatch('GET_SOCKET_INFO', this.handleCreateSocket)
      }
    }
  }
}

${app}/src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

import api from '@/apis'
import handleError from '@/utils/HandleError'

Vue.use(Vuex)

export default new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production',
  state: {
    socketInfo: {
      serverName: '',
      host: '',
      port: '',
      isUpdated: false
    },
    webConfig:{
      webName: '',
      isUpdated: false
    }
  },
  mutations: {
    // Update token
    UPDATE_SOCKET_INFO(state, { socketInfo }) {
      // state.socketInfo = socketInfo
      // Update vuex token
      Object.assign(
        state.socketInfo,
        {
          isUpdated: true
        },
        socketInfo
      )
    }
  },
  actions: {
    // Get socket info
    async GET_SOCKET_INFO({ commit }, callback) {
      // Rquest socket info
      try {
        const res = await api.Common.getSocketUrl()
        // Success
        if (res.success) {
          commit('UPDATE_SOCKET_INFO', {
            socketInfo: res.obj
          })
          // Call back you custom function
          if (callback) {
            callback()
          }
        }
      } catch (e) {
        // Handle api request exception
        handleError.handleApiRequestException(e)
      }
    }
  }
})

由于在foo.vue頁(yè)面需要使用數(shù)據(jù)的時(shí)候我們才去請(qǐng)求數(shù)據(jù)宾尚,因此App.vue的請(qǐng)求可以取消丙笋,這樣一來(lái)用戶只是打開(kāi)我們的網(wǎng)站,并不會(huì)去請(qǐng)求無(wú)意義的數(shù)據(jù)煌贴。優(yōu)化了后臺(tái)的接口請(qǐng)求壓力御板。同時(shí)在第一次進(jìn)入foo.vue頁(yè)面的時(shí)候已經(jīng)請(qǐng)求了數(shù)據(jù),如果用戶沒(méi)有刷新頁(yè)面牛郑,再次訪問(wèn)該頁(yè)面我們的socketInfo對(duì)象的isUpdatedtrue怠肋,可以直接使用,不會(huì)去發(fā)送新的請(qǐng)求淹朋。

${app}/src/App.vue

<!-- App -->
<div id="app"></div>
export default {
  name: 'App',
}

??更新方案

既然是進(jìn)入頁(yè)面之后可以判斷數(shù)據(jù)是否加載完畢灶似,我們也可以直接在頁(yè)面進(jìn)入之前,通過(guò)路由元信息配置該頁(yè)面需要的全局異步數(shù)據(jù)瑞你,然后通過(guò)路由跳轉(zhuǎn)的守衛(wèi)去拉取異步數(shù)據(jù)(全局公用的異步數(shù)據(jù)只需要加載一遍就行酪惭,如果加載失敗我們可以跳轉(zhuǎn)到服務(wù)器錯(cuò)誤頁(yè)面)。完了再顯示我們的頁(yè)面者甲,同時(shí)使用Promise.all來(lái)進(jìn)行多個(gè)異步數(shù)據(jù)的讀取春感。nice~!上代碼

${app}/src/utils/permission.js

這個(gè)文件用來(lái)做路由攔截虏缸。

/**
 * @name Global router permission controller
 * @description Do not delete comments
 * @author SunSeekerX
 * @time 2019-08-20 11:14:34
 * @LastEditors: SunSeekerX
 * @LastEditTime: 2020-05-21 15:28:39
 */

import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
NProgress.configure({ showSpinner: false }) // NProgress Configuration

import router from '@/router'
import store from '@/store'
import { i18n } from '@/lang/index'
import { NotifyFun, handleApiRequestException } from '@/utils/handle-error'

router.beforeEach(async (to, from, next) => {
  // 啟動(dòng)進(jìn)度條
  NProgress.start()
  
  // 公用vuex數(shù)據(jù)
  const {
    siteConfig,
    appConfig,
    socketInfo,
    coinDecimal,
  } = store.state.appSessionStatus

  try {
    // 異步任務(wù)列表
    const task = []
    // 站點(diǎn)信息
    if (!siteConfig.isUpdated) {
      task.push(store.dispatch('GET_SITE_CONFIG'))
    }
    // 站點(diǎn)配置
    if (!appConfig.isUpdated) {
      task.push(store.dispatch('GET_APP_CONFIG'))
    }

    /**
     * @name 檢查前去的頁(yè)面需要的公用數(shù)據(jù)是否加載
     */
    if (to.meta.isUsingCoinDecimal && !coinDecimal.isUpdated) {
      // 需要全局小數(shù)點(diǎn)位數(shù)
      task.push(store.dispatch('GET_COIN_DECIMAL'))
    }

    if (to.meta.isUsingSocketInfo && !socketInfo.isUpdated) {
      // 需要全局socket鏈接信息
      task.push(store.dispatch('GET_SOCKET_INFO'))
    }

    // 異步同時(shí)執(zhí)行請(qǐng)求任務(wù)
    await Promise.all(task)
  } catch (error) {
    // 提示錯(cuò)誤
    handleApiRequestException(error)

    // 顯示網(wǎng)絡(luò)錯(cuò)誤白屏圖
    store.commit('UPDATE_SERVER_ERROR', true)

    // 請(qǐng)求失敗鲫懒,路由導(dǎo)航終止
    return next(false)
  } finally {
    NProgress.done()
  }

  // Permission
  if (store.state.token) {
    // Has login
    next()
  } else {
    // 判斷是否是公開(kāi)頁(yè)面
    if (to.meta.isPublic) {
      next()
    } else {
      // Redirect to login
      next('/user/user-login')
    }
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

路由配置

${app}/src/router/Exchange.js

/**
 * @name Exchange.js
 * @author SunSeekerX
 * @time 2019-09-21 10:51:25
 * @LastEditors: SunSeekerX
 * @LastEditTime: 2020-05-21 14:59:37
 */

import { i18n } from '@/lang/index'

export default [
  // 幣幣交易》首頁(yè)
  {
    path: '/exchange',
    name: 'ExchangeIndex',
    component: () => import('@/views/exchange/index/index'),
    meta: {
      title: i18n.t('Title_Exchange'),
      // 代表路由權(quán)限公開(kāi)
      isPublic: true,
      // 需要socketInfo信息
      isUsingSocketInfo: true,
      // 需要coinDecimal信息
      isUsingCoinDecimal: true,
    },
  },
]

0x3 總結(jié)

記錄下自己平時(shí)解決問(wèn)題的思考方式和解決方案。

本文章代碼僅用工具檢查語(yǔ)法錯(cuò)誤刽辙,純手寫(xiě)窥岩,并未實(shí)際運(yùn)行,不保證邏輯合理宰缤,如果你有更好的方案颂翼,歡迎你和我討論晃洒。

有問(wèn)題才有更好的解決方案。謝謝你的閱讀朦乏。

0x4 謝謝你的閱讀 ??

關(guān)于我

SunSeekerX球及,前端開(kāi)發(fā)、Nodejs開(kāi)發(fā)呻疹、小程序吃引、uni-app開(kāi)發(fā)、等等

喜歡探討技術(shù)實(shí)現(xiàn)方案和細(xì)節(jié)刽锤,完美主義者镊尺,見(jiàn)不得bug

Github:https://github.com/SunSeekerX

個(gè)人博客:https://yoouu.cn/

個(gè)人在線筆記:https://sunseekerx.yoouu.cn/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末并思,一起剝皮案震驚了整個(gè)濱河市鹅心,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纺荧,老刑警劉巖旭愧,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宙暇,居然都是意外死亡输枯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)占贫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)桃熄,“玉大人,你說(shuō)我怎么就攤上這事型奥⊥眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵厢汹,是天一觀的道長(zhǎng)螟深。 經(jīng)常有香客問(wèn)我,道長(zhǎng)烫葬,這世上最難降的妖魔是什么界弧? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮搭综,結(jié)果婚禮上垢箕,老公的妹妹穿的比我還像新娘。我一直安慰自己兑巾,他們只是感情好条获,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蒋歌,像睡著了一般帅掘。 火紅的嫁衣襯著肌膚如雪委煤。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,462評(píng)論 1 302
  • 那天锄开,我揣著相機(jī)與錄音素标,去河邊找鬼称诗。 笑死萍悴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寓免。 我是一名探鬼主播癣诱,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼袜香!你這毒婦竟也來(lái)了撕予?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蜈首,失蹤者是張志新(化名)和其女友劉穎实抡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體欢策,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吆寨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踩寇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啄清。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖俺孙,靈堂內(nèi)的尸體忽然破棺而出辣卒,到底是詐尸還是另有隱情,我是刑警寧澤睛榄,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布荣茫,位于F島的核電站,受9級(jí)特大地震影響场靴,放射性物質(zhì)發(fā)生泄漏计露。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一憎乙、第九天 我趴在偏房一處隱蔽的房頂上張望票罐。 院中可真熱鬧,春花似錦泞边、人聲如沸该押。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蚕礼。三九已至烟具,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奠蹬,已是汗流浹背朝聋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囤躁,地道東北人冀痕。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狸演,于是被迫代替她去往敵國(guó)和親言蛇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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