(五)Vue-響應(yīng)式原理

響應(yīng)式

響應(yīng)式處理的入口

  1. src\core\instance\init.js
  • initState(vm) vm 狀態(tài)的初始化
  • 初始化了 _data溃蔫、_props健提、methods 等
  1. src\core\instance\state.js
// 數(shù)據(jù)的初始化 
if (opts.data) { 
  initData(vm) 
} else { 
  observe(vm._data = {}, true /* asRootData */) 
}
  1. initData(vm) vm 數(shù)據(jù)的初始化
function initData (vm: Component) { 
  let data = vm.$options.data 
  // 初始化 _data私痹,組件中 data 是函數(shù)痪伦,調(diào)用函數(shù)返回結(jié)果 
  // 否則直接返回 data 
  data = vm._data = typeof data === 'function' 
    ? getData(data, vm) 
     : data || {} 
   ……
 // proxy data on instance 
 // 獲取 data 中的所有屬性 
 const keys = Object.keys(data) 
 // 獲取 props / methods 
 const props = vm.$options.props 
 const methods = vm.$options.methods 
 let i = keys.length 
 // 判斷 data 上的成員是否和 props/methods 重名 
……

 // observe data 
 // 數(shù)據(jù)的響應(yīng)式處理 
 observe(data, true /* asRootData */) 
}
  1. src\core\observer\index.js
  • observe(value, asRootData)
  • 負責(zé)為每一個 Object 類型的 value 創(chuàng)建一個 observer 實例
export function observe (value: any, asRootData: ?boolean): Observer | void { 
  // 判斷 value 是否是對象 
  if (!isObject(value) || value instanceof VNode) { 
    return 
  }
  let ob: Observer | void 
  // 如果 value 有 __ob__(observer對象) 屬性 結(jié)束 
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { 
    ob = value.__ob__ 
  } else if ( 
    shouldObserve && 
    !isServerRendering() && 
    (Array.isArray(value) || isPlainObject(value)) && 
    Object.isExtensible(value) && 
    !value._isVue ) {
      // 創(chuàng)建一個 Observer 對象 
      ob = new Observer(value) 
    }
    if (asRootData && ob) { 
      ob.vmCount++ 
    }
    return ob 
}

Observer

  1. src\core\observer\index.js
  • 對對象做響應(yīng)化處理
  • 對數(shù)組做響應(yīng)化處理
export class Observer {
  // 觀測對象
  value: any;
  // 依賴對象
  dep: Dep;
  // 實例計數(shù)器
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    // 初始化實例的 vmCount 為0
    this.vmCount = 0
    // 將實例掛載到觀察對象的 __ob__ 屬性
    def(value, '__ob__', this)
    // 數(shù)組的響應(yīng)式處理
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      // 為數(shù)組中的每一個對象創(chuàng)建一個 observer 實例
      this.observeArray(value)
    } else {
      // 遍歷對象中的每一個屬性,轉(zhuǎn)換成 setter/getter
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    // 獲取觀察對象的每一個屬性
    const keys = Object.keys(obj)
    // 遍歷每一個屬性辉哥,設(shè)置為響應(yīng)式數(shù)據(jù)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
  • walk(obj)
    遍歷 obj 的所有屬性,為每一個屬性調(diào)用 defineReactive() 方法饲齐,設(shè)置 getter/setter

defineReactive()

  1. src\core\observer\index.js
  2. defineReactive(obj, key, val, customSetter, shallow)
  • 為一個對象定義一個響應(yīng)式的屬性捂人,每一個屬性對應(yīng)一個 dep 對象
  • 如果該屬性的值是對象滥搭,繼續(xù)調(diào)用 observe
  • 如果給屬性賦新值,繼續(xù)調(diào)用 observe
  • 如果數(shù)據(jù)更新發(fā)送通知

對象響應(yīng)式處理

// 為一個對象定義一個響應(yīng)式的屬性
/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // 創(chuàng)建依賴對象實例
  const dep = new Dep()
  // 獲取 obj 的屬性描述符對象
  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }
  // 提供預(yù)定義的存取器函數(shù)
  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }
  // 判斷是否遞歸觀察子對象闽坡,并將子對象屬性都轉(zhuǎn)換成 getter/setter疾嗅,返回子觀察對象
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      // 如果預(yù)定義的 getter 存在則 value 等于getter 調(diào)用的返回值
      // 否則直接賦予屬性值
      const value = getter ? getter.call(obj) : val
      // 如果存在當(dāng)前依賴目標冕象,即 watcher 對象交惯,則建立依賴
      if (Dep.target) {
        dep.depend()
        // 如果子觀察目標存在,建立子對象的依賴關(guān)系
        if (childOb) {
          childOb.dep.depend()
          // 如果屬性是數(shù)組意荤,則特殊處理收集數(shù)組對象依賴
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      // 返回屬性值
      return value
    },
    set: function reactiveSetter (newVal) {
      // 如果預(yù)定義的 getter 存在則 value 等于getter 調(diào)用的返回值
      // 否則直接賦予屬性值
      const value = getter ? getter.call(obj) : val
      // 如果新值等于舊值或者新值舊值為NaN則不執(zhí)行
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // 如果沒有 setter 直接返回
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      // 如果預(yù)定義setter存在則調(diào)用玖像,否則直接更新新值
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      // 如果新值是對象齐饮,觀察子對象并返回 子的 observer 對象
      childOb = !shallow && observe(newVal)
      // 派發(fā)更新(發(fā)布更改通知)
      dep.notify()
    }
  })
}

