Vue.js 1.0 Official Guide Note

Vue 實例

屬性和方法

  • 每個 Vue 實例都會代理data 對象里所有的屬性:

    var data = { a: 1 }
    var vm = new Vue({
      data: data
    })
    vm.a === data.a // -> true
    // 設置屬性也會影響到原始數據
    vm.a = 2
    data.a // -> 2
    // ... 反之亦然
    data.a = 3
    vm.a // -> 3
    
  • 只有被代理的屬性是響應的。在實例創(chuàng)建之后添加新的屬性到實例上盏求,它不會觸發(fā)視圖更新失仁。

  • Vue 實例暴露了一些有用的實例屬性和方法祈惶。這些屬性和方法都有前綴 $ ,以便于代理的數據屬性區(qū)分俏拱。

實例生命周期

  • Vue 實例在其生命周期的不同階段會調用不同的鉤子,如 createdcompiled 健民、 readydestroy 贫贝。鉤子的 this 指向調用它的 Vue 實例秉犹。

    var vm = new Vue({
      data: {
        a: 1
      },
      created: function () {
        // `this` 指向 vm 實例
        console.log('a is: ' + this.a)
      }
    })
    // -> "a is: 1"
    
  • 生命周期圖示


數據綁定語法

插值

文本

  • 處理單次插值,今后的數據變化不引起插值更新稚晚。

    <span>This will never change: {{* msg }}</span>
    

原始的 HTML

  • 使用三 Mustache 標簽輸出真的 HTML 字符串崇堵。

    <div>{{{ raw_html }}}</div>
    
  • 內容以 HTML 字符串插入時,**數據綁定將被忽略 **客燕。如果要復用模板片段鸳劳,應當使用 partials

  • 只對可信內容使用 HTML 插值,永不用于用戶提交的內容也搓。

HTML 特性

  • Mustache 標簽也可以用在 HTML 特性 (attribute) 內赏廓。

    <div id="item-{{ id }}"></div>
    
  • Vue.js 指令和特殊特性內不能用插值。

綁定表達式

JavaScript 表達式

  • Vue.js 在數據綁定內支持全功能的 JavaScript 表達式还绘。表達式將在 Vue 實例的作用域內計算楚昭。
  • 每個綁定只能包含單個表達式

過濾器

  • Vue.js 允許在表達式后添加可選的“過濾器”拍顷,以 |(管道符)指示抚太。

    {{ message | capitalize }}
    
  • 管道語法不是 JavaScript 語法,不能在表達式內使用過濾器昔案,只能添加到表達式后邊尿贫。

  • 過濾器可以串聯(lián),也可以接受參數踏揣。過濾器函數始終以表達式的值作為第一個參數庆亡。

    {{ message | filterA | filterB }}
    {{ message | filterA 'arg2' arg3 }}
    

指令

修飾符

  • 修飾符是以半角句號 . 開始的特殊后綴,用于表示指令應當以特殊方式綁定捞稿。

    <a v-bind:href.literal="/a/b/c"></a>
    

Vue.js 為兩個最常用的指令 v-bindv-on 提供特別版的縮寫:

<!-- v-bind -->
<a v-bind:href="url"></a>
<a :href="url"></a>
<!-- v-on -->
<a v-on:click="doSomething"></a>
<a @click="doSomething"></a>

計算屬性

  • 在模板中要使用多余一個表達式的邏輯時又谋,應當使用計算屬性拼缝。

基礎例子

var vm = new Vue({
  el: '#example',
  data: {
    a: 1
  },
  computed: {
    // 一個計算屬性的 getter
    b: function () {
      // `this` 指向 vm 實例
      return this.a + 1
    }
  }
})
<div id="example">
  a={{ a }}, b={{ b }}
</div>

計算 setter

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...
  • 規(guī)定 setter 之后,調用 vm.fullName = 'John Doe' 時彰亥,setter 會被調用咧七,vm.firstNamevm.lastName 也會有相應更新。

Class 與 Style 綁定

  • v-bind 用于 classstyle 時任斋,Vue.js 專門增強了它继阻。表達式的結果類型除了字符串之外,還可以是對象或數組废酷。

綁定 HTML Class

  • class="{{ className }}"v-bind:class 的用法瘟檩,二者只能選其一。

對象語法

  • 傳遞給 v-bind:class 一個對象澈蟆,以動態(tài)地切換 class 墨辛。注意 v-bind:class 指令可以與普通的 class 特性共存。

    <div class="static" v-bind:class="{ 'class-a': isA, 'class-b': isB }"></div>
    
    data: {
      isA: true,
      isB: false
    }
    

    渲染為

    <div class="static class-a"></div>
    
  • 也可以直接綁定數據里的一個對象趴俘。

數組語法

  • 把一個數組傳遞給 v-bind:class 背蟆,以應用一個 class 列表。

    <div v-bind:class="[classA, classB]">
    
    data: {
      classA: 'class-a',
      classB: 'class-b'
    }
    

    渲染為

    <div class="class-a class-b"></div>
    

    如果需要根據條件切換列表中的 class 哮幢,可以使用三元表達式带膀。

    <div v-bind:class="[classA, isB ? classB : '']">
    

    在 1.0.19+ 中,可以在數組語法中使用對象語法橙垢。

    <div v-bind:class="[classA, { classB: isB, classC: isC }]">
    

綁定內聯(lián)樣式

對象語法

  • v-bind:style 是一個 JavaScript 對象垛叨。其中的 CSS 屬性名可以用駝峰式,也可以用短橫分割命名柜某。不過嗽元,更推薦直接綁定一個樣式對象

    <div v-bind:style="styleObject"></div>
    
    data: {
        styleObject: {
        color: 'red',
        fontSize: '13px'
        }
    }
    

數組語法

  • v-bind:style 的數組語法還可以將多個樣式對象應用到一個元素上。

    <div v-bind:style="[styleObjectA, styleObjectB]">
    

