Vue學(xué)習(xí)筆記進(jìn)階篇——vuex核心概念

本文為轉(zhuǎn)載,原文:Vue學(xué)習(xí)筆記進(jìn)階篇——vuex核心概念

前言

本文將繼續(xù)上一篇 vuex文章 著榴,來詳細(xì)解讀一下vuex的核心概念啤挎,states, getters, mutations, actions, mudoles.

State

單一狀態(tài)樹

Vuex 使用的是單一狀態(tài)樹饲嗽,用一個(gè)對(duì)象就包含了全部的應(yīng)用層級(jí)狀態(tài)炭玫。至此它便作為一個(gè)『唯一數(shù)據(jù)源(SSOT)』而存在。這也意味著喝噪,每個(gè)應(yīng)用將僅僅包含一個(gè) store實(shí)例础嫡。單一狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段,在調(diào)試的過程中也能輕易地取得整個(gè)當(dāng)前應(yīng)用狀態(tài)的快照酝惧。

單狀態(tài)樹和模塊化并不沖突 —— 在后面的章節(jié)里我們會(huì)討論如何將狀態(tài)和狀態(tài)變更事件分布到各個(gè)子模塊中榴鼎。

在 Vue 組件中獲得 Vuex 狀態(tài)

那么我們?nèi)绾卧?Vue 組件中展示狀態(tài)呢?由于 Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的晚唇,從 store 實(shí)例中讀取狀態(tài)最簡(jiǎn)單的方法就是在計(jì)算屬性中返回某個(gè)狀態(tài):

    computed:{
        count(){
            return this.$store.state.count
        },
    }

每當(dāng) this.$store.state.count 變化的時(shí)候, 都會(huì)重新求取計(jì)算屬性巫财,并且觸發(fā)更新相關(guān)聯(lián)的 DOM。

然而哩陕,這種模式導(dǎo)致組件依賴的全局狀態(tài)單例平项。在模塊化的構(gòu)建系統(tǒng)中赫舒,在每個(gè)需要使用 state 的組件中需要頻繁地導(dǎo)入,并且在測(cè)試組件時(shí)需要模擬狀態(tài)闽瓢。

而在vue組件中獲得vuex狀態(tài)的時(shí)候接癌,我們更多的是使用mapState輔助函數(shù),因?yàn)樗褂闷饋砗芎?jiǎn)單扣讼,也很方便缺猛。

mapState輔助函數(shù)

當(dāng)一個(gè)組件需要獲取多個(gè)狀態(tài)時(shí)候,將這些狀態(tài)都聲明為計(jì)算屬性會(huì)有些重復(fù)和冗余椭符。為了解決這個(gè)問題荔燎,我們可以使用 mapState輔助函數(shù)幫助我們生成計(jì)算屬性,讓你少按幾次鍵:

// 在單獨(dú)構(gòu)建的版本中輔助函數(shù)為 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 箭頭函數(shù)可使代碼更簡(jiǎn)練
    count: state => state.count,

    // 傳字符串參數(shù) 'count' 等同于 `state => state.count`
    countAlias: 'count',

    // 為了能夠使用 `this` 獲取局部狀態(tài)销钝,必須使用常規(guī)函數(shù)
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

當(dāng)映射的計(jì)算屬性的名稱與 state 的子節(jié)點(diǎn)名稱相同時(shí)有咨,我們也可以給 mapState 傳一個(gè)字符串?dāng)?shù)組。

computed: mapState([
  // 映射 this.count 為 store.state.count
  'count'
])

對(duì)象展開運(yùn)算符

mapState函數(shù)返回的是一個(gè)對(duì)象蒸健。我們?nèi)绾螌⑺c局部計(jì)算屬性混合使用呢座享?通常,我們需要使用一個(gè)工具函數(shù)將多個(gè)對(duì)象合并為一個(gè)似忧,以使我們可以將最終對(duì)象傳給 computed 屬性征讲。但是自從有了對(duì)象展開運(yùn)算符(現(xiàn)處于 ECMASCript 提案 stage-3 階段),我們可以極大地簡(jiǎn)化寫法:

    computed:{
        count(){
            return this.$store.state.count
        },
      ...mapState(['count']),
      ...mapState({
        countAlias: 'count',
        countPlusLocalState (state) {
          return state.count + this.localCount
        }
      })
    }

寫法是多種的橡娄,具體怎么寫,要看自己的喜好好需求了癣籽。

