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
    // 設(shè)置屬性也會影響到原始數(shù)據(jù)
    vm.a = 2
    data.a // -> 2
    // ... 反之亦然
    data.a = 3
    vm.a // -> 3
    
  • 只有被代理的屬性是響應(yīng)的。在實例創(chuàng)建之后添加新的屬性到實例上,它不會觸發(fā)視圖更新。

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

實例生命周期

  • Vue 實例在其生命周期的不同階段會調(diào)用不同的鉤子,如 createdcompiledreadydestroy 。鉤子的 this 指向調(diào)用它的 Vue 實例。

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


數(shù)據(jù)綁定語法

插值

文本

  • 處理單次插值,今后的數(shù)據(jù)變化不引起插值更新。

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

原始的 HTML

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

    <div>{{{ raw_html }}}</div>
    
  • 內(nèi)容以 HTML 字符串插入時,**數(shù)據(jù)綁定將被忽略 **。如果要復(fù)用模板片段,應(yīng)當使用 partials

  • 只對可信內(nèi)容使用 HTML 插值,永不用于用戶提交的內(nèi)容。

HTML 特性

  • Mustache 標簽也可以用在 HTML 特性 (attribute) 內(nèi)。

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

綁定表達式

JavaScript 表達式

  • Vue.js 在數(shù)據(jù)綁定內(nèi)支持全功能的 JavaScript 表達式。表達式將在 Vue 實例的作用域內(nèi)計算。
  • 每個綁定只能包含單個表達式

過濾器

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

    {{ message | capitalize }}
    
  • 管道語法不是 JavaScript 語法,不能在表達式內(nèi)使用過濾器,只能添加到表達式后邊。

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

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

指令

修飾符

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

    <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>

計算屬性

  • 在模板中要使用多余一個表達式的邏輯時,應(yīng)當使用計算屬性

基礎(chǔ)例子

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 之后,調(diào)用 vm.fullName = 'John Doe' 時,setter 會被調(diào)用,vm.firstNamevm.lastName 也會有相應(yīng)更新。

Class 與 Style 綁定

  • v-bind 用于 classstyle 時,Vue.js 專門增強了它。表達式的結(jié)果類型除了字符串之外,還可以是對象或數(shù)組

綁定 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>
    
  • 也可以直接綁定數(shù)據(jù)里的一個對象。

數(shù)組語法

  • 把一個數(shù)組傳遞給 v-bind:class ,以應(yīng)用一個 class 列表。

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

    渲染為

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

    如果需要根據(jù)條件切換列表中的 class ,可以使用三元表達式。

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

    在 1.0.19+ 中,可以在數(shù)組語法中使用對象語法。

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

綁定內(nèi)聯(lián)樣式

對象語法

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

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

數(shù)組語法

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

    <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,實現(xiàn)多個元素的切換。最終渲染結(jié)果不會包含這個元素。

    <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 會出現(xiàn)問題。因此應(yīng)當使用另一個 v-show 替換 v-else

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

v-if vs. v-show

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

列表渲染

v-for

  • v-for 塊內(nèi)我們能完全訪問父組件作用域內(nèi)的屬性,另有一個特殊變量 $index 作為當前數(shù)組元素的索引(從 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>
    

數(shù)組變動檢測

track-by

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

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

    然后可以這樣給出提示:

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

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

track-by $index

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

問題

  • 因為 JavaScript 的限制,Vue.js 不能檢測到下面數(shù)組的變化:

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

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

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

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

對象 v-for

  • 可以使用 v-for 遍歷對象。除了 $index 之外,作用域內(nèi)還可以訪問另外一個特殊變量 $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() 的結(jié)果遍歷,但并不能保證它的結(jié)果在不同的 JavaScript 引擎下一致。

值域 v-for

  • v-for 可以接收一個整數(shù),此時它將重復(fù)模板數(shù)次。

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

顯示過濾 / 排序的結(jié)果

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

  • 使用內(nèi)置過濾器: 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 。

內(nèi)聯(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)
    }
  }
})
  • 如果需要在內(nèi)聯(lián)語句中訪問原生 DOM 事件,可以使用特殊變量 $event

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

事件修飾符

  • 將方法從處理 DOM 事件中解放出來,只專注于數(shù)據(jù)邏輯。為此 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ā)回調(diào) -->
