vue組件深入

組件注冊

組件名

在注冊一個組件的時候,我們始終需要給它一個名字。

Vue.component('my-component-name',{ /* ... */ })

該組件名就是Vue.component的第一個參數。

組件名大小寫

定義組件名的方式有兩種:

使用短橫線分隔命名
Vue.component('my-component-name', { /* ... */ })

當使用短橫線分隔命名定義一個組件時,必須在引用這個自定義元素時使用短橫線分隔命名,例如 <my-component-name>

使用駝峰式命名
Vue.component('MyComponentName', { /* ... */ })

當使用駝峰式命名定義一個組件時,引用這個自定義元素時兩種命名法都可以使用。也就是說<my-component-name><MyComponentName>都是可接受的。注意,盡管如此,直接在DOM(即非字符串的模板)中使用時只有短橫線分隔命名是有效的。

全局注冊

Vue.component('my-component-name',{
  // ... 選項 ...
})

這些組件是全局注冊的。也就是說它們在注冊之后可以用在任何新創建的Vue根實例 (new Vue)的模板中。

Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
new Vue({ el: '#app' })

<div id="app">
  <component-a></component-a>
  <component-b></component-b>
</div>

在所有子組件中也是如此,也就是說這兩個組件在各自內部也都可以相互使用。

局部注冊

通過一個普通的JavaScript對象來定義組件。

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }

然后在components選項中定義要使用的組件。

new Vue({
  el: '#app'
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

對于components對象中的每個屬性來說,其屬性名就是自定義元素的名字,其屬性值就是這個組件的選項對象。
注意局部注冊的組件在其子組件中不可用。例如,如果你希望ComponentAComponentB中可用,則你需要這樣寫:

var ComponentA = { /* ... */ }
var ComponentB = {
  components: {
    'component-a':ComponentA
  },
  // ...
}

或者通過Babel和webpack使用ES2015模塊。

import ComponentA from './ComponentA.vue'

export default {
  components: {
    ComponentA
  },
  // ...
}

在 ES2015+中,在對象中放一個類似ComponentA的變量名其實是ComponentA:ComponentA的縮寫,即這個變量名同時是:

  • 用在模板中的自定義元素的名稱
  • 包含了這個組件選項的變量名

模塊系統

在模塊系統中局部注冊

創建一個components目錄,并將每個組件放置在其各自的文件中。
然后在局部注冊之前導入每個你想使用的組件。例如,在一個假設的ComponentB.jsComponentB.vue文件中:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

現在ComponentAComponentC都可以在ComponentB的模板中使用了。

基礎組件的自動化全局注冊

可能你的許多組件只是包裹了一個輸入框或按鈕之類的元素,是相對通用的。我們有時候會把它們稱為基礎組件,它們會在各個組件中被頻繁的用到。
所以會導致很多組件里都會有一個包含基礎組件的長列表。

import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}

而只是用于模板中的一小部分。

<BaseInput v-model="searchText" @keydown.enter="search"/>
<BaseButton @click="search">
  <BaseIcon name="search"/>
</BaseButton>

如果你使用了webpack,那么就可以使用 require.context 只全局注冊這些非常通用的基礎組件。這里有一份可以讓你在應用入口文件 (比如 src/main.js) 中全局導入基礎組件的示例代碼:

import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
  './components',  // 其組件目錄的相對路徑
  false,  // 是否查詢其子目錄
  // 匹配基礎組件文件名的正則表達式
  /Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
  // 獲取組件配置
  const componentConfig = requireComponent(fileName)
  // 獲取組件的PascalCase命名
  const componentName = upperFirst(
    camelCase(
      // 剝去文件名開頭的 `'./` 和結尾的擴展名
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    )
  )
  // 全局注冊組件
  Vue.component(
    componentName,
    // 如果這個組件選項是通過export default導出的,
    // 那么就會優先使用.default,否則回退到使用模塊的根。
    componentConfig.default || componentConfig
  )
})

