Vuex學習

Vuex學習

一、Vuex是做什么的?

官方解釋:Vuex是一個專為Vue.js應用程序開發(fā)的狀態(tài)管理模式,它采用集中式存儲管理應用的所有組件的狀態(tài)房维,并以相應的規(guī)則保證狀態(tài)以一種個預測的方式發(fā)生變化,Vuex也集成到Vue的官方調試工具devtools extension抬纸,提供了諸如零配置的time-travel調試咙俩、狀態(tài)快照導入導出等高級調試功能。

看完官方解釋湿故,如果你不理解什么式狀態(tài)管理阿趁,那肯定得一臉懵逼。

  • 什么是狀態(tài)坛猪?

    狀態(tài)其實就是一些變量歌焦,比如說用戶的信息啊,地理位置這些需要在多個頁面顯示的狀態(tài)就需要多個組件之間共享這些狀態(tài)砚哆。

  • 什么是狀態(tài)管理?什么是集中式存儲管理?

    通過我們原先組件間傳遞變量的方式只能父傳子躁锁,子傳父纷铣。當我們的項目變得越來越大,然后形成組件樹之后战转,各個不同分支的組件之間如果想要使用某些相同的數據的話就會非常麻煩搜立,就要一級一級的傳。所以就需要進行某些公用狀態(tài)的統(tǒng)一管理槐秧,這就是狀態(tài)管理啄踊,而全部公用的狀態(tài)交給一個對象進行管理的方式就式集中式管理。就像是公司的財政部刁标,你這邊想要用錢颠通,那就找財政部開,那邊想要用錢膀懈,也就去找財政部開顿锰。將公司的財政進行集中式,交給財政部打理启搂,就差不多這樣硼控。而Vuex就是扮演了這種大管家的作用,只要是公用的狀態(tài)都有它來管理胳赌,這樣就大大的簡化了組件間共享數據的傳遞牢撼。

  • 為什么需要使用一個專門的創(chuàng)建來進行狀態(tài)管理?

    如果僅僅只是進行狀態(tài)管理疑苫,那感覺好像沒必要安裝一個專門的插件熏版,咱們自己在Vue的原型對象中添加一個對象,Vue.prototype.objManager = {}缀匕,不就能進行狀態(tài)管理了嗎纳决?所以,Vuex最大的便利其實和VueJS一樣乡小,它們都加入了響應式系統(tǒng)阔加,所以它們中的屬性能夠做到響應式,所以我們使用Vuex满钟。

二胜榔、單頁面的狀態(tài)管理

單頁面狀態(tài)管理.png

單個頁面就是單個組件,在一個組件中進行狀態(tài)管理是非常簡單的湃番,因為不需要進行組件間數據的傳遞夭织。其中

State:就是需要管理的狀態(tài),就相當于data中的保存的變量吠撮。

View:視圖層尊惰,用來顯示State中的數據

Actions:就是一些用戶的交互操作,比如說點擊啊什么的,然后State會根據Actions來改變狀態(tài)弄屡。

三题禀、多頁面的狀態(tài)管理

Vue中對單個組件中的狀態(tài)管理已經幫我們實現好了,而且是響應式的膀捷。那如果現在我們需要進行多個頁面也就是多個組件間都需要使用的一些狀態(tài)的管理迈嘹,而且也想要它們做到響應式呢,這就需要使用到vuex了全庸,它就是一個全局的大管家秀仲,它是通過單例設計模式設計出來的,由于只會有一個大管家壶笼,而且它一直存在神僵,那就可以將共享的狀態(tài)交給它保存管理,然后按照它的使用規(guī)則就能做到響應式的管理共享狀態(tài)了拌消。

上面提到了vuex的使用規(guī)則挑豌,那都有哪些規(guī)則呢,后面我們再繼續(xù)學習墩崩,現在先記住一點氓英,就是State的改變必須得通過Mutations來進行,上面這幅圖就是它的工作流程鹦筹。如果你在組件中直接來拿到State進行修改的話铝阐,Devtools就檢測不到數據的改變了。