<div v-on:click.self="doThat">...</div>
```

按鍵修飾符

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

    <!-- 只有在 keyCode 是 13 時調(diào)用 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 被銷毀時,所有的事件處理器都會自動被刪除。

表單控件綁定

基礎(chǔ)用法

Checkbox

  • 多個勾選框,綁定到同一個數(shù)組。

    <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: []
        }
    })
    
  • 多選(綁定到一個數(shù)組)。

    <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 實現(xiàn),并且這個屬性的值可以不是字符串。

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

參數(shù)特性

lazy

  • 在默認情況下, v-modelinput 事件中同步輸入框值與數(shù)據(jù),可以添加一個特性 lazy,從而改到在 change 事件中同步。

number

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

debounce

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

    <input v-model="msg" debounce="500">
    
  • debounce 參數(shù)不會延遲 input 事件:它延遲“寫入”底層數(shù)據(jù)。因此在使用 debounce 時應(yīng)當用 vm.$watch() 相應(yīng)數(shù)據(jù)的變化。若想延遲 DOM 事件,應(yīng)當使用 debounce 過濾器。

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

過渡

  • 應(yīng)用過渡效果,要在目標元素上使用 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 選項注冊。如果找到了,將在過渡的不同階段調(diào)用相應(yīng)的鉤子。
    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 定義離開的結(jié)束狀態(tài) */
.expand-enter, .expand-leave {
  height: 0;
  padding: 0 10px;
  opacity: 0;
}
  • 可以在同一元素上通過動態(tài)綁定實現(xiàn)不同的過渡。

    <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),只應(yīng)用一幀然后立即刪除。
    3. .fade-leave 定義離開過渡的結(jié)束狀態(tài)。在離開過渡開始時生效,在它結(jié)束后刪除。
  • 如果 transition 特性沒有值,類名默認是 .v-transitionv-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 鉤子需要調(diào)用 done 回調(diào),否則他們將被同步調(diào)用,過渡將立即結(jié)束。
  • 為 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)建一個組件構(gòu)造器。

  • 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> 只能用在父組件模板內(nèi)
            'my-component': Child
        }
    })
    

注冊語法糖

  • 直接傳入選項對象而不是構(gòu)造器給 Vue.component()component 選項,Vue.js 會在背后自動調(diào)用 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 構(gòu)造器的多數(shù)選項可以用在 Vue.extend() 中,但如果簡單的把一個對象作為 datael 傳遞給 Vue.extend()

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

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

  • 應(yīng)當使用一個函數(shù)作為 data 選項,讓這個函數(shù)返回一個新的對象。

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

模板解析

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

    • a 不能包含其它的交互元素(如按鈕,鏈接)
    • ulol 只能直接包含 li
    • select 只能包含 optionoptgroup
    • table 只能直接包含 theadtbodytfoottrcaptioncolcolgroup
    • tr 只能直接包含 thtd
  • 不能依賴自定義組件在瀏覽器驗證之前的展開結(jié)果。

  • 自定義標簽(包括自定義元素和特殊標簽,如 <component><template>? 、 <partial> )不能用在 ulselecttable 等對內(nèi)部元素有限制的標簽內(nèi)。

  • 對于自定義元素,應(yīng)當使用 is 屬性。

    <table>
      <tr is="my-component"></tr>
    </table>
    
  • <template> 不能用在 <table> 內(nèi),應(yīng)當使用 <tbody><table> 可以有多個 <tbody>

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

Props

使用 props 傳遞數(shù)據(jù)

  • 組件實例的作用域是孤立的,不能也不應(yīng)該在子組件的模板內(nèi)直接引用父組件的數(shù)據(jù),可以使用 props 把數(shù)據(jù)傳遞給子組件。

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

camelCase vs. kebab-case

  • 名字形式為 camelCase 的 prop 用作特性時,需要轉(zhuǎn)變?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 到父組件的數(shù)據(jù)。每當父組件數(shù)據(jù)變化時,也會傳導(dǎo)給子組件。

    <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)語法)

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

Prop 綁定類型

  • prop 默認是單向綁定的。當父組件的屬性變化時,將傳導(dǎo)給子組件。

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

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

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

Prop 驗證

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

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

  • 具體可以參閱 Vue.js 的相關(guān)文檔。

父子組件通信

父鏈

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

自定義事件

  • 每一個 Vue 實例都是一個事件觸發(fā)器。
    • 使用 $on() 監(jiān)聽事件;
    • 使用 $emit() 在它上面觸發(fā)事件;
    • 使用 $dispatch() 派發(fā)事件,事件沿著父鏈冒泡;
    • 使用 $broadcast() 廣播事件,事件向下傳導(dǎo)給所有的后代。
  • 不同于 DOM 事件,Vue 事件在冒泡過程中第一次觸發(fā)回調(diào)之后自動停止冒泡,除非回調(diào)明確返回 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 是一個數(shù)組或?qū)ο螅鄳?yīng)的子組件。

使用 Slot 分發(fā)內(nèi)容

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

編譯作用域

  • 組件作用域:父組件模板的內(nèi)容在父組件作用域內(nèi)編譯;子組件模板的內(nèi)容在子組件作用域內(nèi)編譯。

  • 以下模板

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

    msg 應(yīng)當是綁定到父組件的數(shù)據(jù)(假定 someChildProperty 是子組件的屬性)。

  • 一個常見的錯誤:

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

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

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

單個 Slot

  • 父組件的內(nèi)容將被拋棄,除非子組件模板包含 <slot> 。如果子組件模板只有一個沒有特性的 slot ,父組件的整個內(nèi)容將插到 slot 所在的地方并替換它。

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

  • 一個例子

    my-component 組件模板:

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

    父組件模板:

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

    渲染結(jié)果

    <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ā)內(nèi)容。

  • 可以有一個匿名 slot 作為默認 slot。如果沒有默認 slot ,找不到匹配的內(nèi)容片段將被拋棄。

  • 一個例子

    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>
```
渲染結(jié)果:

```
<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

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

    <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 ,但是不能將數(shù)據(jù)傳遞給組件,因為其作用域是孤立的。應(yīng)當使用 props 傳遞。

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

異步組件

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

  • 一個例子

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

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

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

遞歸組件

  • name 選項的組件可以再模板內(nèi)部遞歸調(diào)用自己。

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

    上面組件會導(dǎo)致一個錯誤 “max stack size exceeded”,所以要確保遞歸調(diào)用有終止條件

片段實例

N/A

內(nèi)聯(lián)模板

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

    <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 讓模板的作用域難以理解,并且不能緩存模板編譯結(jié)果。最佳實踐是使用 template 選項在組件內(nèi)定義模板。

深入響應(yīng)式原理

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

自定義指令

基礎(chǔ)

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

鉤子函數(shù)

  • 定義對象可以提供幾個鉤子函數(shù):
    • bind: 只調(diào)用一次,在指令第一次綁定到元素上時調(diào)用。

    • update: 在 bind 之后立即以初始值為參數(shù)第一次調(diào)用,之后每當綁定值變化時調(diào)用,參數(shù)為新值與舊值。

    • unbind: 只調(diào)用一次,在指令從元素上解綁時調(diào)用。

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

      當只需要 update 函數(shù)時,可以傳入一個函數(shù)替代定義對象

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

指令實例屬性

  • 所有的鉤子函數(shù)將被復(fù)制到實際的指令對象中,鉤子內(nèi) this 指向這個指令對象。這個對象暴露了一些有用的屬性。

    • el: 指令綁定的元素。
    • vm: 擁有該指令的上下文 ViewModel。
    • expression: 指令的表達式,不包括參數(shù)和過濾器。
    • arg: 指令的參數(shù)。
    • name: 指令的名字,不包含前綴。
    • modifiers: 一個對象,包含指令的修飾符。
    • descriptor: 一個對象,包含指令的解析結(jié)果。
  • 一個例子

    <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!'
      }
    })
    

    結(jié)果

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

字面修飾符

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

  • 普通字符串不能響應(yīng)數(shù)據(jù)變化,故 update 方法將只調(diào)用一次。

    <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>
    
  • 元素指令是終結(jié)性的,這意味著,一旦 Vue 遇到一個元素指令,它將跳過該元素及其子元素 —— 只有該元素指令本身可以操作該元素及其子元素。

高級選項

[正在整理]

自定義過濾器

基礎(chǔ)

  • 可以使用全局方法 Vue.filter() 注冊一個自定義過濾器,接收兩個參數(shù): 過濾器 ID 和 過濾器函數(shù)。過濾器函數(shù)以值為參數(shù),返回轉(zhuǎn)換后的值。

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

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

雙向過濾器

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

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

動態(tài)參數(shù)

  • 如果過濾器參數(shù)沒有用引號包起來,則它會在當前 vm 作用域內(nèi)動態(tài)計算。另外,過濾器函數(shù)的 this 始終指向調(diào)用它的 vm。

混合

基礎(chǔ)

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

    // 定義一個混合對象
    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!"
    

選項合并

  • 當混合對象與組件包含同名選項時,這些選項將以適當?shù)牟呗?/strong>合并。
  • 混合的鉤子將在組件自己的鉤子之前調(diào)用。
  • 注意 Vue.extend() 使用同樣的合并策略。

全局混合

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

自定義選項合并策略

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

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

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

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

插件

開發(fā)插件

[正在整理]

使用插件

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

    // 調(diào)用 `MyPlugin.install(Vue)`
    Vue.use(MyPlugin)
    

    也可以傳入一個選項對象

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

構(gòu)建大型應(yīng)用

  • 在 Vue 中可以使用諸如 Jade 之類的預(yù)處理器。

路由

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

與服務(wù)器通信

狀態(tài)管理

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

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