通過(guò)源碼深入了解 vue3 的 ref vs reactive

最近剛開(kāi)始用 vue3灌砖,其中組合式 API 的 ref 和 reactive 兩者讓我有些困惑:

  • 它們都返回響應(yīng)式的數(shù)據(jù)送漠,那么它們兩者的區(qū)別在哪活鹰?
  • 它們的原理是怎樣的分冈?

于是就有了看源碼的想法胁镐,源碼是直接從 https://unpkg.com/vue@3/dist/vue.global.js 上保存下來(lái)的偎血。后續(xù)就可以在源碼上面調(diào)試學(xué)習(xí)啦。

結(jié)論

先說(shuō)結(jié)論6⑵(我知道很多朋友不喜歡看過(guò)程颇玷,只要結(jié)論。比如我自己 0,0)

  • 參數(shù)
    • ref() 函數(shù)的參數(shù)既可以是原始類型(string就缆、number帖渠、boolean)也可以是對(duì)象類型(對(duì)象、數(shù)組竭宰、Set空郊、Map)。
    • 如果將一個(gè)對(duì)象類型的數(shù)據(jù)賦值給 ref() 函數(shù)切揭,這個(gè)對(duì)象將通過(guò) reactive() 轉(zhuǎn)為具有深層次響應(yīng)式的對(duì)象狞甚。
    • reactive() 函數(shù)只有在接收對(duì)象類型是響應(yīng)式的。它也可以接收 ref 函數(shù)返回的對(duì)象伴箩,不過(guò)如果需要解構(gòu)就需要使用對(duì)象包裹入愧。如 { a: refObj }
  • 返回值
    • ref() 接受一個(gè)內(nèi)部值,并返回一個(gè)響應(yīng)式的嗤谚、可更改的 ref 對(duì)象棺蛛。該對(duì)象通過(guò)內(nèi)部值 .value 的 setter 和 getter 來(lái)獲取和修改內(nèi)部數(shù)據(jù),如 count.value = 4巩步。
    • reactive() 函數(shù)返回一個(gè)對(duì)象的深層次響應(yīng)式代理旁赊。

他們最終的目的都是能響應(yīng)式渲染模板(即數(shù)據(jù)變化后網(wǎng)頁(yè)內(nèi)容也隨之變化)。

ref

源碼

先看下 ref 的源碼椅野,ref() 函數(shù)執(zhí)行了 createRef() 函數(shù)终畅,而 createRef() 中實(shí)例化了 RefImpl 類。

function ref(value) {
  return createRef(value, false)
}