四铐拐、多頁面狀態(tài)管理徘键,vuex基本使用,計數器小案例

想要進行多頁面狀態(tài)的管理遍蟋,那就需要使用到vuex插件了

Add組件

<template>
  <div>
    <h2>------------Add的內容-------------</h2>
    <button @click="countAdd">+</button>
  </div>
</template>

<script>
export default {
  name:"Add",
  methods: {
    countAdd(){
      // this.$store.state.count++;不能這么干
      this.$store.commit('increment');
    }
  },
}
</script>

<style scoped>

</style>

你可以下載vue提供的devtools瀏覽器插件吹害,這個插件用于對vue項目進行debug等等操作,它能監(jiān)聽vuex的state改變虚青,不過必須是要通過mutation改變的才行它呀,你可以通過this.$store.state.count直接來改變state,不過不要這樣做棒厘,雖然這樣也能響應式纵穿,不要跳過mutation來修改state的內容。這樣devtools是捕捉不到這次修改的記錄的奢人,這樣子devtools的數據和頁面顯示的就不同了谓媒。

$store和之前的$router差不多,都是代指我們掛載在Vue實例對象中的router或著store

五何乎、Vuex狀態(tài)管理圖例

vuex狀態(tài)管理.png

通過這副vuex的狀態(tài)管理條例句惯,可以很明確的看出來土辩,State最好通過Mutations來進行修改,官方也推薦這樣宗弯。