組件仍然保有局部狀態(tài)

使用 Vuex 并不意味著你需要將所有的狀態(tài)放入 Vuex挽唉。雖然將所有的狀態(tài)放到 Vuex 會(huì)使?fàn)顟B(tài)變化更顯式和易調(diào)試,但也會(huì)使代碼變得冗長(zhǎng)和不直觀筷狼。如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件瓶籽,最好還是作為組件的局部狀態(tài)。你應(yīng)該根據(jù)你的應(yīng)用開發(fā)需要進(jìn)行權(quán)衡和確定埂材。

Getters

有時(shí)候我們需要從store 中的state中派生出一些狀態(tài)塑顺,例如對(duì)列表進(jìn)行過濾并計(jì)數(shù):

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}

如果有多個(gè)組件需要用到此屬性,我們要么復(fù)制這個(gè)函數(shù)俏险,或者抽取到一個(gè)共享函數(shù)然后在多處導(dǎo)入它 —— 無論哪種方式都不是很理想严拒。

Vuex 允許我們?cè)?code>store中定義『getters』(可以認(rèn)為是 store計(jì)算屬性)。Getters接受 state 作為其第一個(gè)參數(shù):

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

Getters會(huì)暴露為 store.getters 對(duì)象:

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Getters也可以接受其他 getters 作為第二個(gè)參數(shù):

getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}
store.getters.doneTodosCount // -> 1

我們可以很容易地在任何組件中使用它:

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

mapGetters 輔助函數(shù)

mapGetters輔助函數(shù)僅僅是將store中的 getters映射到局部計(jì)算屬性:

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用對(duì)象展開運(yùn)算符將 getters 混入 computed 對(duì)象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

如果你想將一個(gè) getter 屬性另取一個(gè)名字竖独,使用對(duì)象形式:

mapGetters({
  // 映射 this.doneCount 為 store.getters.doneTodosCount
  doneCount: 'doneTodosCount'
})

其實(shí)裤唠,你會(huì)發(fā)現(xiàn),mapGetter的作用和mapState是一樣的莹痢。語法也是一致的种蘸。

Mutations

更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation墓赴。Vuex 中的 mutations 非常類似于事件:每個(gè) mutation 都有一個(gè)字符串的 事件類型 (type)和 一個(gè)回調(diào)函數(shù) (handler)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方航瞭,并且它會(huì)接受 state 作為第一個(gè)參數(shù):

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態(tài)
      state.count++
    }
  }
})

你不能直接調(diào)用一個(gè) mutation handler诫硕。這個(gè)選項(xiàng)更像是事件注冊(cè):“當(dāng)觸發(fā)一個(gè)類型為 increment 的 mutation 時(shí),調(diào)用此函數(shù)刊侯≌掳欤”要喚醒一個(gè) mutation handler,你需要以相應(yīng)的 type 調(diào)用store.commit方法:

store.commit('increment')

提交載荷(Payload)

你可以向 store.commit傳入額外的參數(shù)滔吠,即 mutation 的 載荷(payload)

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

在大多數(shù)情況下纲菌,載荷應(yīng)該是一個(gè)對(duì)象,這樣可以包含多個(gè)字段并且記錄的 mutation 會(huì)更易讀:

// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
store.commit('increment', {
  amount: 10
})

對(duì)象風(fēng)格的提交方式

提交 mutation 的另一種方式是直接使用包含 type 屬性的對(duì)象:

store.commit({
  type: 'increment',
  amount: 10
})

當(dāng)使用對(duì)象風(fēng)格的提交方式疮绷,整個(gè)對(duì)象都作為載荷傳給 mutation 函數(shù)翰舌,因此 handler 保持不變:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

Mutations 需遵守 Vue 的響應(yīng)規(guī)則

既然 Vuex 的 store 中的狀態(tài)是響應(yīng)式的,那么當(dāng)我們變更狀態(tài)時(shí)冬骚,監(jiān)視狀態(tài)的 Vue 組件也會(huì)自動(dòng)更新椅贱。這也意味著 Vuex 中的 mutation 也需要與使用 Vue 一樣遵守一些注意事項(xiàng):

  1. 最好提前在你的 store 中初始化好所有所需屬性。
  2. 當(dāng)需要在對(duì)象上添加新屬性時(shí)只冻,你應(yīng)該
  • 使用 Vue.set(obj, 'newProp', 123), 或者 -
  • 以新對(duì)象替換老對(duì)象庇麦。例如,利用 stage-3 的對(duì)象展開運(yùn)算符我們可以這樣寫:
state.obj = { ...state.obj, newProp: 123 }

使用常量替代 Mutation 事件類型

使用常量替代 mutation 事件類型在各種 Flux 實(shí)現(xiàn)中是很常見的模式喜德。這樣可以使 linter 之類的工具發(fā)揮作用山橄,同時(shí)把這些常量放在單獨(dú)的文件中可以讓你的代碼合作者對(duì)整個(gè) app 包含的 mutation 一目了然:

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'

// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // 我們可以使用 ES2015 風(fēng)格的計(jì)算屬性命名功能來使用一個(gè)常量作為函數(shù)名
    [SOME_MUTATION] (state) {
      // mutate state
    }
  }
})

用不用常量取決于你 —— 在需要多人協(xié)作的大型項(xiàng)目中恨狈,這會(huì)很有幫助衩婚。但如果你不喜歡礁鲁,你完全可以不這樣做胶台。

mutation 必須是同步函數(shù)

一條重要的原則就是要記住 mutation 必須是同步函數(shù)污尉。為什么姜盈?請(qǐng)參考下面的例子:

mutations: {
  someMutation (state) {
    api.callAsyncMethod(() => {
      state.count++
    })
  }
}

現(xiàn)在想象硕蛹,我們正在 debug 一個(gè) app 并且觀察 devtool 中的 mutation 日志浪漠。每一條 mutation 被記錄秕豫,devtools 都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照朴艰。然而,在上面的例子中 mutation 中的異步函數(shù)中的回調(diào)讓這不可能完成:因?yàn)楫?dāng) mutation 觸發(fā)的時(shí)候混移,回調(diào)函數(shù)還沒有被調(diào)用祠墅,devtools 不知道什么時(shí)候回調(diào)函數(shù)實(shí)際上被調(diào)用 —— 實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的的狀態(tài)的改變都是不可追蹤的。

在組件中提交 Mutations

你可以在組件中使用 this.$store.commit('xxx')提交 mutation沫屡,或者使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為store.commit 調(diào)用(需要在根節(jié)點(diǎn)注入 store)饵隙。

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // 映射 this.increment() 為 this.$store.commit('increment')
    ]),
    ...mapMutations({
      add: 'increment' // 映射 this.add() 為 this.$store.commit('increment')
    })
  }
}

Action

Action 類似于 mutation,不同在于:

Action 提交的是 mutation沮脖,而不是直接變更狀態(tài)金矛。
Action 可以包含任意異步操作芯急。

讓我們來注冊(cè)一個(gè)簡(jiǎn)單的 action:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

Action函數(shù)接受一個(gè)與store實(shí)例具有相同方法和屬性的 context對(duì)象,因此你可以調(diào)用 context.commit 提交一個(gè) mutation驶俊,或者通過 context.statecontext.getters 來獲取 stategetters娶耍。當(dāng)我們?cè)谥蠼榻B到 Modules時(shí),你就知道 context 對(duì)象為什么不是 store 實(shí)例本身了饼酿。
上面的action注冊(cè)也可簡(jiǎn)寫為這樣

actions: {
  increment ({ commit }) {
    commit('increment')
  }
}

分發(fā) Action

Action 通過 store.dispatch 方法觸發(fā):

store.dispatch('increment')

乍一眼看上去感覺多此一舉榕酒,我們直接分發(fā) mutation 豈不更方便?實(shí)際上并非如此故俐,還記得 mutation 必須同步執(zhí)行這個(gè)限制么想鹰?Action 就不受約束!我們可以在 action 內(nèi)部執(zhí)行異步操作:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

Actions 支持同樣的載荷方式對(duì)象方式進(jìn)行分發(fā):

// 以載荷形式分發(fā)
store.dispatch('incrementAsync', {
  amount: 10
})

// 以對(duì)象形式分發(fā)
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

在組件中分發(fā) Action

你在組件中使用 this.$store.dispatch('xxx')分發(fā)action药版,或者使用 mapActions 輔助函數(shù)將組件的 methods映射為 store.dispatch調(diào)用(需要先在根節(jié)點(diǎn)注入 store):

import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment' // 映射 this.increment() 為 this.$store.dispatch('increment')
    ]),
    ...mapActions({
      add: 'increment' // 映射 this.add() 為 this.$store.dispatch('increment')
    })
  }
}