function createRef(rawValue, shallow) {
  // 如果已經(jīng)是 ref 則直接返回
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

RefImpl 類中除了構(gòu)造函數(shù)竟闪,只有一個(gè) value 內(nèi)部值的 setter 和 getter 函數(shù)离福。在構(gòu)造函數(shù)中 _rawValue 是原始數(shù)據(jù),而 _value 是響應(yīng)數(shù)據(jù)(如果數(shù)據(jù)是對(duì)象類型則為 Proxy)炼蛤。

那么 _value 是如何來(lái)的妖爷?如果不是淺層響應(yīng)式,則會(huì)調(diào)用 toReactive 函數(shù)理朋。

class RefImpl {
  constructor(value, __v_isShallow) {
    this.__v_isShallow = __v_isShallow
    this.dep = undefined
    this.__v_isRef = true
    this._rawValue = __v_isShallow ? value : toRaw(value)
    this._value = __v_isShallow ? value : toReactive(value)
  }
  get value() {
    trackRefValue(this)
    return this._value
  }
  set value(newVal) {
    const useDirectValue =
      this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}

toRaw() 函數(shù)中絮识,遞歸獲取數(shù)據(jù)的原始數(shù)據(jù)绿聘。(reactive() 函數(shù)返回的代理對(duì)象中帶有 __v_raw 標(biāo)簽,它會(huì)讓 getter 函數(shù)返回原始數(shù)據(jù))

function toRaw(observed) {
  const raw = observed && observed['__v_raw' /* ReactiveFlags.RAW */]
  return raw ? toRaw(raw) : observed
}

toReactive() 函數(shù)中次舌,就可以看到已經(jīng)使用 reactive() 函數(shù)的邏輯了熄攘。

如果將一個(gè)對(duì)象賦值給 ref,那么這個(gè)對(duì)象將通過(guò) reactive() 轉(zhuǎn)為具有深層次響應(yīng)式的對(duì)象彼念。

const toReactive = (value) => (isObject(value) ? reactive(value) : value)

順便瞅一眼 isObject() 函數(shù)挪圾,對(duì)象類型的判定就是 typeof val === 'object'。不過(guò)由于 JavaScript 的缺陷逐沙,所以 typeof null 也是 object洛史,需要排除掉。

const isObject = (val) => val !== null && typeof val === 'object'

小實(shí)驗(yàn)

實(shí)驗(yàn)出真知

const a = ref('123')
a.value += '456'
// '123456'
const b = ref(6)
b.value += 8
// 14
const c = ref(false)
c.value = !c.value
// true

const r3 = ref(false)
r3.value = true
r3.value = 'oh li gei' // value 是不限定類型的
// oh li gei

const d = ref(null)
// null
const e = ref(undefined)
// undefined
const f = ref(Symbol())
// Symbol()

// 這里打贏 ref 返回的對(duì)象
const g = ref({ a: [1, 2, 3] })
// RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: Array(4), _value: Proxy}
const h = ref([3, 4, 5, 6])
// RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: Array(4), _value: Proxy}
const i = ref(new Set())
// RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: Set(1), _value: Proxy}

// 驗(yàn)證 toRaw 函數(shù)的 __v_raw 屬性
const x = reactive({ a: 1, b: { c: { d: 3 } } })
const y = ref(x)
console.log('x', x.__v_raw) // { a: 1, b: { c: { d: 3 } } }
console.log('y', y.__v_raw) // undefined
  • 對(duì)于字符串酱吝、數(shù)字也殖、布爾類型來(lái)說(shuō),ref 函數(shù)可以讓這些數(shù)據(jù)變成響應(yīng)式的务热。
  • 對(duì)于 null忆嗜、undefined、symbol 這類特殊數(shù)據(jù)崎岂,ref 函數(shù)返回值還是其本身捆毫,無(wú)意義。
  • 對(duì)于對(duì)象類型數(shù)據(jù)冲甘,正如源碼所說(shuō)绩卤,對(duì)數(shù)據(jù)使用了 reactive() 來(lái)進(jìn)行深層響應(yīng)式代理。從 ref 返回的對(duì)象可以看出江醇,_rawValue 是原始數(shù)據(jù)濒憋,而 _value 是數(shù)據(jù)的代理。
  • reactive 函數(shù)返回的代理對(duì)象中帶有 __v_raw 標(biāo)簽陶夜,會(huì)返回原始數(shù)據(jù)

reactive

源碼

reactive()

reactive() 函數(shù)除了判斷只讀外就只是調(diào)用了 createReactiveObject() 函數(shù)凛驮。

createReactiveObject() 函數(shù)中,排除了各種不需要代理的情況条辟,并根據(jù)數(shù)據(jù)類型不同進(jìn)行不同的代理邏輯處理黔夭。最后將代理結(jié)構(gòu)記錄到一個(gè) Map 中。

function reactive(target) {
  // 如果 target 是 Readonly 的代理羽嫡,返回自身
  if (isReadonly(target)) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap,
  )
}

function createReactiveObject(
  target,
  isReadonly,
  baseHandlers,
  collectionHandlers,
  proxyMap,
) {
  // target 不是對(duì)象類型本姥,返回自身
  if (!isObject(target)) {
    {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target 已經(jīng)是代理,返回自身
  if (
    target['__v_raw' /* ReactiveFlags.RAW */] &&
    !(isReadonly && target['__v_isReactive' /* ReactiveFlags.IS_REACTIVE */])
  ) {
    return target
  }
  // target 已經(jīng)有響應(yīng)的代理杭棵,返回代理
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  /**
   * 判斷 target 數(shù)據(jù)類型
   * 0 無(wú)效婚惫,直接返回
   * 1 COMMON 類型,使用 baseHandlers 代理配置
   * 2 COLLECTION 類型,使用 collectionHandlers 代理配置
   */
  const targetType = getTargetType(target)
  if (targetType === 0 /* TargetType.INVALID */) {
    return target
  }
  const proxy = new Proxy(
    target,
    targetType === 2 /* TargetType.COLLECTION */
      ? collectionHandlers
      : baseHandlers,
  )
  // 記錄代理關(guān)系
  proxyMap.set(target, proxy)
  return proxy
}

數(shù)據(jù)類型判斷

判斷數(shù)據(jù)類型的代碼如下辰妙,根據(jù)不同的數(shù)據(jù)分為:

  • 0 無(wú)效數(shù)據(jù)類型,不進(jìn)行代理返回自身甫窟。
  • 1 普通對(duì)象類型
  • 2 收集器類型

類型的獲取是通過(guò) Object.prototype.toString.call(target) 獲取到 '[object Set]' 這類字符串密浑,并截取 Set 這段有效字符串返回。

function getTargetType(value) {
  return value['__v_skip' /* ReactiveFlags.SKIP */] ||
    !Object.isExtensible(value)
    ? 0 /* TargetType.INVALID */
    : targetTypeMap(toRawType(value))
}

function targetTypeMap(rawType) {
  switch (rawType) {
    case 'Object':
    case 'Array':
      return 1 /* TargetType.COMMON */
    case 'Map':
    case 'Set':
    case 'WeakMap':
    case 'WeakSet':
      return 2 /* TargetType.COLLECTION */
    default:
      return 0 /* TargetType.INVALID */
  }
}

普通類數(shù)據(jù)代理

reactive() 函數(shù)中可以看到粗井,代理配置分別使用的是 mutableHandlers 和 mutableCollectionHandlers尔破。

普通類型的代理配置 mutableHandlers 如下。這里代碼量較大浇衬,暫時(shí)就只貼出 getter 和 settter 函數(shù)懒构。

const mutableHandlers = {
  get: get$1, // get 方法用于攔截某個(gè)屬性的讀取操作
  set: set$1, // set 方法用來(lái)攔截某個(gè)屬性的賦值操作
  deleteProperty, // deleteProperty 方法用于攔截 delete 操作
  has: has$1, // has() 方法用來(lái)攔截HasProperty操作
  ownKeys, //  ownKeys() 方法用來(lái)攔截對(duì)象自身屬性的讀取操作
}

在 createGetter 中的處理邏輯如下:

  • 如果是標(biāo)簽 __v_raw 等則返回響應(yīng)的值;
  • 如果是數(shù)組 API 的關(guān)鍵字 inclueds push 等就用 arrayInstrumentations 進(jìn)行處理耘擂;(所以可以在 reactive 的返回值中直接使用數(shù)組 API arr.push()
  • 通過(guò) Reflect.get() 獲取到目標(biāo)返回值胆剧。
  • 如果返回值是一個(gè)對(duì)象,且不是只讀數(shù)據(jù)醉冤。那么就以遞歸的方式對(duì)這個(gè)子對(duì)象使用 reactive() 函數(shù)繼續(xù)綁定響應(yīng)式代理秩霍。(即深層響應(yīng)式轉(zhuǎn)換)
  • 返回最終結(jié)果。
const get$1 = /*#__PURE__*/ createGetter()

function createGetter(isReadonly = false, shallow = false) {
  return function get(target, key, receiver) {
    if (key === '__v_isReactive' /* ReactiveFlags.IS_REACTIVE */) {
      return !isReadonly
    } else if (key === '__v_isReadonly' /* ReactiveFlags.IS_READONLY */) {
      return isReadonly
    } else if (key === '__v_isShallow' /* ReactiveFlags.IS_SHALLOW */) {
      return shallow
    } else if (
      key === '__v_raw' /* ReactiveFlags.RAW */ &&
      receiver ===
        (isReadonly
          ? shallow
            ? shallowReadonlyMap
            : readonlyMap
          : shallow
          ? shallowReactiveMap
          : reactiveMap
        ).get(target)
    ) {
      return target
    }
    const targetIsArray = isArray(target)
    if (!isReadonly) {
      if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
        return Reflect.get(arrayInstrumentations, key, receiver)
      }
      if (key === 'hasOwnProperty') {
        return hasOwnProperty
      }
    }
    const res = Reflect.get(target, key, receiver) // Reflect.get 方法查找并返回target對(duì)象的name屬性
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }
    if (!isReadonly) {
      track(target, 'get' /* TrackOpTypes.GET */, key)
    }
    if (shallow) {
      return res
    }
    if (isRef(res)) {
      // ref 解構(gòu)取值
      return targetIsArray && isIntegerKey(key) ? res : res.value
    }
    if (isObject(res)) {
      // 遞歸生成響應(yīng)式代理
      return isReadonly ? readonly(res) : reactive(res)
    }
    return res
  }
}

在 set 函數(shù)中

  • 首先是調(diào)用 toRaw() 函數(shù)將 value 和 oldValue 遞歸從代理變?yōu)樵紨?shù)據(jù)蚁阳。原理大致如 reactive({ a: 1 }).__v_raw // output: { a: 1}
  • 如果 oldValue 是 ref() 函數(shù)返回的铃绒,則進(jìn)行解構(gòu)賦值。
  • 通過(guò) Reflect.set() 函數(shù)對(duì)代理目標(biāo) target 進(jìn)行賦值螺捐。