數(shù)組的響應(yīng)式處理

  1. Observer 的構(gòu)造函數(shù)
export class Observer {
  // 觀測對象
  value: any;
  // 依賴對象
  dep: Dep;
  // 實例計數(shù)器
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    // 初始化實例的 vmCount 為0
    this.vmCount = 0
    // 將實例掛載到觀察對象的 __ob__ 屬性
    def(value, '__ob__', this)
    // 數(shù)組的響應(yīng)式處理
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      // 為數(shù)組中的每一個對象創(chuàng)建一個 observer 實例
      this.observeArray(value)
    } else {
      // 遍歷對象中的每一個屬性祖驱,轉(zhuǎn)換成 setter/getter
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    // 獲取觀察對象的每一個屬性
    const keys = Object.keys(obj)
    // 遍歷每一個屬性捺僻,設(shè)置為響應(yīng)式數(shù)據(jù)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

// helpers

/**
 * Augment a target Object or Array by intercepting
 * the prototype chain using __proto__
 */
function protoAugment (target, src: Object) {
  /* eslint-disable no-proto */
  target.__proto__ = src
  /* eslint-enable no-proto */
}

/**
 * Augment a target Object or Array by defining
 * hidden properties.
 */
/* istanbul ignore next */
function copyAugment (target: Object, src: Object, keys: Array<string>) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key])
  }
}
  1. 處理數(shù)組修改數(shù)據(jù)的方法
  • src\core\observer\array.js
const arrayProto = Array.prototype
// 使用數(shù)組的原型創(chuàng)建一個新的對象
export const arrayMethods = Object.create(arrayProto)
// 修改數(shù)組元素的方法
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  // 保存數(shù)組原方法
  const original = arrayProto[method]
  // 調(diào)用 Object.defineProperty() 重新定義修改數(shù)組的方法
  def(arrayMethods, method, function mutator (...args) {
    // 執(zhí)行數(shù)組的原始方法
    const result = original.apply(this, args)
    // 獲取數(shù)組對象的 ob 對象
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 對插入的新元素,重新遍歷數(shù)組元素設(shè)置為響應(yīng)式數(shù)據(jù)
    if (inserted) ob.observeArray(inserted)
    // notify change
    // 調(diào)用了修改數(shù)組的方法拔稳,調(diào)用數(shù)組的ob對象發(fā)送通知
    ob.dep.notify()
    return result
  })
})

Dep 類

  • src\core\observer\dep.js
  • 依賴對象
  • 記錄 watcher 對象
  • depend() -- watcher 記錄對應(yīng)的 dep
  • 發(fā)布通知
  1. 在 defineReactive() 的 getter 中創(chuàng)建 dep 對象锹雏,并判斷 Dep.target 是否有值, 調(diào)用 dep.depend()
  2. dep.depend() 內(nèi)部調(diào)用 Dep.target.addDep(this)礁遵,也就是 watcher 的 addDep() 方 法,它內(nèi)部最 調(diào)用 dep.addSub(this)铲球,把 watcher 對象晰赞,添加到 dep.subs.push(watcher) 中掖鱼,也 就是把訂閱者 添加到 dep 的 subs 數(shù)組中,當(dāng)數(shù)據(jù)變化的時候調(diào)用 watcher 對象的 update() 方法
  3. 調(diào)用 mountComponent() 方法的時 候芍瑞,創(chuàng)建了 渲染 watcher 對象褐墅,執(zhí)行 watcher 中的 get() 方法。設(shè)置 Dep.target
  4. get() 方法內(nèi)部調(diào)用 pushTarget(this)竟贯,把當(dāng)前 Dep.target = watcher屑那,同時把當(dāng)前 watcher 入棧艘款, 因為有父子組件嵌套的時候先把父組件對應(yīng)的 watcher 入棧,再去處理子組件的 watcher蜘欲,子 組件的處理完畢 后晌柬,再把父組件對應(yīng)的 watcher 出棧空繁,繼續(xù)操作
  5. Dep.target 用來存放目前正在使用的watcher。全局唯一闷祥,并且一次也只能有一個 watcher 被使用