六脯燃、Vuex的核心概念

  • State

    • State單一狀態(tài)樹

      單一狀態(tài)樹的意思指的是,在我們的應用中只有一個Vuex提供的這個大管家蒙保。為什么這樣設計呢,就是因為如果我們的狀態(tài)分別存儲在多個store對象中的話欲主,那么如果你某個需求需要用到好幾個store里的狀態(tài)邓厕,那么就得分別去多個store里拿,這在之后的管理和維護都會變得非常困難扁瓢,因為你同時得維護多個store揩晴,特別的調試的時候迎吵,如果出現了一個bug,可能就得全部store排查一遍。

  • getters

    • getters基本使用

      getters和我們原先在組件中定義的computed有異曲同工之妙萎馅,都是將原有的數據進行一定的變換后返回。

      const store = new Vuex.Store({
        state:{
          goods:[
            {id:100,name:'熱水壺',price:80},
            {id:101,name:'保溫杯',price:83},
            {id:102,name:'洗衣機',price:2000},
            {id:103,name:'熱水器',price:134},
            {id:104,name:'電視',price:4141},
            {id:105,name:'電腦',price:2454},
            {id:106,name:'冰箱',price:414},
            {id:107,name:'羽絨服',price:4641}
          ]
        }
      

      現在需要取出State中價格高于500的商品退敦,這就需要使用到getters了越驻。不可能每個使用到的地方都重復的寫一串代碼吧,這也是計算屬性和getters 的意義楣铁。

      getters:{
          pMFiveH(state) {
            return state.goods.filter(g => g.price > 500);
          }
        }
      
    • getters本身作為參數

      getters本身是可以作為參數玖雁,傳入某個getter中的,這樣子就可以進行一些混合的操作了盖腕,接著上面的例子赫冬,現在需要求出價格高于500的商品的數量。

      getters:{
          pMFiveH(state) {
            return state.goods.filter(g => g.price > 500);
          },
              //注意:這里雖然state沒有用到溃列,都是參數列表中一定要先寫state劲厌,才能寫getters,因為它在傳參的時候就是先傳入的state然后才傳的getters
          pMFiveHLength(state,getters) {
            return getters.pMFiveH.length;
          }
        }
      
    • getters傳遞參數

      getters和computed一樣听隐,默認是不能傳遞參數的补鼻,他們是當作屬性一樣來使用的而不是方法,如果我們想要往getters中傳入參數遵绰,那么只能讓getters返回一個函數辽幌,然后我們調用這個函數。

      getGoodById(state,getters,id) {
            console.log(id);
          }
      

      如果強行往getters定義的方法中傳入第三個你自定義的參數椿访,并且試圖調用它的話乌企,它會報這個錯誤

      Error in render: "TypeError: _vm.$store.getters.getGoodById is not a function"

      所以驗證了不能傳參,現在就來解決這個問題

      getGoodById(state) {
            /*
              find() 方法返回通過測試(函數內判斷)的數組的第一個元素的值成玫。
              find() 方法為數組中的每個元素都調用一次函數執(zhí)行:
              當數組中的元素在測試條件時返回 true 時, find() 返回符合條件的元素加酵,之后的值不會再調用執(zhí)行函數拳喻。
              如果沒有符合條件的元素返回 undefined
              注意: find() 對于空數組,函數是不會執(zhí)行的猪腕。
              注意: find() 并沒有改變數組的原始值冗澈。
            */
            return id => state.goods.find(g => g.id === id)
          }
      

      可以這樣定義方法,然后這樣調用

      {{$store.getters.getGoodById(100)}}

      $store.getters.getGoodById ==> 返回一個函數fn

      然后調用這個fn(100)

  • Mutations

    • mutations基本使用

      上面給計數器小案例已經演示過基本使用了陋葡,Vuex的store狀態(tài)更新的唯一方式:提交Mutation,Mutation主要包括兩部分亚亲,字符串的事件類型(type),一個回調函數,該回調函數的第一個參數就是state腐缤。

      mutations:{
          increment//1.事件類型捌归,提交的時候就是提交它
          (state) {
            state.count++;
          },//2.回調函數
          decrement(state) {
            state.count--;
          }
        },
            
      this.$store.commit('increment');//提交mutation
      
    • mutations傳遞參數

      在使用mutation更新狀態(tài)的時候,我們可能希望攜帶一些額外的參數岭粤,這些參數被稱之為mutation的載荷(Payload)

      subFive(state,n){
            state.count -= n;
          }
      

      如果只有一個參數惜索,就這樣子傳過去,不過一般傳過去的都是一個對象剃浇,這個對象就是載荷payload.

      subFive(state,payload){
            state.count -= payload.count;
          }
      this.$store.commit('subFive',{
              count:5
            });
      
    • mutations提交風格

      上面的commit進行提交是一種普通的方式巾兆,vue還提供了另外的一種提交風格,就是commit一個對象虎囚。

      this.$store.commit({
              type:'subFive',
              count:5
            });
      

      這種提交風格角塑,vue是將提交的對象整體作為Payload來處理,這種風格看起來更加好看一點溜宽。

    • mutations響應規(guī)則

      之前說必須要按照一些Vuex的規(guī)則進行數據的改變才能做到響應式吉拳,現在就來學習一下都有哪些規(guī)則。

      • 提前在store中初始化好所需的屬性

      • 當給state中的對象添加新屬性時适揉,請這樣添加

        • 使用Vue.set(obj,'newProp',value)
        • 將新的對象給舊的對象留攒,就是將舊的對象的指針重新指向新的對象

        先試一下直接添加

        //定義Mutation
          aAddHeight(state,payload) {
              state.author.height = payload.height;
            }
            /*************/
        //提交Mutation
            addHeight(){
              this.$store.commit({
                type:'aAddHeight',
                height:1.88
              })
            }
        
非常尷尬,明明state已經改變了嫉嘀,但是頁面沒有改變炼邀,這是因為Vue中有個響應式系統(tǒng),在將Vuex的store掛載到Vue實例前剪侮,vue會將所有已經定義的屬性給加入到響應式系統(tǒng)中拭宁,而原來沒有的屬性自然不會有響應式了,因為它沒有加入到響應式系統(tǒng)中瓣俯。那怎么將沒有添加入響應式系統(tǒng)的屬性給手動的添加進去呢杰标,這就是Vue.set方法,它會將屬性給加入響應式系統(tǒng)內彩匕。

> set(object: object, **key: string | number**, value: any): any
aAddHeight(state,payload) {
      //無法響應式
      // state.author.height = payload.height;
      //響應式
      // 方式一:使用Vue.set
      // Vue.set(state.author,'height',payload.height);
      //方式二:將舊對象重新賦值
      //...state.author將這個對象解構出來腔剂,然后放在這里
      state.author = {...state.author,'height':payload.height}
    }

這下子就能給新附加的屬性也加入到響應式系統(tǒng)中了。

如果說Vue.set可以將之前沒定義的屬性加入到響應式系統(tǒng)中驼仪,那舊對象重新賦值為什么也能響應式呢掸犬,我的理解是袜漩,雖然state中的舊author對象中原來沒有height屬性,但是state中原先是定義了一個author湾碎,這個對象是響應式的宙攻,所以這個對象整體的改變就是響應式的。

  • mutations常量管理

前面我們說過一個Mutation又一個事件類型和一個回調函數構成介褥。在提交mutation的時候是要提交這個事件類型座掘,當我們的項目越來越大的時候,Vuex管理的狀態(tài)越來越多呻顽,需要更新的狀態(tài)也會越來越多雹顺,所以mutation就會被定義的越來越多。

方法多了之后廊遍,使用者在使用這些Mutation的時候,直接寫嘛贩挣,容易單詞拼寫錯誤喉前,返回去復制嘛又浪費時間,這時候就可以使用常量來替代Mutation的事件類型王财,這樣就方便管理這些事件類型了卵迂,而且還能讓整個項目的事件類型一目了然,就是沒有了業(yè)務代碼摻雜在一起了绒净。

具體怎么做呢见咒?

  • 創(chuàng)建一個文件:mutation-types.js并且在其中定義我們需要使用的事件類型常量

  • 在使用這些常量命名函數是可以這樣來使用

    [常量]() {

    ? 函數體

    }

  • mutation-types.js

export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const SUB_FIVE = 'subFive'
export const A_ADD_HEIGHT = 'aAddHeight'
  • mutations
import * as types from './mutation-types.js'
...
mutations:{
    [types.INCREMENT](state) {
      state.count++;
    },
    [types.DECREMENT](state) {
      state.count--;
    },
    [types.SUB_FIVE](state,payload) {
      state.count -= payload.count;
    },
    [types.A_ADD_HEIGHT](state,payload) {
      state.author = {...state.author,'height':payload.height}
    }
  },
  • 使用
import {A_ADD_HEIGHT} from 'store/mutation-types.js'
methods: {
    addHeight(){
      this.$store.commit({
        type:A_ADD_HEIGHT,
        height:1.88
      })
    }
  },
  • mutations同步函數

    若是想要使用devtools的話,還有一個要求挂疆,就是在Mutation中的方法必須是同步方法改览,如果在Mutation中又異步操作的話,devtools將不能很好的追蹤這個操作什么時候會被完成缤言。

    [types.A_ADD_HEIGHT](state,payload) {
          //使用setTimeout測試異步操作
          setTimeout(
            () => {
              state.author = {...state.author,'height':payload.height}
            },1000);
        }
    

你會發(fā)現宝当,雖然頁面改變了,說明state的值已經修改了胆萧,但是devtools中的author對象卻沒有追加一個height屬性庆揩,因為它沒有追蹤到mutation的完成。所以不要在mutation中進行異步操作跌穗。

  • Actions

    上面我們說到在mutation中不能進行異步操作订晌,那如果我們確實不能再Vuex中進行一些異步操作怎么辦呢,比如說一些網絡請求蚌吸。這個時候就可以使用Vuex圖例中的Action了.

    Action和Mutation類似锈拨,是用來代替Mutation進行異步操作的。Mutation默認會傳入state,可以傳入payload套利。而Action默認會傳入context推励,上下文對象鹤耍,具有和store對象相同的方法和屬性。不過他們并不是同一個對象验辞。

    • 基本使用

      /*********定義**************/
      actions:{
          [types.A_ADD_HEIGHT](context,payload) {
            setTimeout(() => context.commit({
              type:types.A_ADD_HEIGHT,
              height:payload.height
            }),3000);
          }
        }
      /********使用*********/
      methods: {
          addHeight(){
            this.$store.dispatch(A_ADD_HEIGHT,{
              height:1.88
            });
          }
        },
      

      Action是使用dispatch來使用的稿黄,和Mutation一樣可以攜帶一個對象payload

      //也有這種使用風格
      this.$store.dispatch({
              type:A_ADD_HEIGHT,
              height:1.88
            });
      
  • Action返回Promise

有時我們希望再進行網絡請求成功之后進行某些操作,那么我們如何直到異步操作已經結束了呢跌造,我們可以讓Action返回一個Promise杆怕。然后在組件中then,這樣在異步操作完成之后調用resolve然后就會執(zhí)行then中的代碼壳贪。

/***************定義*************/
actions:{
    [types.A_ADD_HEIGHT](context,payload) {
      return new Promise((resolve,reject) => {
        setTimeout(() => context.commit({
          type:types.A_ADD_HEIGHT,
          height:payload.height
        }),3000);
        resolve("網絡請求已經完成了");
      })
    }
  }
/***************使用*************/
methods: {
    addHeight(){
      this.$store.dispatch({
        type:A_ADD_HEIGHT,
        height:1.88
      }).then(res => {
        console.log(res);
      })
    }
  },
  • Modules

    • 認識Module

      官方說法:

      由于使用單一狀態(tài)樹陵珍,應用的所有狀態(tài)會集中到一個比較大的對象。當應用變得非常復雜時违施,store 對象就有可能變得相當臃腫互纯。

      為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)磕蒲。每個模塊擁有自己的 state留潦、mutation、action辣往、getter兔院、甚至是嵌套子模塊——從上至下進行同樣方式的分割

      const moduleA = {
        state:{
          count:7
        }
      }
      const moduleB = {
        state:{
          count:5
        }
      }
      //創(chuàng)建store對象
      const store = new Vuex.Store({
          state:{
            count:0  
          },
        modules:{
          a:moduleA,
          b:moduleB
        }
      })
      

      通過$store.state.a的方式拿到moduleA的state

      通過$store.state.b的方式拿到moduleB的state

      雖然我們定義的store里的state里沒有a,但是它最終會把Modules里的東西放到store的state里面的站削。

  • 默認情況下的Modules

    默認情況下坊萝,也就是上面這種定義Modules的方式,這些模塊是被注冊在全局命名空間里的许起,這就相當于只是將原本龐大的store對象給切割了十偶,分開保存而已,模塊和store對象之間還是非常緊密聯(lián)系的街氢。

    這種情況下扯键,所有模塊中定義的Mutations和Actions都會接收來自全局commit的事件類型。就像是他們還像原來那樣定義一樣珊肃,不過這時候Mutation和getter接收的第一個參數是模塊的局部狀態(tài)對象荣刑,就是哪個模塊的Mutation和getter就針對這個模塊進行操作,這樣子使得我們更好的對狀態(tài)進行管理伦乔。

    const moduleA = {
      state:{
        count:0
      },
      mutations:{
        increment(state){
          state.count++
        }
      }
    }
    const moduleB = {
      state:{
        count:0
      },
      mutations:{
        increment(state){
          state.count++
        }
      }
    }
    //2.創(chuàng)建store對象并且導出
    export default new Vuex.Store({
      state:{
        count:0
      },
      mutations:{
        increment(state){
          state.count++
        }
      },
      modules:{
        a:moduleA,
        b:moduleB
      }
    });
    

    這時候提交一個increment事件厉亏,三個mutation都會收到這個事件類型,然后做出操作烈和。如果在模塊中定義了相同名字的getter會直接報錯的爱只,說getter重復了。

    對于模塊內部的getter招刹,根節(jié)點狀態(tài)會作為第三個參數暴露出來

    getter(state,getters,rootState)

    對于模塊內部的action恬试,它接收的第一個參數是依然是上下文對象context窝趣,這個對象中有這些屬性。

里面的commit,dispatch,getters都是全局的训柴,state是局部的



所以根據上面的探討哑舒,結論是,在使用這種默認的Mudules來進行分模塊狀態(tài)管理的時候幻馁,不要出現重名的Mutation,getter,Action
  • 給模塊添加上命名空間

    如果的確是想要使用重名的Mutation,getter,Action的話洗鸵,那就得將單個的模塊給封裝起來了,就是給他加上命名空間仗嗦,添加一個屬性namespaced:true,以后它里面的狀態(tài)管理膘滨,提交mutation就得這樣commit('a/xxxx'),其中a是模塊名,xxxx是事件類型稀拐。

  • 文件目錄的組織

    官方文檔有這么一句話:

    Vuex 并不限制你的代碼結構火邓。但是,它規(guī)定了一些需要遵守的規(guī)則:

    1. 應用層級的狀態(tài)應該集中到單個 store 對象中德撬。
    2. 提交 mutation 是更改狀態(tài)的唯一方法贡翘,并且這個過程是同步的。
    3. 異步邏輯都應該封裝到 action 里面砰逻。

    只要你遵守以上規(guī)則,如何組織代碼隨你便泛鸟。如果你的 store 文件太大蝠咆,只需將 action、mutation 和 getter 分割到單獨的文件北滥。

    對于大型應用刚操,我們會希望把 Vuex 相關代碼分割到模塊中。下面是項目結構示例

Vuex推薦我們這樣來組織文件目錄

├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我們組裝模塊并導出 store 的地方
├── actions.js # 根級別的 action
├── mutations.js # 根級別的 mutation
└── modules
├── cart.js # 購物車模塊
└── products.js # 產品模塊

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末再芋,一起剝皮案震驚了整個濱河市菊霜,隨后出現的幾起案子,更是在濱河造成了極大的恐慌济赎,老刑警劉巖鉴逞,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異司训,居然都是意外死亡构捡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門壳猜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勾徽,“玉大人,你說我怎么就攤上這事统扳〈悖” “怎么了畅姊?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吹由。 經常有香客問我若未,道長,這世上最難降的妖魔是什么溉知? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任陨瘩,我火速辦了婚禮,結果婚禮上级乍,老公的妹妹穿的比我還像新娘舌劳。我一直安慰自己,他們只是感情好玫荣,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布甚淡。 她就那樣靜靜地躺著,像睡著了一般捅厂。 火紅的嫁衣襯著肌膚如雪贯卦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天焙贷,我揣著相機與錄音撵割,去河邊找鬼。 笑死辙芍,一個胖子當著我的面吹牛啡彬,可吹牛的內容都是我干的。 我是一名探鬼主播故硅,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼庶灿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吃衅?” 一聲冷哼從身側響起往踢,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎徘层,沒想到半個月后峻呕,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡惑灵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年山上,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片英支。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡佩憾,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情妄帘,我是刑警寧澤楞黄,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站抡驼,受9級特大地震影響鬼廓,放射性物質發(fā)生泄漏。R本人自食惡果不足惜致盟,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一碎税、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧馏锡,春花似錦雷蹂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至党巾,卻和暖如春萎庭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背齿拂。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工驳规, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人署海。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓达舒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叹侄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359