const set$1 = /*#__PURE__*/ createSetter()

function createSetter(shallow = false) {
  return function set(target, key, value, receiver) {
    let oldValue = target[key]
    // 不需要更新的情況
    if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
      return false
    }
    if (!shallow) {
      if (!isShallow(value) && !isReadonly(value)) {
        oldValue = toRaw(oldValue) // 遞歸轉(zhuǎn)為原始數(shù)據(jù)
        value = toRaw(value) // 遞歸轉(zhuǎn)為原始數(shù)據(jù)
      }
      // ref 解構(gòu)賦值
      if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
        oldValue.value = value
        return true
      }
    }
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    const result = Reflect.set(target, key, value, receiver) // Reflect.set 方法設(shè)置 target 對(duì)象的 name 屬性等于value颠悬。
    // don't trigger if target is something up in the prototype chain of original
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        trigger(target, 'add' /* TriggerOpTypes.ADD */, key, value)
      } else if (hasChanged(value, oldValue)) {
        trigger(target, 'set' /* TriggerOpTypes.SET */, key, value, oldValue)
      }
    }
    return result
  }
}

收集器類數(shù)據(jù)代理

收集器類型的代理配置只有一個(gè) getter 函數(shù),它對(duì)收集器類型數(shù)據(jù)的 API 進(jìn)行了定義定血。