自動添加前綴

  • Vue.js 會自動為需要添加廠商前綴的 CSS 屬性添加廠商前綴喂击。

條件渲染

v-if

  • 使用 v-if 控制元素切換剂癌。

    <h1 v-if="ok">Yes</h1>
    <h1 v-else>No</h1>
    

template v-if

  • 使用 template 元素當做包裝元素,并在上邊使用 v-if翰绊,實現多個元素的切換佩谷。最終渲染結果不會包含這個元素。

    <template v-if="ok">
        <h1>Title</h1>
        <p>Paragraph 1</p>
        <p>Paragraph 2</p>
    </template>
    

v-show

  • v-showv-if 用法大體一樣监嗜,不同的是有 v-show 的元素會始終渲染并保持在 DOM 中?谐檀,其變化只是簡單地切換元素的 CSS 屬性 display
  • v-show 不支持 <template> 語法裁奇。

組件警告

  • v-show 用在組件上時桐猬,因為指令的優(yōu)先級 v-else 會出現問題。因此應當使用另一個 v-show 替換 v-else 刽肠。

    <custom-component v-show="condition"></custom-component>
    <p v-show="!condition">這可能也是一個組件</p>
    

v-if vs. v-show

  • v-if 是真實的條件渲染溃肪,因為它會確保條件塊在切換當中合適地銷毀與重建條件塊內的時間監(jiān)聽器和子組件免胃。
  • v-if 具有惰性。如果在初始渲染時條件為假惫撰,則什么也不做杜秸;在條件第一次變?yōu)檎鏁r才開始局部編譯。相比之下 v-show 元素始終被編譯并保留润绎。
  • 一般來說,v-if 有更高的切換消耗而 v-show 有更高的初始渲染消耗诞挨。因此莉撇,如果需要頻繁切換 v-show 較好惶傻,如果在運行時條件不大可能改變 v-if 較好棍郎。

列表渲染

v-for

  • v-for 塊內我們能完全訪問父組件作用域內的屬性,另有一個特殊變量 $index 作為當前數組元素的索引(從 0 開始)银室。

    <ul id="example-2">
        <li v-for="item in items">
        {{ parentMessage }} - {{ $index }} - {{ item.message }}
        </li>
    </ul>
    
  • 可以為索引指定一個別名(如果 v-for 用于一個對象涂佃,則可以為對象的鍵指定一個別名)

    <div v-for="(index, item) in items">
        {{ index }} {{ item.message }}
    </div>
    
  • 從 1.0.17 開始可以使用 of 替代 in 以更接近 JavaScript 遍歷器的語法。

template v-for

  • 類似于 template v-if 蜈敢,也可以將 v-for 用在 <template> 標簽上辜荠,以渲染一個包含多個元素的塊。

    <ul>
        <template v-for="item in items">
            <li>{{ item.msg }}</li>
            <li class="divider"></li>
        </template>
    </ul>
    

數組變動檢測

track-by

  • 有時需要用全新對象(例如通過 API 調用創(chuàng)建的對象)替換數組抓狭。因為 v-for 默認通過數據對象的特征來決定對已有作用域和 DOM 元素的復用程度伯病,這可能導致重新渲染整個列表。但是否过,如果每個對象都有一個唯一 ID 的屬性午笛,便可以使用 track-by 特性給 Vue.js 一個提示,Vue.js 因而能盡可能地復用已有實例苗桂。
    假定數據為:

    {
        items: [
            { _uid: '88f869d', ... },
            { _uid: '7496c10', ... }
        ]
    }
    

    然后可以這樣給出提示:

    <div v-for="item in items" track-by="_uid">
        <!-- content -->
    </div>
    

    然后在替換數組 items 時药磺,如果 Vue.js 遇到一個包含 _uid: '88f869d' 的新對象,它知道它可以復用這個已有對象的作用域與 DOM 元素煤伟。

track-by $index

  • 如果沒有唯一的鍵供追蹤癌佩,可以使用 track-by="$index" ,它強制讓 v-for 進入原位更新模式:片段不會被移動便锨,而是簡單地以對應索引的新值刷新驼卖。這種模式也能處理數組中重復的值。
  • 這樣做的代價是:DOM 節(jié)點不會再映射數組元素順序的改變鸿秆,不能同步臨時狀態(tài)(比如 <input> 元素的值)以及組件的私有狀態(tài)酌畜。因此**如果 v-for 塊包含 <input> 元素或子組件,要小心使用 track-by="$index" 卿叽。

問題

  • 因為 JavaScript 的限制桥胞,Vue.js 不能檢測到下面數組的變化:

    1. 直接用索引設置元素恳守,如 vm.items[0] = {}
    2. 修改數據的長度贩虾,如 vm.items.length = 0催烘。
  • 解決問題 1 ,使用 Vue.js 為觀察數組擴展的 $set() 方法缎罢。

    // 與 `example1.items[0] = ...` 相同伊群,但是能觸發(fā)視圖更新
    example1.items.$set(0, { childMsg: 'Changed!'})
    
  • 解決問題 2 ,使用一個空數組替換 items 即可策精。

  • Vue.js 同時為觀察數組擴展了 $remove() 方法舰始,用于從目標數組中查找并刪除元素,而無需使用 splice()咽袜。

    this.items.$remove(item)
    
  • 在遍歷一個數組時,如果數組元素是對象并且對象用 Object.freeze() 凍結询刹,要明確指定 track-by 谜嫉。在這種情況下如果 Vue.js 不能自動追蹤對象,將會給出一條警告凹联。

對象 v-for

  • 可以使用 v-for 遍歷對象沐兰。除了 $index 之外,作用域內還可以訪問另外一個特殊變量 $key 蔽挠。

    <ul id="repeat-object" class="demo">
        <li v-for="value in object">
            {{ $key }} : {{ value }}
        </li>
    </ul>
    
    new Vue({
        el: '#repeat-object',
        data: {
            object: {
                FirstName: 'John',
                LastName: 'Doe',
                Age: 30
            }
        }
    })
    

    也可以給對象的鍵提供一個別名:

    <div v-for="(key, val) in object">
        {{ key }} {{ val }}
    </div>
    
  • 遍歷對象使用 Object.keys() 的結果遍歷僧鲁,但并不能保證它的結果在不同的 JavaScript 引擎下一致。