// dep 是個可觀察對象傲诵,可以有多個指令訂閱它
/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 */
export default class Dep {
  // 靜態(tài)屬性拴竹,watcher 對象
  static target: ?Watcher;
  // dep 實例 Id
  id: number;
  // dep 實例對應(yīng)的 watcher 對象/訂閱者數(shù)組
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  // 添加新的訂閱者 watcher 對象
  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  // 移除訂閱者
  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  // 將觀察對象和 watcher 建立依賴
  depend () {
    if (Dep.target) {
      // 如果 target 存在,把 dep 對象添加到 watcher 的依賴中
      Dep.target.addDep(this)
    }
  }

  // 發(fā)布通知
  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    // 調(diào)用每個訂閱者的update方法實現(xiàn)更新
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
// Dep.target 用來存放目前正在使用的watcher
// 全局唯一座泳,并且一次也只能有一個watcher被使用
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null
const targetStack = []
// 入棧并將當(dāng)前 watcher 賦值給 Dep.target
// 父子組件嵌套的時候先把父組件對應(yīng)的 watcher 入棧挑势,
// 再去處理子組件的 watcher啦鸣,子組件的處理完畢后诫给,再把父組件對應(yīng)的 watcher 出棧,繼續(xù)操作
export function pushTarget (target: ?Watcher) {
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
  // 出棧操作
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}

Watcher 類

  1. Watcher 分為三種凫碌,Computed Watcher吃型、用戶 Watcher (偵聽器)勤晚、渲染 Watcher
  2. 渲染 Watcher 的創(chuàng)建時機
  • /src/core/instance/lifecycle.js
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode
    if (process.env.NODE_ENV !== 'production') {
      /* istanbul ignore if */
      if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
        vm.$options.el || el) {
        warn(
          'You are using the runtime-only build of Vue where the template ' +
          'compiler is not available. Either pre-compile the templates into ' +
          'render functions, or use the compiler-included build.',
          vm
        )
      } else {
        warn(
          'Failed to mount component: template or render function not defined.',
          vm
        )
      }
    }
  }
  callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  }

  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}
  • 渲染 wacher 創(chuàng)建的位置 lifecycle.js 的 mountComponent 函數(shù)中
  • Wacher 的構(gòu)造函數(shù)初始化鸟蜡,處理 expOrFn (渲染 watcher 和偵聽器處理不同)
  • 調(diào)用 this.get() 挺邀,它里面調(diào)用 pushTarget() 然后 this.getter.call(vm, vm) (對于渲染 wacher 調(diào) 用 updateComponent)跳座,如果是用戶 wacher 會獲取屬性的值(觸發(fā)get操作)
  • 當(dāng)數(shù)據(jù)更新的時候疲眷,dep 中調(diào)用 notify() 方法您朽,notify() 中調(diào)用 wacher 的 * update() 方法
  • update() 中調(diào)用 queueWatcher()
  • queueWatcher() 是一個核心方法,去除重復(fù)操作几颜,調(diào)用flushSchedulerQueue() 刷新隊列并執(zhí)行watcher
  • flushSchedulerQueue() 中對 wacher 排序蛋哭,遍歷所有 wacher 涮母,如果有 before,觸發(fā)生命周期的鉤子函數(shù) beforeUpdate棺妓,執(zhí)行 wacher.run()炮赦,它內(nèi)部調(diào)用 this.get()吠勘,然后調(diào)用 this.cb() (渲染wacher 的 cb 是 noop)
  • 整個流程結(jié)束