如果調(diào)用 set.add() map.get() 這類 API赔癌,就會(huì)去調(diào)用 instrumentations 對(duì)象中相應(yīng)的函數(shù)。否則就返回代理目標(biāo) target 自身澜沟。

const mutableCollectionHandlers = {
  get: /*#__PURE__*/ createInstrumentationGetter(false, false),
}

function createInstrumentationGetter(isReadonly, shallow) {
  const instrumentations = shallow
    ? isReadonly
      ? shallowReadonlyInstrumentations
      : shallowInstrumentations
    : isReadonly
    ? readonlyInstrumentations
    : mutableInstrumentations
  return (target, key, receiver) => {
    if (key === '__v_isReactive' /* ReactiveFlags.IS_REACTIVE */) {
      return !isReadonly
    } else if (key === '__v_isReadonly' /* ReactiveFlags.IS_READONLY */) {
      return isReadonly
    } else if (key === '__v_raw' /* ReactiveFlags.RAW */) {
      return target
    }
    return Reflect.get(
      hasOwn(instrumentations, key) && key in target
        ? instrumentations
        : target,
      key,
      receiver,
    )
  }
}

const mutableInstrumentations = {
  get(key) {
    return get(this, key)
  },
  get size() {
    return size(this)
  },
  has,
  add,
  set,
  delete: deleteEntry,
  clear,
  forEach: createForEach(false, false),
}