值域 v-for

  • v-for 可以接收一個整數象泵,此時它將重復模板數次寞秃。

    <div>
        <span v-for="n in 10">{{ n }} </span>
    </div>
    

顯示過濾 / 排序的結果

  • 創(chuàng)建一個計算屬性,返回過濾 / 排序過的數組偶惠。

  • 使用內置過濾器: filterByorderBy (詳見 API)春寿。

    <div v-for="item in items | filterBy 'hello'">
    
    <ul>
        <li v-for="user in users | orderBy 'name'">
            {{ user.name }}
        </li>
    </ul>
    

方法與事件處理器

方法處理器

<div id="example">
    <button v-on:click="greet">Greet</button>
</div>
  • event 是原生 DOM 事件,使用 event.target 訪問 DOM 忽孽。

內聯(lián)語句處理器

<div id="example-2">
    <button v-on:click="say('hi')">Say Hi</button>
    <button v-on:click="say('what')">Say What</button>
</div>
new Vue({
  el: '#example-2',
  methods: {
    say: function (msg) {
      alert(msg)
    }
  }
})
  • 如果需要在內聯(lián)語句中訪問原生 DOM 事件绑改,可以使用特殊變量 $event

    <button v-on:click="say('hello!', $event)">Submit</button>
    
    methods: {
        say: function (msg, event) {
            // 現在我們可以訪問原生事件對象
            event.preventDefault()
        }
    }
    

事件修飾符

  • 將方法從處理 DOM 事件中解放出來兄一,只專注于數據邏輯厘线。為此 Vue.js 為 v-on 提供兩個事件修飾符.prevent.stop
```
<!-- 阻止單擊事件冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修飾符可以串聯(lián) -->
<a v-on:click.stop.prevent="doThat">
<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>
```
1.0.16 添加了兩個額外的修飾符:

```
<!-- 添加事件偵聽器時使用 capture 模式 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只當事件在該元素本身(而不是子元素)觸發(fā)時觸發(fā)回調 -->
<div v-on:click.self="doThat">...</div>
```

按鍵修飾符

  • Vue.js 允許為 v-on 添加按鍵修飾符出革,同時為常用的 KeyCode 提供了別名造壮。

    <!-- 只有在 keyCode 是 13 時調用 vm.submit() -->
    <input v-on:keyup.13="submit">
    
    <!-- 同上 -->
    <input v-on:keyup.enter="submit">
    <!-- 縮寫語法 -->
    <input @keyup.enter="submit">
    
  • 1.0.17+ 可以自定義按鍵別名。

    // 可以使用 @keyup.f1
    Vue.directive('on').keyCodes.f1 = 112
    

為什么在 HTML 中監(jiān)聽事件

  • 當一個 ViewModel 被銷毀時,所有的事件處理器都會自動被刪除耳璧。

表單控件綁定

基礎用法

Checkbox

  • 多個勾選框成箫,綁定到同一個數組。

    <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
    <label for="jack">Jack</label>
    <input type="checkbox" id="john" value="John" v-model="checkedNames">
    <label for="john">John</label>
    <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
    <label for="mike">Mike</label>
    <br>
    <span>Checked names: {{ checkedNames | json }}</span>
    
    new Vue({
        el: '...',
        data: {
            checkedNames: []
        }
    })
    
  • 多選(綁定到一個數組)旨枯。

    <select v-model="selected" multiple>
        <option selected>A</option>
        <option>B</option>
        <option>C</option>
    </select>
    <br>
    <span>Selected: {{ selected | json }}</span>
    
  • 動態(tài)選項蹬昌,用 v-for 渲染。

    <select v-model="selected">
        <option v-for="option in options" v-bind:value="option.value">
            {{ option.text }}
        </option>
    </select>
    <span>Selected: {{ selected }}</span>
    
    new Vue({
        el: '...',
        data: {
            selected: 'A',
            options: [
                { text: 'One', value: 'A' },
                { text: 'Two', value: 'B' },
                { text: 'Three', value: 'C' }
            ]
        }
    })
    

綁定 value

  • 對于單選按鈕攀隔,勾選框及選擇框選項皂贩, v-model 綁定的 value 通常是靜態(tài)字符串(對于勾選框是邏輯值);若想綁定 value 到 Vue 實例的一個動態(tài)屬性上昆汹,可以用 v-bind 實現明刷,并且這個屬性的值可以不是字符串。

Checkbox

<input
  type="checkbox"
  v-model="toggle"
  v-bind:true-value="a"
  v-bind:false-value="b">
// 當選中時
vm.toggle === vm.a
// 當沒有選中時
vm.toggle === vm.b

Radio

<input type="radio" v-model="pick" v-bind:value="a">
// 當選中時
vm.pick === vm.a

Select Options

<select v-model="selected">
  <!-- 對象字面量 -->
  <option v-bind:value="{ number: 123 }">123</option>
</select>
// 當選中時
typeof vm.selected // -> 'object'
vm.selected.number // -> 123

參數特性

lazy

  • 在默認情況下筹煮, v-modelinput 事件中同步輸入框值與數據,可以添加一個特性 lazy居夹,從而改到在 change 事件中同步败潦。

number

  • 如果想自動將用戶的輸入轉為 Number 類型(如果原值的轉換結果為 NaN 則返回原值),可以添加一個特性 number 准脂。
<input v-model="age" number>