set 、delete植锉、watch

  1. vm.$set
    向響應(yīng)式對象中添加一個屬性峭拘,并確保這個新屬性同樣是響應(yīng)式的鸡挠,且觸發(fā)視圖更新。它必須用于向響應(yīng)式對象上添加新屬性彭沼,因為 Vue 無法探測普通的新增屬性 (比如this.myObject.newProperty = 'hi')
  • 使用方式:vm.$set(obj, 'foo', 'test')
  1. vm.$delete
    刪除對象的屬性备埃。如果對象是響應(yīng)式的褐奴,確保刪除能觸發(fā)更新視圖敦冬。這個方法主要用于避開 Vue不能檢測到屬性被刪除的限制望众,但是你應(yīng)該很少會使用它烂翰。
  • 使用方式:vm.$delete(vm.obj, 'msg')
  1. vm.$watch
    觀察 Vue 實例變化的一個表達式或計算屬性函數(shù)蚤氏。回調(diào)函數(shù)得到的參數(shù)為新值和舊值佳恬。表達式只接受監(jiān)督的鍵路徑毁葱。對于更復(fù)雜的表達式贰剥,用一個函數(shù)取代。
// expOrFn 是表達式 
vm.$watch('msg', function (newVal, oldVal) { 
  console.log(newVal, oldVal) 
})
vm.$watch('user.firstName', function (newVal, oldVal) { 
  console.log(newVal) 
})

// expOrFn 是函數(shù) 
vm.$watch(function () { 
   return this.a + this.b 
}, function (newVal, oldVal) { 
  console.log(newVal) 
})

// deep 是 true前痘,消耗性能 
vm.$watch('user', function (newVal, oldVal) { 
  // 此時的 newVal 是 user 對象 
  console.log(newVal === vm.user) 
}, {
  deep: true 
})

// immediate 是 true 
vm.$watch('msg', function (newVal, oldVal) { 
  console.log(newVal) 
}, {
  immediate: true 
})

三種類型的 Watcher 對象
沒有靜態(tài)方法芹缔,因為 $watch 方法中要使用 Vue 的實例
Watcher 分三種:計算屬性 Watcher瓶盛、用戶 Watcher (偵聽器)惩猫、渲染 Watcher
創(chuàng)建順序:計算屬性 Watcher、用戶 Watcher (偵聽器)吵取、渲染 Watcher

渲染 watcher 的執(zhí)行過程
1.當(dāng)數(shù)據(jù)更新锯厢,defineReactive 的 set 方法中調(diào)用 dep.notify()
2.調(diào)用 watcher 的 update()
3.調(diào)用 queueWatcher(),把 wacher 存入隊列捺氢,如果已經(jīng)存入摄乒,不重復(fù)添加
4.循環(huán)調(diào)用 flushSchedulerQueue()
通過 nextTick(),在消息循環(huán)結(jié)束之前時候調(diào)用 flushSchedulerQueue()
6.調(diào)用 wacher.run()
7.調(diào)用 wacher.get() 獲取最新值
如果是渲染 wacher 結(jié)束
如果是用戶 watcher斋否,調(diào)用 this.cb()

異步更新隊列-nextTick()

  1. Vue 更新 DOM 是異步執(zhí)行的拭荤,批量的
    在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)舅世。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的 DOM缨硝。
  2. vm.$nextTick(function () { /* 操作 DOM */ }) / Vue.nextTick(function () {})

Demo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末查辩,一起剝皮案震驚了整個濱河市奕短,隨后出現(xiàn)的幾起案子翎碑,更是在濱河造成了極大的恐慌,老刑警劉巖遣铝,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酿炸,死亡現(xiàn)場離奇詭異涨冀,居然都是意外死亡,警方通過查閱死者的電腦和手機扁眯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門姻檀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胶台,你說我怎么就攤上這事诈唬∧梗” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長济竹。 經(jīng)常有香客問我送浊,道長,這世上最難降的妖魔是什么唁桩? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任荒澡,我火速辦了婚禮与殃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘米奸。我一直安慰自己爽篷,他們只是感情好逐工,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著佃却,像睡著了一般饲帅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上育八,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天髓棋,我揣著相機與錄音惶洲,去河邊找鬼恬吕。 笑死,一個胖子當(dāng)著我的面吹牛渐裂,可吹牛的內(nèi)容都是我干的钠惩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼举塔!你這毒婦竟也來了央渣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后场钉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逛万,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡批钠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年埋心,在試婚紗的時候發(fā)現(xiàn)自己被綠了拷呆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖项秉,靈堂內(nèi)的尸體忽然破棺而出伙狐,到底是詐尸還是另有隱情瞬欧,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站野建,受9級特大地震影響候生,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜须蜗,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一明肮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧循未,春花似錦秫舌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽飒房。三九已至,卻和暖如春护糖,著一層夾襖步出監(jiān)牢的瞬間嚼松,已是汗流浹背献酗。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罕偎,地道東北人很澄。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像颜及,于是被迫代替她去往敵國和親甩苛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

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