function add(value) {
  value = toRaw(value) // 去代理
  const target = toRaw(this) // 去代理
  const proto = getProto(target)
  const hadKey = proto.has.call(target, value)
  if (!hadKey) {
    target.add(value) // 執(zhí)行原生函數(shù)
    trigger(target, 'add' /* TriggerOpTypes.ADD */, value, value)
  }
  return this
}

function set(key, value) {
  value = toRaw(value)
  const target = toRaw(this)
  const { has, get } = getProto(target)
  let hadKey = has.call(target, key)
  if (!hadKey) {
    key = toRaw(key)
    hadKey = has.call(target, key)
  } else {
    checkIdentityKeys(target, has, key)
  }
  const oldValue = get.call(target, key)
  target.set(key, value)
  if (!hadKey) {
    trigger(target, 'add' /* TriggerOpTypes.ADD */, key, value)
  } else if (hasChanged(value, oldValue)) {
    trigger(target, 'set' /* TriggerOpTypes.SET */, key, value, oldValue)
  }
  return this
}

源碼小結(jié)

  • reactive 函數(shù)只對(duì) Object Array Map Set WeakMap WeakSet 類型的數(shù)據(jù)生效届榄,且分為了兩種處理方式。
  • reactive 函數(shù)會(huì)排除各種不符合條件的數(shù)據(jù)倔喂,返回?cái)?shù)據(jù)本身铝条。
  • reactive 函數(shù)是通過(guò)代理 Proxy 實(shí)現(xiàn)數(shù)據(jù)的存取的。
  • reactive 中的 __v_raw __v_isShallow 并不是屬性值席噩,而是判斷標(biāo)簽班缰。會(huì)根據(jù)標(biāo)簽返回相應(yīng)結(jié)果。
  • reactive 對(duì)于 ref 對(duì)象的解構(gòu)其實(shí)就是在 get 的時(shí)候取 .value 值悼枢,而在 set 的時(shí)候?qū)⒅祩鹘o .value埠忘。
  • reactive 對(duì)于 Set、Map 這類數(shù)據(jù),僅提供了 getter 方法莹妒。如果調(diào)用這類數(shù)據(jù) API 函數(shù)名船,vue 在做了數(shù)據(jù)處理后會(huì)去調(diào)用它的原生函數(shù)。如果是獲取數(shù)據(jù)內(nèi)容旨怠,則直接返回?cái)?shù)據(jù)本身渠驼。
  • 對(duì)象類型數(shù)據(jù)想要變成響應(yīng)式的,就必須用 reactive 函數(shù)代理鉴腻。
  • 上面代碼中用到了 ES6 的 Reflect 和 Proxy 迷扇,關(guān)于它們的更多內(nèi)容可以訪問(wèn) Proxy - ECMAScript 6 入門Reflect - ECMAScript 6 入門 了解。

小實(shí)驗(yàn)

以下寫法 reactive() 返回值是其自身爽哎,但不是響應(yīng)式的蜓席。而且 vue 會(huì)發(fā)出警告:value cannot be made reactive: 123

var a = reactive('123')
// 123

function add() {
  a += '456' // 變量 a 有變化,但是 HTML 無(wú)變化
}

var b = reactive(6)
b += 8
// 16

const c = reactive(false)
// false
setTimeout(() => {
  c = true // c 變?yōu)?true课锌,但是 HTML 無(wú)變化
}, 1000)

const d = reactive(null)
// null

const e = reactive(undefined)
// undefined

const f = reactive(Symbol())
// Synbol()

下面這些情況可以正常使用 reactive() 函數(shù)厨内。

const g = reactive({ a: 1, b: { c: 3 } })
g.a++
// Proxy: { a: 2, b: { c: 3 } }
setInterval(() => {
  // 網(wǎng)頁(yè)會(huì)每秒變化數(shù)據(jù)
  g.a++
  g.b.c += 2
}, 1000)

const h = reactive([3, 4, 5, 6])
h.push(8)
// Proxy: {0: 3, 1: 4, 2: 5, 3: 6, 4: 8}