debounce

  • debounce 設置一個最小的延時劫扒,在每次敲擊之后延時同步輸入框的值與數據沐旨。如果每次更新都要進行高耗操作(例如在輸入提示中 Ajax 請求)纬傲,它較為有用营罢。

    <input v-model="msg" debounce="500">
    
  • debounce 參數不會延遲 input 事件:它延遲“寫入”底層數據晕翠。因此在使用 debounce 時應當用 vm.$watch() 相應數據的變化瞎抛。若想延遲 DOM 事件蚓曼,應當使用 debounce 過濾器祷安。

    <input @keyup="onKeyup | debounce 500">
    

過渡

  • 應用過渡效果唉地,要在目標元素上使用 transition 特性砾脑。
    <div v-if="show" transition="my-transition"></div>
    
  • transition 特性可以與下面資源一起用:
    • v-if
    • v-show
    • v-for(只在插入和刪除時觸發(fā)幼驶,使用 vue-animated-list 插件)
    • 動態(tài)組件
    • 在組件的根節(jié)點上,并且被 Vue 實例 DOM 方法(如 vm.$appendTo(el) )觸發(fā)韧衣。
  • 當插入或刪除帶有過渡的元素時盅藻,Vue 將:
    1. 嘗試以 ID "my-transition" 查找 JavaScript 過渡鉤子對象 —— 通過 Vue.transition(id, hooks)transitions 選項注冊。如果找到了畅铭,將在過渡的不同階段調用相應的鉤子氏淑。
    2. 自動嗅探目標元素是否有 CSS 過渡或動畫,并在合適時添加 / 刪除 CSS 類名硕噩。
    3. 如果沒有找到 JavaScript 鉤子并且也沒有檢測到 CSS 過渡 / 動畫假残,DOM操作(插入 / 刪除)在下一幀中立即執(zhí)行。

CSS 過渡

示例

<div v-if="show" transition="expand">hello</div>
/* 必需 */
.expand-transition {
  transition: all .3s ease;
  height: 30px;
  padding: 10px;
  background-color: #eee;
  overflow: hidden;
}
/* .expand-enter 定義進入的開始狀態(tài) */
/* .expand-leave 定義離開的結束狀態(tài) */
.expand-enter, .expand-leave {
  height: 0;
  padding: 0 10px;
  opacity: 0;
}
  • 可以在同一元素上通過動態(tài)綁定實現不同的過渡炉擅。

    <div v-if="show" :transition="transitionName">hello</div>
    
    new Vue({
        el: '...',
        data: {
            show: false,
            transitionName: 'fade'
        }
    })
    
  • 可以提供 JavaScript 鉤子守问。

    Vue.transition('expand', {
    beforeEnter: function (el) {
        el.textContent = 'beforeEnter'
    },
    enter: function (el) {
        el.textContent = 'enter'
    },
    afterEnter: function (el) {
        el.textContent = 'afterEnter'
    },
    enterCancelled: function (el) {
        // handle cancellation
    },
    beforeLeave: function (el) {
        el.textContent = 'beforeLeave'
    },
    leave: function (el) {
        el.textContent = 'leave'
    },
    afterLeave: function (el) {
        el.textContent = 'afterLeave'
    },
    leaveCancelled: function (el) {
        // handle cancellation
    }
    })
    

過渡的 CSS 類名

  • 類名的添加和切換取決于 transition 特性的值匀归,比如 transition="fade" ,會有三個 CSS 類名:
    1. .fade-transition 始終保留在元素上耗帕。
    2. .fade-enter 定義進入過渡的開始狀態(tài)穆端,只應用一幀然后立即刪除。
    3. .fade-leave 定義離開過渡的結束狀態(tài)仿便。在離開過渡開始時生效体啰,在它結束后刪除。
  • 如果 transition 特性沒有值嗽仪,類名默認是 .v-transition 荒勇, v-enterv-leave 闻坚。

自定義過渡類名

  • 自定義過渡類名會覆蓋默認的類名沽翔。

顯式聲明 CSS 過渡類型

  • 可能希望一個元素同時帶有兩種類型的動畫,此時要顯式的聲明期望 Vue 處理的動畫類型( animation 或者 transition)窿凤。

    Vue.transition('bounce', {
    // 該過渡效果將只偵聽 `animationend` 事件
    type: 'animation'
    })
    

CSS 動畫

  • 在動畫中 v-center 類名在節(jié)點插入 DOM 后不會立即刪除仅偎,而是在 animationed 事件觸發(fā)時刪除。

JavaScript 過渡

  • 只使用 JavaScript 鉤子雳殊,不用定義任何 CSS 規(guī)則橘沥。當只使用 JavaScript 過渡時, enterleave 鉤子需要調用 done 回調夯秃,否則他們將被同步調用座咆,過渡將立即結束。
  • 為 JavaScript 過渡顯式聲明 css: false 以使 Vue.js 跳過 CSS 檢測仓洼。

漸進過渡

  • transitionv-for 一起使用時可以創(chuàng)建漸進式過渡介陶。給過渡元素添加一個特性 staggerenter-staggerleave-stagger 色建〗锫或者提供一個鉤子 staggerenter-staggerleave-stagger 镀岛。

    <div v-for="item in list" transition="stagger" stagger="100"></div>
    
    Vue.transition('stagger', {
      stagger: function (index) {
        // 每個過渡項目增加 50ms 延時
        // 但是最大延時限制為 300ms
        return Math.min(300, index * 50)
      }
    })
    

組件

使用組件

注冊

  • Vue.extend() 創(chuàng)建一個組件構造器弦牡。

  • Vue.component(tag, constructor) 注冊。

    var MyComponent = Vue.extend({
      // 選項...
    })
    
    // 全局注冊組件漂羊,tag 為 my-component
    Vue.component('my-component', MyComponent)
    
  • 組件在注冊之后驾锰,便可以在父實例的模塊中以自定義元素 <my-component> 的形式使用。

  • 注意自定義元素的作用只是作為一個掛載點走越⊥衷ィ可以用實例選項 replace 決定是否替換。