Modules

由于使用單一狀態(tài)樹辑舷,應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對(duì)象。當(dāng)應(yīng)用變得非常復(fù)雜時(shí)槽片,store 對(duì)象就有可能變得相當(dāng)臃腫何缓。

為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)还栓。每個(gè)模塊擁有自己的state碌廓、mutationaction剩盒、getter谷婆、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)

模塊的局部狀態(tài)

對(duì)于模塊內(nèi)部的 mutation 和 getter,接收的第一個(gè)參數(shù)是模塊的局部狀態(tài)對(duì)象辽聊。

const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // 這里的 `state` 對(duì)象是模塊的局部狀態(tài)
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}

同樣波材,對(duì)于模塊內(nèi)部的 action,局部狀態(tài)通過 context.state 暴露出來身隐, 根節(jié)點(diǎn)狀態(tài)則為 context.rootState

const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit('increment')
      }
    }
  }
}

對(duì)于模塊內(nèi)部的getter,根節(jié)點(diǎn)狀態(tài)會(huì)作為第三個(gè)參數(shù)暴露出來:

const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}

項(xiàng)目結(jié)構(gòu)

Vuex 并不限制你的代碼結(jié)構(gòu)唯灵。但是贾铝,它規(guī)定了一些需要遵守的規(guī)則:

  1. 應(yīng)用層級(jí)的狀態(tài)應(yīng)該集中到單個(gè) store 對(duì)象中。
  2. 提交 mutation 是更改狀態(tài)的唯一方法埠帕,并且這個(gè)過程是同步的垢揩。
  3. 異步邏輯都應(yīng)該封裝到 action 里面。

只要你遵守以上規(guī)則敛瓷,如何組織代碼隨你便叁巨。如果你的 store文件太大,只需將action呐籽、mutation锋勺、和 getters分割到單獨(dú)的文件蚀瘸。

對(duì)于大型應(yīng)用,我們會(huì)希望把 Vuex 相關(guān)代碼分割到模塊中庶橱。下面是項(xiàng)目結(jié)構(gòu)示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請(qǐng)求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js    # 我們組裝模塊并導(dǎo)出 store 的地方
    ├── actions.js   # 根級(jí)別的 action
    ├── mutations.js      # 根級(jí)別的 mutation
    └── modules
        ├── cart.js       # 購(gòu)物車模塊
        └── products.js   # 產(chǎn)品模塊

本文為原創(chuàng)贮勃,轉(zhuǎn)載請(qǐng)注明出處

上一節(jié):Vue學(xué)習(xí)筆記進(jìn)階篇——vuex安裝及使用
返回目錄
下一節(jié):Vue學(xué)習(xí)筆記實(shí)戰(zhàn)篇——音樂播放器 · 需求說明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市苏章,隨后出現(xiàn)的幾起案子寂嘉,更是在濱河造成了極大的恐慌,老刑警劉巖枫绅,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泉孩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡并淋,警方通過查閱死者的電腦和手機(jī)寓搬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來预伺,“玉大人订咸,你說我怎么就攤上這事〕昃鳎” “怎么了脏嚷?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瞒御。 經(jīng)常有香客問我父叙,道長(zhǎng),這世上最難降的妖魔是什么肴裙? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任趾唱,我火速辦了婚禮,結(jié)果婚禮上蜻懦,老公的妹妹穿的比我還像新娘甜癞。我一直安慰自己,他們只是感情好宛乃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布悠咱。 她就那樣靜靜地躺著,像睡著了一般征炼。 火紅的嫁衣襯著肌膚如雪析既。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天谆奥,我揣著相機(jī)與錄音眼坏,去河邊找鬼。 笑死酸些,一個(gè)胖子當(dāng)著我的面吹牛宰译,可吹牛的內(nèi)容都是我干的檐蚜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼囤屹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼熬甚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肋坚,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤乡括,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后智厌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诲泌,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年铣鹏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敷扫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诚卸,死狀恐怖葵第,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情合溺,我是刑警寧澤卒密,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站棠赛,受9級(jí)特大地震影響哮奇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睛约,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一鼎俘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辩涝,春花似錦贸伐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至沧踏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巾钉,已是汗流浹背翘狱。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砰苍,地道東北人潦匈。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓阱高,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親茬缩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赤惊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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