全局注冊的行為必須在根Vue實例(通過new Vue)創建之前發生。

Prop

Prop的大小寫

HTML中的特性名是大小寫不敏感的,所以瀏覽器會把所有大寫字符解釋為小寫字符。這意味著當你使用DOM中的模板時,駝峰命名法的prop名需要使用其等價的短橫線分隔命名。

Vue.component('blog-post', {
  // 在JavaScript中是駝峰命名法的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML中是短橫線分隔命名的 -->
<blog-post post-title="hello!"></blog-post>

如果使用字符串模板,那么這個限制就不存在了。

靜態和動態的Prop

可以像這樣給prop傳入一個靜態的值。

<blog-post title="My journey with Vue"></blog-post>

prop還可以通過v-bind動態賦值。

<blog-post v-bind:title="post.title"></blog-post>

任何類型的值都可以傳給一個prop

傳入一個數字
<!-- 即便42是靜態的,我們仍然需要v-bind來告訴Vue -->
<!-- 這是一個JavaScript表達式而不是一個字符串 -->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一個變量進行動態賦值 -->
<blog-post v-bind:likes="post.likes"></blog-post>
傳入一個布爾值
<!-- 包含該prop沒有值的情況在內,都意味著true -->
<blog-post favorited></blog-post>
<!-- 即便false是靜態的,我們仍然需要v-bind來告訴Vue -->
<!-- 這是一個JavaScript表達式而不是一個字符串。-->
<base-input v-bind:favorited="false">
<!-- 用一個變量進行動態賦值。-->
<base-input v-bind:favorited="post.currentUserFavorited">
傳入一個數組
<!-- 即便數組是靜態的,我們仍然需要v-bind來告訴Vue -->
<!-- 這是一個JavaScript表達式而不是一個字符串 -->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
<!-- 用一個變量進行動態賦值 -->
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>
傳入一個對象
<!-- 即便對象是靜態的,我們仍然需要v-bind來告訴Vue -->
<!-- 這是一個JavaScript表達式而不是一個字符串 -->
<blog-post v-bind:comments="{ id: 1, title: 'My Journey with Vue' }"></blog-post>
<!-- 用一個變量進行動態賦值 -->
<blog-post v-bind:post="post"></blog-post>
傳入一個對象的所有屬性

如果你想要將一個對象的所有屬性都作為prop傳入,可以使用不帶參數的v-bind(取代v-bind:prop-name)。

post: {
  id: 1,
  title: 'My Journey with Vue'
}
//下面的模板:
<blog-post v-bind="post"></blog-post>
//等價于:
<blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post>

單向數據流

所有的prop都使得其父子prop之間形成了一個單向下行綁定:父級prop的更新會向下流動到子組件中,但是反過來則不行。這樣會防止從子組件意外改變父級組件的狀態,從而導致你的應用的數據流向難以理解。
額外的,每次父級組件發生更新時,子組件中所有的prop都將會刷新為最新的值。這意味著你不應該在一個子組件內部改變prop。如果你這樣做了,Vue會在瀏覽器的控制臺中發出警告。
這里有兩種常見的試圖改變一個prop的情形:

  1. 這個prop用來傳遞一個初始值;這個子組件接下來希望將其作為一個本地的prop數據來使用。在這種情況下,最好定義一個本地的data屬性并將這個prop用作其初始值。
props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}
  1. 這個prop以一種原始的值傳入且需要進行轉換。在這種情況下,最好使用這個prop的值來定義一個計算屬性。
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

注意在JavaScript中對象和數組是通過引用傳入的,所以對于一個數組或對象類型的prop來說,在子組件中改變這個對象或數組本身將會影響到父組件的狀態。

Prop驗證

我們可以為組件的prop指定需求。如果有一個需求沒有被滿足,則Vue會在瀏覽器控制臺中警告你。
為了定制prop的驗證方式,可以為props中的值提供一個帶有驗證需求的對象,而不是一個字符串數組。

Vue.component('my-component', {
  props: {
    // 基礎的類型檢查 (`null` 匹配任何類型)
    propA: Number,
    // 多個可能的類型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true
    },
    // 帶有默認值的數字
    propD: {
      type: Number,
      default: 100
    },
    // 帶有默認值的對象
    propE: {
      type: Object,
      // 對象或數組且一定會從一個工廠函數返回默認值
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定義驗證函數
    propF: {
      validator: function (value) {
        // 這個值必須匹配下列字符串中的一個
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

prop驗證失敗的時候,(開發環境構建版本的)Vue將會產生一個控制臺的警告。
注意那些prop會在一個組件實例創建之前進行驗證,所以實例的屬性 (如datacomputed等) 在defaultvalidator函數中是不可用的。

類型檢查

type可以是下列原生構造函數中的一個:StringNumberBooleanFunctionObjectArraySymbol
額外的,type還可以是一個自定義的構造函數,并且通過instanceof來進行檢查確認。例如,給定下列現成的構造函數:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

你可以使用:

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

來驗證author prop的值是否是通過new Person創建的。

非Prop的特性

一個非prop特性是指傳向一個組件,但是該組件并沒有相應prop定義的特性。
因為顯式定義的prop適用于向一個子組件傳入信息,然而組件庫的作者并不總能預見組件會被用于怎樣的場景。這也是為什么組件可以接受任意的特性,而這些特性會被添加到這個組件的根元素上。
例如,想象一下你通過一個Bootstrap插件使用了一個第三方的<bootstrap-data-input>組件,這個插件需要在其<input>上用到一個data-date-picker特性。我們可以將這個特性添加到你的組件實例上:

<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>

然后這個data-date-picker="activated"特性就會自動添加到<bootstrap-date-input>的根元素上。

替換/合并已有的特性

想象一下 <bootstrap-date-input> 的模板是這樣的:

<input type="date" class="form-control">

為了給我們的日期選擇器插件定制一個主題,我們可能需要像這樣添加一個特別的類名:

<bootstrap-date-input
 data-date-picker="activated"
 class="date-picker-theme-dark"
></bootstrap-date-input>

在這種情況下,我們定義了兩個不同的 class 的值:

  • form-control,這是在組件的模板內設置好的
  • date-picker-theme-dark,這是從組件的父級傳入的

對于絕大多數特性來說,從外部提供給組件的值會替換掉組件內部設置好的值。所以如果傳入 type="text" 就會替換掉 type="date" 并把它破壞!慶幸的是,classstyle 特性會稍微智能一些,即兩邊的值會被合并起來,從而得到最終的值:form-control date-picker-theme-dark

禁用特性繼承

如果你不希望組件的根元素繼承特性,你可以設置在組件的選項中設置inheritAttrs: false。例如:

Vue.component('my-component', {
  inheritAttrs: false,
  // ...
})

這尤其適合配合實例的 $attrs 屬性使用,該屬性包含了傳遞給一個組件的特性名和特性值,例如:

{
 class: 'username-input',
 placeholder: 'Enter your username'
}

有了 inheritAttrs: false$attrs,你就可以手動決定這些特性會被賦予哪個元素。在撰寫基礎組件的時候是常會用到的:

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on:input="$emit('input', $event.target.value)"
      >
    </label>
  `
})

這個模式允許你在使用基礎組件的時候更像是使用原始的HTML元素,而不會擔心哪個元素是真正的根元素:

<base-input
  v-model="username"
  class="username-input"
  placeholder="Enter your username"
></base-input>

插槽

插槽內容

Vue實現了一套內容分發的API,將<slot>元素作為承載分發內容的出口。
它允許你像這樣合成組件:

<navigation-link url="/profile">
  Your Profile
</navigation-link>

然后你在<navigation-link>的模板中可能會寫為:

<a v-bind:href="url" class="nav-link">
  <slot></slot>
</a>

當組件渲染的時候,這個<slot>元素將會被替換為Your Profile。插槽內可以包含任何模板代碼,包括 HTML。

<navigation-link url="/profile">
  <!-- 添加一個 Font Awesome 圖標 -->
  <span class="fa fa-user"></span>
  Your Profile
</navigation-link>

甚至其它的組件。

<navigation-link url="/profile">
  <!-- 添加一個圖標的組件 -->
  <font-awesome-icon name="user"></font-awesome-icon>
  Your Profile
</navigation-link>

如果<navigation-link>沒有包含一個<slot>元素,則任何傳入它的內容都會被拋棄。

具名插槽

有些時候我們需要多個插槽。例如,一個假設的<base-layout>組件多模板如下:

<div class="container">
  <header>
    <!-- 我們希望把頁頭放這里 -->
  </header>
  <main>
    <!-- 我們希望把主要內容放這里 -->
  </main>
  <footer>
    <!-- 我們希望把頁腳放這里 -->
  </footer>
</div>

對于這樣的情況,<slot>元素有一個特殊的特性:name。這個特性可以用來定義額外的插槽。

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

在向具名插槽提供內容的時候,我們可以在一個父組件的<template>元素上使用slot特性。

<base-layout>
  <template slot="header">
    <h1>Here might be a page title</h1>
  </template>
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>
  <template slot="footer">
    <p>Here's some contact info</p>
  </template>
</base-layout>

另一種slot特性的用法是直接用在一個普通的元素上:

<base-layout>
  <h1 slot="header">Here might be a page title</h1>
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>
  <p slot="footer">Here's some contact info</p>
</base-layout>

我們還是可以保留一個未命名插槽,這個插槽是默認插槽,也就是說它會作為所有未匹配到插槽的內容的統一出口。上述兩個示例渲染出來的HTML都將會是:

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

插槽的默認內容

有的時候為插槽提供默認的內容是很有用的。例如,一個<submit-button>組件可能希望這個按鈕的默認內容是“Submit”,但是同時允許用戶覆寫為“Save”、“Upload”或別的內容。
可以在<slot>標簽內部指定默認的內容來做到這一點。

<button type="submit">
  <slot>Submit</slot>
</button>

如果父組件為這個插槽提供了內容,則默認的內容會被替換掉。

自定義事件

事件名

跟組件和prop不同,事件名不存在任何自動化的大小寫轉換。而是觸發的事件名需要完全匹配監聽這個事件所用的名稱。如果觸發一個駝峰式命名名字的事件:

this.$emit('myEvent')

則監聽這個名字的短橫線分隔命名版本是不會有任何效果的:

<my-component v-on:my-event="doSomething"></my-component>

跟組件和prop不同,事件名不會被用作一個JavaScript變量名或屬性名,所以就沒有理由使用駝峰式命名了。并且v-on事件監聽器在DOM模板中會被自動轉換為全小寫 (因為HTML是大小寫不敏感的),所以v-on:myEvent將會變成v-on:myevent——導致myEvent不可能被監聽到。
因此,我們推薦你始終使用短橫線分隔命名的事件名。

自定義組件的v-model

2.2.0+ 新增

一個組件上的v-model默認會利用名為 valueprop和名為input的事件,但是像單選框、復選框等類型的輸入控件可能會將value特性用于不同的目的。model選項可以用來避免這樣的沖突:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

現在在這個組件上使用v-model的時候:

<base-checkbox v-model="lovingVue"></base-checkbox>

這里的lovingVue的值將會傳入這個名為checkedprop。同時當<base-checkbox>觸發一個change事件并附帶一個新的值的時候,這個lovingVue的屬性將會被更新。
注意你仍然需要在組件的props選項里聲明checked這個prop

將原生事件綁定到組件

要在一個組件的根元素上直接監聽一個原生事件。可以使用-on.native修飾符:

<base-input v-on:focus.native="onFocus"></base-input>

有的時候這是很有用的,不過在你嘗試監聽一個類似<input>的非常特定的元素時,這并不是個好主意。比如上述<base-input>組件可能做了如下重構,所以根元素實際上是一個<label>元素:

<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  >
</label>

這時,父級的.native監聽器將靜默失敗。它不會產生任何報錯,但是onFocus處理函數不會被調用。
為了解決這個問題,Vue提供了一個$listeners屬性,它是一個對象,里面包含了作用在這個組件上的所有監聽器。

{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

有了這個$listeners屬性,你就可以配合v-on="$listeners"將所有的事件監聽器指向這個組件的某個特定的子元素。對于類似<input>的你希望它也可以配合v-model工作的組件來說,為這些監聽器創建一個類似下述inputListeners的計算屬性通常是非常有用的:

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` 將所有的對象合并為一個新對象
      return Object.assign({},
        // 我們從父級添加所有的監聽器
        this.$listeners,
        // 然后我們添加自定義監聽器,
        // 或覆寫一些監聽器的行為
        {
          // 這里確保組件配合 `v-model` 的工作
          input: function (event) {
            vm.$emit('input', event.target.value)
          }
        }
      )
    }
  },
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on="inputListeners"
      >
    </label>
  `
})

現在<base-input>組件是一個完全透明的包裹器了,也就是說它可以完全像一個普通的<input>元素一樣使用了:所有跟它相同的特性和監聽器的都可以工作。

.sync修飾符

2.3.0+ 新增

在有些情況下,我們可能需要對一個 prop 進行“雙向綁定”。不幸的是,真正的雙向綁定會帶來維護上的問題,因為子組件可以修改父組件,且在父組件和子組件都沒有明顯的改動來源。
我們推薦以 update:my-prop-name 的模式觸發事件取而代之。舉個例子,在一個包含 title prop 的假設的組件中,我們可以用以下方法表達對其賦新值的意圖:

this.$emit('update:title', newTitle)

然后父組件可以監聽那個事件并根據需要更新一個本地的數據屬性。例如:

<text-document
 v-bind:title="doc.title"
 v-on:update:title="doc.title = $event"
></text-document>

為了方便起見,我們為這種模式提供一個縮寫,即 .sync 修飾符:

<text-document v-bind:title.sync="doc.title"></text-document>

當我們用一個對象同時設置多個 prop 的時候,也可以將這個 .sync 修飾符和 v-bind 配合使用:

<text-document v-bind.sync="doc"></text-document>

這樣會把 doc 對象中的每一個屬性 (如 title) 都作為一個獨立的 prop 傳進去,然后各自添加用于更新的 v-on 監聽器。
v-bind.sync 用在一個字面量的對象上,例如 v-bind.sync=”{ title: doc.title }”,是無法正常工作的,因為在解析一個像這樣的復雜表達式的時候,有很多邊緣情況需要考慮。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容

  • 長尾經濟,可以是內部范圍經濟(學習經濟和創意經濟)和外部范圍經濟(集群經濟和網絡經濟)的結合。作為產業集群意義上的...
    云丹風卿閱讀 118評論 0 0
  • 覺察日記 1.事件:兒子這幾天敢騎車上路了,要騎自行車回老家,我騎電車,他騎自行車,有些逆風,他一氣騎了三十多里路...
    芝麻花開節節高閱讀 127評論 0 0
  • 從此以后 快樂又癡癲 吹下每一片搖搖欲墜的花瓣 搭公車去遠遠的地方,好奇張望 不問歸期 從此以后 孤獨又自由 對著...
    妖怪張小山閱讀 211評論 0 1
  • 為什么你輕易俘虜我的心, 我卻難以將你的目光拉近 ? 愛情懂原諒, 但不談愧疚。 遇見 , 可以是燈火闌珊, 也無...
    云水慧心閱讀 230評論 0 0
  • 文/清荷沐陽 ~1~ “我昨天提交的論文題目被刷下來了,現又重新提交,不知能否能過?”五妹吉吉突然在我們的專屬寫作...
    清荷沐陽閱讀 472評論 24 24