局部注冊

  • 可以用實例選項 components 注冊局部組件。

    var Child = Vue.extend({ /* ... */ })
    var Parent = Vue.extend({
        template: '...',
        components: {
            // <my-component> 只能用在父組件模板內
            'my-component': Child
        }
    })
    

注冊語法糖

  • 直接傳入選項對象而不是構造器給 Vue.component()component 選項赏酥,Vue.js 會在背后自動調用 Vue.extend() 喳整。

    // 在一個步驟中擴展與注冊
    Vue.component('my-component', {
        template: '<div>A custom component!</div>'
    })
    // 局部注冊也可以這么做
    var Parent = Vue.extend({
        components: {
            'my-component': {
                template: '<div>A custom component!</div>'
            }
        }
    })
    

組件選項問題

  • 傳入 Vue 構造器的多數選項可以用在 Vue.extend() 中,但如果簡單的把一個對象作為 datael 傳遞給 Vue.extend()

    var data = { a: 1 }
    var MyComponent = Vue.extend({
      data: data
    })
    

    MyComponent 所有的實例都會共享同一個 data 對象(或者 el 對象)裸扶。

  • 應當使用一個函數作為 data 選項框都,讓這個函數返回一個新的對象。

    var MyComponent = Vue.extend({
      data: function () {
        return { a: 1 }
      }
    })
    

模板解析

  • 一些 HTML 元素對什么元素可以放在它里面有限制呵晨,比如:

    • a 不能包含其它的交互元素(如按鈕魏保,鏈接)
    • ulol 只能直接包含 li
    • select 只能包含 optionoptgroup
    • table 只能直接包含 theadtbody 摸屠, tfoot 谓罗, trcaption 季二, col 檩咱, colgroup
    • tr 只能直接包含 thtd
  • 不能依賴自定義組件在瀏覽器驗證之前的展開結果胯舷。

  • 自定義標簽(包括自定義元素和特殊標簽刻蚯,如 <component><template>? 需纳、 <partial> )不能用在 ul 芦倒, select 艺挪, table 等對內部元素有限制的標簽內不翩。

  • 對于自定義元素,應當使用 is 屬性麻裳。

    <table>
      <tr is="my-component"></tr>
    </table>
    
  • <template> 不能用在 <table> 內口蝠,應當使用 <tbody><table> 可以有多個 <tbody> 津坑。

    <table> <tbody v-for="item in items">
        <tr>Even row</tr>
        <tr>Odd row</tr>
      </tbody>
    </table>
    

Props

使用 props 傳遞數據

  • 組件實例的作用域是孤立的妙蔗,不能也不應該在子組件的模板內直接引用父組件的數據,可以使用 props 把數據傳遞給子組件疆瑰。

    Vue.component('child', {
      // 聲明 props
      props: ['msg'],
      // prop 可以用在模板內
      // 可以用 `this.msg` 設置
      template: '<span>{{ msg }}</span>'
    })
    
    <child msg="hello!"></child>
    

camelCase vs. kebab-case

  • 名字形式為 camelCase 的 prop 用作特性時眉反,需要轉變?yōu)?kebab-case。

    Vue.component('child', {
      // camelCase in JavaScript
      props: ['myMessage'],
      template: '<span>{{ myMessage }}</span>'
    })
    
    <!-- kebab-case in HTML -->
    <child my-message="hello!"></child>
    

動態(tài) Props

  • 可以用 v-bind 綁定動態(tài) Props 到父組件的數據穆役。每當父組件數據變化時寸五,也會傳導給子組件。

    <div>
      <input v-model="parentMsg">
      <br>
      <child v-bind:my-message="parentMsg"></child>
    </div>
    

字面量語法 vs. 動態(tài)語法

  • 字面 prop

    <!-- 傳遞了一個字符串 "1" -->
    <comp some-prop="1"></comp>
    
  • 動態(tài) prop (使用動態(tài)語法)

    <!-- 傳遞實際的數字  -->
    <comp :some-prop="1"></comp>
    

Prop 綁定類型

  • prop 默認是單向綁定的耿币。當父組件的屬性變化時梳杏,將傳導給子組件。

  • 可以使用 .sync.once 綁定修飾符 顯式地強制雙向或單次綁定。

    <!-- 默認為單向綁定 -->
    <child :msg="parentMsg"></child>
    <!-- 雙向綁定 -->
    <child :msg.sync="parentMsg"></child>
    <!-- 單次綁定 -->
    <child :msg.once="parentMsg"></child>
    
  • 雙向綁定會把子組件的 msg 屬性同步回父組件的 parentMsg 屬性十性。單次綁定在建立之后不會同步之后的變化叛溢。

  • 如果 prop 是一個對象或數組,是按引用傳遞劲适。在子組件內修改它影響父組件的狀態(tài)楷掉,不管使用哪種綁定類型

Prop 驗證

  • 為 props 加入驗證要求减响,以確保其他人正確使用組件靖诗。 此時 props 的值是一個對象,包含驗證要求支示。

    Vue.component('example', {
      props: {
        // 基礎類型檢測 (`null` 意思是任何類型都可以)
        propA: Number,
        // 多種類型 (1.0.21+)
        propM: [String, Number],
        // 必需且是字符串
        propB: {
          type: String,
          required: true
        },
        // 數字刊橘,有默認值
        propC: {
          type: Number,
          default: 100
        },
        // 對象/數組的默認值應當由一個函數返回
        propD: {
          type: Object,
          default: function () {
            return { msg: 'hello' }
          }
        },
        // 指定這個 prop 為雙向綁定
        // 如果綁定類型不對將拋出一條警告
        propE: {
          twoWay: true
        },
        // 自定義驗證函數
        propF: {
          validator: function (value) {
            return value > 10
          }
        },
        // 轉換函數(1.0.12 新增)
        // 在設置值之前轉換值
        propG: {
          coerce: function (val) {
            return val + '' // 將值轉換為字符串
          }
        },
        propH: {
          coerce: function (val) {
            return JSON.parse(val) // 將 JSON 字符串轉換為對象
          }
        }
      }
    })
    
  • 當 prop 驗證失敗了,Vue 將拒絕在子組件上設置此值颂鸿,如果使用的是開發(fā)版本會拋出一條警告促绵。

  • 具體可以參閱 Vue.js 的相關文檔。