const i = reactive(new Set())
i.add('2')
i.add({ b: 3 })
i.add(321)
// Proxy: { "Set(4)": [ "2", { "b": 3 }, 321 ] }
setTimeout(() => {
  i.add(null)
  // Proxy: { "Set(4)": [ "2", { "b": 3 }, 321, null ] }
}, 1000)

const j = reactive(new Map())
j.set('yo', 'good')
j.set('x', { b: 3 })
setTimeout(() => {
  j.delete('x')
  // Proxy: { "Map(1)": { "yo =>": "good" } }
}, 1000)

既然 reactive 函數(shù)可以解構(gòu) ref,那么進(jìn)行一些嘗試渺贤。以下是官網(wǎng)的原話隘庄。

值得注意的是,當(dāng)訪問(wèn)到某個(gè)響應(yīng)式數(shù)組或 Map 這樣的原生集合類型中的 ref 元素時(shí)癣亚,不會(huì)執(zhí)行 ref 的解包丑掺。

但在實(shí)際實(shí)驗(yàn)下來(lái)發(fā)現(xiàn)這句話并不嚴(yán)謹(jǐn)。

var a = ref(new Map())
var b = reactive(a)
a.value.set('a', 1)
b.value.set('b', 2) // 需要加上 value
// { "Map(2)": { "a =>": 1, "b =>": 2 } }

console.log('a === b', a.value === b.value) // true

var a = ref(new Set())
var b = reactive({ a })
a.value.add(1)
b.a.add(2) // ! 被對(duì)象包裹的 Map 是可以被解構(gòu)的
// { "Map(2)": { "a =>": 1, "b =>": 2 } }
console.log('a === b', a.value === b.a) // true

嘗試了 Object述雾、Array街州、Set 后發(fā)現(xiàn),被 ref 函數(shù)返回的對(duì)象如果直接傳給 reactive 函數(shù)是不會(huì)被解構(gòu)的玻孟,但如果 ref 對(duì)象被對(duì)象符號(hào)包裹 reactive({ ref: ref(new Set()) }) 的情況下是可以被解構(gòu)的唆缴。

最后

本文我們先提出了 ref 和 reactive 的疑問(wèn),然后給出結(jié)果黍翎。再?gòu)脑创a層面逐步分析了 ref 和 reactive 函數(shù)面徽。也算是基本掌握其原理了。

關(guān)于 ref 和 reactive 的內(nèi)容就這么多啦匣掸,希望對(duì)你有用趟紊。

本文正在參加「金石計(jì)劃」

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碰酝,隨后出現(xiàn)的幾起案子霎匈,更是在濱河造成了極大的恐慌,老刑警劉巖送爸,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铛嘱,死亡現(xiàn)場(chǎng)離奇詭異暖释,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)墨吓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門球匕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人帖烘,你說(shuō)我怎么就攤上這事亮曹。” “怎么了蚓让?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)讥珍。 經(jīng)常有香客問(wèn)我历极,道長(zhǎng),這世上最難降的妖魔是什么衷佃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任趟卸,我火速辦了婚禮,結(jié)果婚禮上氏义,老公的妹妹穿的比我還像新娘锄列。我一直安慰自己,他們只是感情好惯悠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布邻邮。 她就那樣靜靜地躺著,像睡著了一般克婶。 火紅的嫁衣襯著肌膚如雪筒严。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天情萤,我揣著相機(jī)與錄音鸭蛙,去河邊找鬼。 笑死筋岛,一個(gè)胖子當(dāng)著我的面吹牛娶视,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播睁宰,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肪获,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了柒傻?” 一聲冷哼從身側(cè)響起贪磺,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诅愚,沒(méi)想到半個(gè)月后寒锚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體劫映,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年刹前,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泳赋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喇喉,死狀恐怖祖今,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拣技,我是刑警寧澤千诬,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站膏斤,受9級(jí)特大地震影響徐绑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莫辨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一傲茄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沮榜,春花似錦盘榨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至型酥,卻和暖如春捷犹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冕末。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工萍歉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人档桃。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓枪孩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親藻肄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蔑舞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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