父子組件通信

父鏈

  • 組件可以用 this.$parent 訪問它的父組件嘴纺。根實例的后代可以用 this.$root 訪問它败晴。父組件有一個數組 this.$children ,包含它所有的子元素栽渴。
  • 子組件應當避免直接依賴父組件的數據尖坤,盡量顯式地使用 props 傳遞數據。
  • 不要再子組件中修改父組件的狀態(tài)闲擦。

自定義事件

  • 每一個 Vue 實例都是一個事件觸發(fā)器慢味。
    • 使用 $on() 監(jiān)聽事件;
    • 使用 $emit() 在它上面觸發(fā)事件墅冷;
    • 使用 $dispatch() 派發(fā)事件纯路,事件沿著父鏈冒泡;
    • 使用 $broadcast() 廣播事件寞忿,事件向下傳導給所有的后代驰唬。
  • 不同于 DOM 事件,Vue 事件在冒泡過程中第一次觸發(fā)回調之后自動停止冒泡腔彰,除非回調明確返回 true 叫编。

使用 v-on 綁定自定義事件

  • 在模板中子組件用到的地方聲明事件處理器。

    <child v-on:child-msg="handleIt"></child>
    

子組件索引

  • 可以使用 v-ref 為子組件指定一個索引 ID 霹抛。

    <div id="parent">
      <user-profile v-ref:profile></user-profile>
    </div>  
    
    var parent = new Vue({ el: '#parent' })
    // 訪問子組件
    var child = parent.$refs.profile
    
  • v-refv-for 一起用時搓逾,ref 是一個數組或對象,包含相應的子組件上炎。

使用 Slot 分發(fā)內容

  • 為了讓組件可以組合(內容分發(fā))恃逻,Vue.js 實現了一個內容分發(fā) API 雏搂,使用特殊的 <slot> 元素作為原始內容的插槽。

編譯作用域

  • 組件作用域:父組件模板的內容在父組件作用域內編譯寇损;子組件模板的內容在子組件作用域內編譯凸郑。

  • 以下模板

    <child-component>
      {{ msg }}
    </child-component>
    

    msg 應當是綁定到父組件的數據(假定 someChildProperty 是子組件的屬性)。

  • 一個常見的錯誤:

    <!-- 無效 -->
    <child-component v-show="someChildProperty"></child-component>
    

    父組件模板不應該知道子組件的狀態(tài)矛市,應當在子組件模板中這樣做:

    Vue.component('child-component', {
      // 有效芙沥,因為是在正確的作用域內
      template: '<div v-show="someChildProperty">Child</div>',
      data: function () {
        return {
          someChildProperty: true
        }
      }
    })
    
  • 類似地,分發(fā)內容也是在父組件作用域內編譯浊吏。

單個 Slot

  • 父組件的內容將被拋棄而昨,除非子組件模板包含 <slot> 。如果子組件模板只有一個沒有特性的 slot 找田,父組件的整個內容將插到 slot 所在的地方并替換它歌憨。

  • <slot> 標簽的內容視為回退內容,在子組件的作用域內編譯墩衙。當宿主元素為空并且沒有內容供插入時顯示回退內容务嫡。

  • 一個例子

    my-component 組件模板:

    <div>
      <h1>This is my component!</h1>
      <slot>
        如果沒有分發(fā)內容則顯示我。
      </slot>
    </div>
    

    父組件模板:

    <my-component>
      <p>This is some original content</p>
      <p>This is some more original content</p>
    </my-component>
    

    渲染結果

    <div>
      <h1>This is my component!</h1>
      <p>This is some original content</p>
      <p>This is some more original content</p>
    </div>
    

具名 Slot

  • <slot> 元素可以用一個特殊屬性 name 來配置如何分發(fā)內容漆改。

  • 可以有一個匿名 slot 作為默認 slot心铃。如果沒有默認 slot ,找不到匹配的內容片段將被拋棄挫剑。

  • 一個例子

    multi-insertion 組件:

    <div>
      <slot name="one"></slot>
      <slot></slot>
      <slot name="two"></slot>
    </div>
    

    父組件模板:

    <multi-insertion>
    

<p slot="one">One</p>
<p slot="two">Two</p>
<p>Default A</p>
</multi-insertion>
```
渲染結果:

```
<div>

<p slot="one">One</p>
<p>Default A</p>
<p slot="two">Two</p>
</div>
```

動態(tài)組件

  • 多個組件可以使用同一個掛載點去扣,然后動態(tài)地進行切換。使用保留的 <component> 元素樊破,動態(tài)地綁定到它的 is 特性愉棱。

    new Vue({
      el: 'body',
      data: {
        currentView: 'home'
      },
      components: {
        home: { /* ... */ },
        posts: { /* ... */ },
        archive: { /* ... */ }
      }
    })
    
    <component :is="currentView">
      <!-- 組件在 vm.currentview 變化時改變 -->
    </component>
    

keep-alive

  • 如果把切換出去的組件保留在內存中,可以保留它的狀態(tài)或避免重新渲染捶码。為此可以添加一個 keep-alive 指令參數:

    <component :is="currentView" keep-alive>
      <!-- 非活動組件將被緩存 -->
    </component>
    

activate 鉤子

  • 控制組件切換這一時段羽氮,可以為切入組件添加 activate 鉤子或链。

    Vue.component('activate-example', {
      activate: function (done) {
        var self = this
        loadDataAsync(function (data) {
          self.someData = data
          done()
        })
      }
    })
    
  • activate 鉤子只作用于動態(tài)組件切換靜態(tài)組件初始化渲染的過程中惫恼,不作用于使用實例方法手工插入的過程中。

transition-mode

  • transition-mode 特性用于指定兩個動態(tài)組件之間如何過渡澳盐。

    <!-- 先淡出再淡入 -->
    <component
      :is="view"
      transition="fade"
      transition-mode="out-in">
    </component>
    
    .fade-transition {
      transition: opacity .3s ease;
    }
    .fade-enter, .fade-leave {
      opacity: 0;
    }
    

雜項

  • 自定義組件也可以使用 v-for 祈纯,但是不能將數據傳遞給組件,因為其作用域是孤立的叼耙。應當使用 props 傳遞腕窥。

    <my-component
      v-for="item in items"
      :item="item"
      :index="$index">
    </my-component>
    

異步組件

  • Vue.js 允許將組件定義為一個工廠函數,動態(tài)地解析組件的定義筛婉。Vue.js 只在組件需要渲染時觸發(fā)工廠函數簇爆,并且把結果緩存起來癞松,用于后面的再次渲染。

  • 一個例子

    Vue.component('async-example', function (resolve, reject)   {
      setTimeout(function () {
        resolve({
          template: '<div>I am async!</div>'
        })
      }, 1000)
    })
    

    工廠函數接收一個 resolve 回調入蛆,在收到從服務器下載的組件定義時調用响蓉。也可以調用 reject(reason) 指示加載失敗。這里 setTimeout 只是為了演示哨毁。怎么獲取組件完全由你決定枫甲。推薦配合使用 Webpack 的代碼分割功能

    Vue.component('async-webpack-example', function (resolve)   {
      // 這個特殊的 require 語法告訴 webpack
      // 自動將編譯后的代碼分割成不同的塊,
      // 這些塊將通過 ajax 請求自動下載扼褪。
      require(['./my-async-component'], resolve)
    })
    

遞歸組件

  • name 選項的組件可以再模板內部遞歸調用自己想幻。

    var StackOverflow = Vue.extend({
      name: 'stack-overflow',
      template:
        '<div>' +
          // 遞歸地調用它自己
          '<stack-overflow></stack-overflow>' +
        '</div>'
    })
    

    上面組件會導致一個錯誤 “max stack size exceeded”,所以要確保遞歸調用有終止條件话浇。

片段實例

N/A

內聯(lián)模板

  • 如果子組件有 inline-template 特性脏毯,組件將把它的內容當做它的模板,而不是分發(fā)內容幔崖。

    <my-component inline-template>
      <p>These are compiled as the component's own template</p>
      <p>Not parent's transclusion content.</p>
    </my-component>
    
  • inline-template 讓模板的作用域難以理解抄沮,并且不能緩存模板編譯結果。最佳實踐是使用 template 選項在組件內定義模板岖瑰。

深入響應式原理

[可以查看我的 Vue 2 的 Guide Note]

自定義指令

基礎

  • Vue.js 允許用戶通過 Vue.directive(id, definition) 方法注冊一個全局自定義指令叛买,接收兩個參數:指令 ID 和 定義對象。也可以用組件的 directive 選項注冊一個局部自定義指令蹋订。

鉤子函數

  • 定義對象可以提供幾個鉤子函數:
    • bind: 只調用一次率挣,在指令第一次綁定到元素上時調用。

    • update: 在 bind 之后立即以初始值為參數第一次調用露戒,之后每當綁定值變化時調用椒功,參數為新值與舊值。

    • unbind: 只調用一次智什,在指令從元素上解綁時調用动漾。

      Vue.directive('my-directive', {
        bind: function () {
          // 準備工作
          // 例如,添加事件處理器或只需要運行一次的高耗任務
        },
        update: function (newValue, oldValue) {
          // 值更新時的工作
          // 也會以初始值為參數調用一次
        },
        unbind: function () {
          // 清理工作
          // 例如荠锭,刪除 bind() 添加的事件監(jiān)聽器
        }
      })
      
      <div v-my-directive="someValue"></div>
      

      當只需要 update 函數時旱眯,可以傳入一個函數替代定義對象

      Vue.directive('my-directive', function (value) {
        // 這個函數用作 update()
      })
      

指令實例屬性

  • 所有的鉤子函數將被復制到實際的指令對象中,鉤子內 this 指向這個指令對象证九。這個對象暴露了一些有用的屬性删豺。

    • el: 指令綁定的元素。
    • vm: 擁有該指令的上下文 ViewModel愧怜。
    • expression: 指令的表達式呀页,不包括參數和過濾器。
    • arg: 指令的參數拥坛。
    • name: 指令的名字蓬蝶,不包含前綴尘分。
    • modifiers: 一個對象,包含指令的修飾符丸氛。
    • descriptor: 一個對象音诫,包含指令的解析結果。
  • 一個例子

    <div id="demo" v-demo:hello.a.b="msg"></div>
    
    Vue.directive('demo', {
      bind: function () {
        console.log('demo bound!')
      },
      update: function (value) {
        this.el.innerHTML =
          'name - '       + this.name + '<br>' +
          'expression - ' + this.expression + '<br>' +
          'argument - '   + this.arg + '<br>' +
          'modifiers - '  + JSON.stringify(this.modifiers) + '<br>' +
          'value - '      + value
      }
    })
    var demo = new Vue({
      el: '#demo',
      data: {
        msg: 'hello!'
      }
    })
    

    結果

    name - demo
    expression - msg
    argument = hello
    modifiers - {"b":true,"a":true}
    value = hello!
    

字面修飾符

  • 當指令使用了字面修飾符雪位,它的值將按照普通字符串處理并傳遞給 update 方法竭钝。

  • 普通字符串不能響應數據變化,故 update 方法將只調用一次雹洗。

    <div v-demo.literal="foo bar baz">
    
    Vue.directive('demo', function (value) {
      console.log(value) // "foo bar baz"
    })
    

元素指令

  • 元素指令可以看做一個輕量組件香罐。

    Vue.elementDirective('my-directive', {
      // API 同普通指令
      bind: function () {
        // 操作 this.el...
      }
    })
    
    <my-directive></my-directive>
    
  • 元素指令是終結性的,這意味著时肿,一旦 Vue 遇到一個元素指令庇茫,它將跳過該元素及其子元素 —— 只有該元素指令本身可以操作該元素及其子元素。

高級選項

[正在整理]

自定義過濾器

基礎

  • 可以使用全局方法 Vue.filter() 注冊一個自定義過濾器螃成,接收兩個參數: 過濾器 ID 和 過濾器函數旦签。過濾器函數以值為參數,返回轉換后的值寸宏。

    Vue.filter('reverse', function (value) {
        return value.split('').reverse().join('')
    })
    
  • 過濾器函數可以接收任意數量的參數

    Vue.filter('wrap', function (value, begin, end) {
      return begin + value + end
    })
    
    <!-- 'hello' => 'before hello after' -->
    <span v-text="message | wrap 'before' 'after'"></span>
    

雙向過濾器

  • 定義一個過濾器宁炫,把來自視圖( <input> 元素)的值寫回模型之前轉化它。

    Vue.filter('currencyDisplay', {
      // model -> view
      // 在更新 `<input>` 元素之前格式化值
      read: function(val) {
        return '$'+val.toFixed(2)
      },
      // view -> model
      // 在寫回數據之前格式化值
      write: function(val, oldVal) {
        var number = +val.replace(/[^\d.]/g, '')
        return isNaN(number) ? 0 : parseFloat(number.toFixed(2))
      }
    })
    

動態(tài)參數

  • 如果過濾器參數沒有用引號包起來氮凝,則它會在當前 vm 作用域內動態(tài)計算羔巢。另外,過濾器函數的 this 始終指向調用它的 vm罩阵。

混合

基礎

  • 混合以一種靈活的方式為組件提供分布復用功能竿秆。混合對象可以包含任意的組件選項稿壁。當組件使用了混合對象時幽钢,混合對象的所有選項將被“混入”組件自己的選項中。

    // 定義一個混合對象
    var myMixin = {
      created: function () {
        this.hello()
      },
      methods: {
        hello: function () {
          console.log('hello from mixin!')
        }
      }
    }
    // 定義一個組件傅是,使用這個混合對象
    var Component = Vue.extend({
      mixins: [myMixin]
    })
    var component = new Component() // -> "hello from mixin!"
    

選項合并

  • 當混合對象與組件包含同名選項時匪燕,這些選項將以適當的策略合并。
  • 混合的鉤子將在組件自己的鉤子之前調用落午。
  • 注意 Vue.extend() 使用同樣的合并策略谎懦。

全局混合

  • 慎用全局混合肚豺,因為它影響到每個創(chuàng)建的 Vue 實例溃斋,包括第三方組件。在大多數情況下吸申,它應當只用于自定義選項梗劫。

自定義選項合并策略

  • 如果想用自定義邏輯合并自定義選項享甸,則向 Vue.config.optionMergeStrategies 添加一個函數:

    Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
      // 返回 mergedVal
    }
    

    對于多數值為對象的選項,可以簡單地使用 methods 所用的合并策略

    var strategies = Vue.config.optionMergeStrategies
    strategies.myOption = strategies.methods
    

插件

開發(fā)插件

[正在整理]

使用插件

  • 通過 Vue.use() 全局方法使用插件梳侨。

    // 調用 `MyPlugin.install(Vue)`
    Vue.use(MyPlugin)
    

    也可以傳入一個選項對象

    Vue.use(MyPlugin, { someOption: true })
    
  • 雖然對于一些插件蛉威,如果 Vue 是全局變量則自動調用 Vue.use(),但是在模塊環(huán)境中應當始終顯式調用 Vue.use() 走哺。

構建大型應用

  • 在 Vue 中可以使用諸如 Jade 之類的預處理器蚯嫌。

路由

  • vue-router
  • 對于簡單的路由邏輯,可以通過監(jiān)聽 hashchange 事件丙躏,并使用動態(tài)組件實現择示。

與服務器通信

狀態(tài)管理

  • 使用 store 模式,把所有的 action 放在 store 內晒旅,action 修改 store 的狀態(tài)栅盲。
  • 不要在 action 中替換原始的狀態(tài)對象!
  • Vuex : Vue's Flux.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末废恋,一起剝皮案震驚了整個濱河市谈秫,隨后出現的幾起案子,更是在濱河造成了極大的恐慌鱼鼓,老刑警劉巖拟烫,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異迄本,居然都是意外死亡构灸,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門岸梨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喜颁,“玉大人,你說我怎么就攤上這事曹阔“肟” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵赃份,是天一觀的道長寂拆。 經常有香客問我,道長抓韩,這世上最難降的妖魔是什么纠永? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谒拴,結果婚禮上尝江,老公的妹妹穿的比我還像新娘。我一直安慰自己英上,他們只是感情好炭序,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布啤覆。 她就那樣靜靜地躺著,像睡著了一般惭聂。 火紅的嫁衣襯著肌膚如雪窗声。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天辜纲,我揣著相機與錄音笨觅,去河邊找鬼。 笑死耕腾,一個胖子當著我的面吹牛屋摇,可吹牛的內容都是我干的。 我是一名探鬼主播幽邓,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼炮温,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了牵舵?” 一聲冷哼從身側響起柒啤,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎畸颅,沒想到半個月后担巩,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡没炒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年涛癌,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片送火。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡拳话,死狀恐怖,靈堂內的尸體忽然破棺而出种吸,到底是詐尸還是另有隱情弃衍,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布坚俗,位于F島的核電站镜盯,受9級特大地震影響,放射性物質發(fā)生泄漏猖败。R本人自食惡果不足惜速缆,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恩闻。 院中可真熱鬧艺糜,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侠草。三九已至辱挥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間边涕,已是汗流浹背晤碘。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留功蜓,地道東北人园爷。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像式撼,于是被迫代替她去往敵國和親童社。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內容