vue3.0中的數(shù)據(jù)監(jiān)聽误续、響應式原理簡析

實現(xiàn)可數(shù)據(jù)可響應的方式

通過可響應對象,實現(xiàn)對數(shù)據(jù)的偵測光绕,從而告知外界數(shù)據(jù)變化女嘲。

  1. 單一的訪問器getter和setter
  2. Object.defineProperty()--實現(xiàn)了vue2.0響應式原理
  3. Proxy--實現(xiàn)3.0版本的原理
vue2.0響應式原理的弊端
  1. 需要對 Object 和 Array 兩種類型采用不同的處理方式。

  2. 為了感知 Array 的變化诞帐,對 Array 原型上幾個改變數(shù)組自身的內(nèi)容的方法做了攔截欣尼,雖然實現(xiàn)了對數(shù)組的可響應,但同樣存在一些問題停蕉。vue2.0的數(shù)組方法攔截

  3. defineProperty 通過遞歸實現(xiàn) getter/setter 也存在一定的性能問題愕鼓。

vue3.0主要使用的語法
  1. proxy
  2. reflect
  3. WeakMap

1.reflect

Reflect對象與Proxy對象一樣,也是擁有get慧起、set方法菇晃。

  • Reflect對象的設計目的:
  1. 將Object對象的一些明顯屬于語言內(nèi)部的方法(比如Object.defineProperty),放到Reflect對象上
  2. 修改某些Object方法的返回結果蚓挤,讓其變得更合理
  3. 讓Object操作都變成函數(shù)行為
  4. Reflect對象的方法與Proxy對象的方法一一對應磺送,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法灿意。

2.關于proxy

Proxy 可以代理數(shù)組估灿,并且 API 提供了get 、 set缤剧。

let data = { foo: 'foo' }
let p = new Proxy(data, {
  get(target, key, receiver) {
    return target[key]
  },
  set(target, key, value, receiver) {
    console.log(receiver)// receiver是proxy對象本身
    console.log('set value')
    target[key] = value // target就是data對象
  }
})
p.foo = 123 
//Proxy{}
//{foo: "foo"} "set value"
// 此時data被修改馅袁,p是代理data的代理對象
let data = [1,2,3]
let p = new Proxy(data, {
  get(target, key, receiver) {
    return target[key]
  },
  set(target, key, value, receiver) {
    console.log('set value')
    target[key] = value
  }
})
p.push(4) // Uncaught TypeError: 'set' on proxy: trap returned falsish for property '3'
  • 產(chǎn)生問題:set也需要返回值
    需要修改為
let data = [1,2,3]
let p = new Proxy(data, {
  get(target, key, receiver) {
    return target[key]
  },
  set(target, key, value, receiver) {
    console.log('set value')
    target[key] = value
    //++
    return true
    //++
  }
})

p.push(4)

// set value // 打印2次

打印兩次是因為處理數(shù)組時候push不但修改數(shù)組的項還修改了length

let data = [1,2,3]
let p = new Proxy(data, {
  get(target, key, receiver) {
    console.log('get value:', key)
    return target[key]
  },
  set(target, key, value, receiver) {
    console.log('set value:', key, value)
    target[key] = value
    return true
  }
})

p.push(1)

// get value: push
// get value: length
// set value: 3 1
// set value: length 4
  • 產(chǎn)生問題:多次觸發(fā)set和get就會多次觸發(fā)view的更新,此時只需要觸發(fā)一次set更新
    可以用類似于 debounce 的操作處理多次執(zhí)行的問題(vue3.0有更好的方式)
function reactive(data, cb) {
  let timer = null
  return new Proxy(data, {
    get(target, key, receiver) {
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      clearTimeout(timer)
      timer = setTimeout(() => {
        cb && cb()
      }, 0);
      return Reflect.set(target, key, value, receiver)
    }
  })
}

let ary = [1, 2]
let p = reactive(ary, () => {
  console.log('trigger')
})
p.push(3)

// trigger

但是vue3.0提供了更好的方式處理執(zhí)行重復的問題荒辕,利用私有屬性
(以下沒有完全解決重復問題汗销,對于數(shù)組只是避免了length對數(shù)組更新的重復渲染犹褒,像splice,unshift這種多次修改索引的方法還會觸發(fā)多次)

function trigger(){
    console.log('觸發(fā)視圖更新')
}
function isObject(target){
    return typeof target==='object'&&target!==null;
}
function reactive(target){
    if(!isObject(target)){
        return target;
    }
    const handler={
        set(target,key,value,receiver){
            console.log('setValue',value)
            //如果觸發(fā)的是私有屬性的話弛针,觸發(fā)視圖的更新
            if(!target.hasOwnProperty(key)){//添加新屬性
                trigger();
            }else if(value!==target[key]){//set的值變化叠骑,更新屬性
                trigger();
            }
            return Reflect.set(target,key,value,receiver)
        },
        get(target,key,receiver){
            const res=Reflect.get(target,key,receiver);
            console.log('getValue',res)
            return res;

        },
        deleteProperty(target,key){
            return Reflect.deleteProperty(target,key);
        }
    }
    let observer=new Proxy(target,handler);
    return observer;
}
let obj=[1,2,3];
let p=reactive(obj)
p.unshift(1);

監(jiān)聽深層的對象

let obj={
  a:[1,2,3],
  b:{c:4}
};
let p=reactive(obj)
p.b.c = 2

// getValue: {c:4} 不觸發(fā)set
  • 產(chǎn)生問題:proxy 只能代理一層;
    因為沒有觸發(fā) set 的輸出钦奋,反而是觸發(fā)了 get 座云,因為 set 的過程中訪問了 bar 這個屬性。
    深度數(shù)據(jù)監(jiān)聽付材,可以用 遞歸 處理問題朦拖,把對象的每一層都進行代理(同vue2.0),但是優(yōu)于2.0,3.0的處理方式是只有使用數(shù)據(jù)的當前屬性的時候才會去代理厌衔,2.0則是全部代理璧帝,3.0的處理方式節(jié)約的大量不必要的工作。
function trigger(){
    console.log('觸發(fā)視圖更新')
}
function isObject(target){
    return typeof target==='object'&&target!==null;
}
function hasOwn(){
    
}
function reactive(target){
    if(!isObject(target)){
        return target;
    }
    const handler={
        set(target,key,value,receiver){
            //如果觸發(fā)的是私有屬性的話富寿,觸發(fā)視圖的更新
            if(!target.hasOwnProperty(key)){//添加新屬性
                trigger();
            }else if(value!==target[key]){//set的值變化睬隶,更新屬性
                trigger();
            }
            return Reflect.set(target,key,value,receiver)
        },
        get(target,key,receiver){
            const res=Reflect.get(target,key,receiver);
            if(isObject(res)){
                return reactive(res)
            }
            return res;
        },
        deleteProperty(target,key){
            return Reflect.deleteProperty(target,key);
        }
    }
    let observer=new Proxy(target,handler);
    return observer;
}
let obj={
    a:[1,2,3],
    b:{c:1}
}
let p=reactive(obj)
p.b.c=4
// 觸發(fā)視圖更新

有些數(shù)據(jù)并非需要偵測,我們需要對數(shù)據(jù)偵測做更細的控制页徐,全部監(jiān)聽浪費一部分的性能苏潜,vue3.0有更好的解決辦法,weakMap变勇。

3.weakMap對象恤左,實現(xiàn)完整的reactive數(shù)據(jù)監(jiān)聽

WeakMap對象也是鍵值對的集合。它的鍵必須是對象類型搀绣,值可以是任意類型飞袋。它的鍵被弱保持,也就是說链患,當其鍵所指對象沒有其他地方引用的時候巧鸭,它會被GC回收掉。WeakMap提供的接口與Map相同麻捻。

  • 我認為使用WeakMap主要是因為
  1. WeakMap的鍵是對象類型纲仍,保持弱引用。
  2. 列表是否存在取決于垃圾回收器的狀態(tài)贸毕,性能比object更高
//++ ----------
const toProxy= new WeakMap()
const toRaw = new WeakMap()
// ++ ----------
function trigger(){
    console.log('觸發(fā)視圖更新')
}
function isObject(target){
    return typeof target==='object'&&target!==null;
}
function reactive(target){
    if(!isObject(target)){
        return target;
    }
// ++ ----------
    //如果代理表中存在target了就說明巷折,target已經(jīng)被代理過了,就返回代理過的對象
    if(toProxy.get(target)){
        console.log('target已經(jīng)被代理過了崖咨,是一個obj對象')
        return toProxy.get(target)
    }

    if(toRaw.has(target)){//如果這個對象被代理過了,就把對象原封不動的返回
        console.log('target已經(jīng)被代理過了,是一個proxy對象')
        return target
    }
// ++ ----------
    const handler={
        set(target,key,value,receiver){
            const oldValue = target[key];
            //如果觸發(fā)的是私有屬性的話油吭,觸發(fā)視圖的更新
            if(!target.hasOwnProperty(key)){//添加新屬性击蹲,用hasOwnProperty是因為可以屏蔽一些無用的屬性更新署拟,像數(shù)組的length等
                trigger();
            }else if(value!==oldValue){//set的值變化,更新屬性
                trigger();
            }
            return Reflect.set(target,key,value,receiver)
        },
        get(target,key,receiver){
            const res=Reflect.get(target,key,receiver);
            if(isObject(res)){
                return reactive(res)
            }
            return res;
        },
        deleteProperty(target,key){
            return Reflect.deleteProperty(target,key);
        }
    }
    let observer=new Proxy(target,handler);
// ++ ----------
    toProxy.set(target,observer);//記錄原對象代理過的結果
    toRaw.set(observer,target);
// ++ ----------
    return observer;
}
let obj={
    a:[1,2,3],
    b:{c:1}
}
let obj2={
    a:[1,2,3],
    b:{c:1}
}
let p=reactive(obj)
p=reactive(p);// p是被代理過的對象

let n=reactive(obj2);
let n1=reactive(obj2);//多次代理一個對象obj1=

以上實現(xiàn)了一個簡易的數(shù)據(jù)監(jiān)聽

4. 依賴收集(發(fā)布訂閱)歌豺,實現(xiàn)響應式

更新視圖主要依賴于effect函數(shù)推穷。

let obj=reactive({name:'zf'});
effect(()=>{ // effect會執(zhí)行兩次,默認先執(zhí)行一次 之后依賴的數(shù)據(jù)變化了 會再次執(zhí)行类咧。
  console.log(obj.name)//會調(diào)用get方法
});
obj.name='jw';//代理的屬性值被修改

effect的作用是首先會執(zhí)行一次傳入的函數(shù)馒铃,之后如果代理的屬性有變化,會繼續(xù)觸發(fā)傳入effect的函數(shù)痕惋。
所以此時需要把傳入effect的函數(shù)包裝成一個響應式的函數(shù)区宇。

//棧 先進后出
let activeEffectStacks=[]; // 
//響應式  副作用
function effect(fn){
  // 需要把fn這個函數(shù)包裝成響應式的函數(shù),在把這個函數(shù)默認先執(zhí)行一次
  let effect = createReactiveEffect(fn);
  effect()//默認執(zhí)行一次
}
function createReactiveEffect(fn){
  let effect=function(){//這個就是創(chuàng)建的響應式的effect
      return run(effect,fn) // 運行 1.讓fn執(zhí)行 2.把這個effect存入到棧中
  }
  return effect;// 返回這個包裝好的函數(shù)
}
function run (){//運行fn并把effect存起來
  try{//try包裹防止fn內(nèi)報錯
    activeEffectStacks.push(effect);
    fn();//利用了js是單線程的。
    //fn觸發(fā)值戳,里邊讀取了proxy的屬性议谷,所以會進入到reactive的get中。
  }finally{
    activeEffectStacks.pop();//用完釋放
  }
 
}

reactive的get中收集依賴

const toProxy= new WeakMap()
const toRaw = new WeakMap()
function trigger(){
    console.log('觸發(fā)視圖更新')
}
function isObject(target){
    return typeof target==='object'&&target!==null;
}
function reactive(target){
    if(!isObject(target)){
        return target;
    }
    //如果代理表中存在target了就說明堕虹,target已經(jīng)被代理過了卧晓,就返回代理過的對象
    if(toProxy.get(target)){
        console.log('target已經(jīng)被代理過了,是一個obj對象')
        return toProxy.get(target)
    }

    if(toRaw.has(target)){//如果這個對象被代理過了赴捞,就把對象原封不動的返回
        console.log('target已經(jīng)被代理過了,是一個proxy對象')
        return target
    }
    const handler={
        set(target,key,value,receiver){
            // const oldValue = target[key];
            const res = Reflect.set(target, key, value, receiver)
            //如果觸發(fā)的是私有屬性的話逼裆,觸發(fā)視圖的更新
            //if(!target.hasOwnProperty(key)){//添加新屬性,用hasOwnProperty是因為可以屏蔽一些無用的屬性更新赦政,像數(shù)組的length等(經(jīng)測試會引起bug)
//++++++++++++++++++++++++++++
            //    trigger(target,'add',key);
//++++++++++++++++++++++++++++
            //}else if(value!==oldValue){//set的值變化胜宇,更新屬性
//++++++++++++++++++++++++++++
            //    trigger(target,'set',key);
//++++++++++++++++++++++++++++
            //}
            if (target.hasOwnProperty(key)) {
              trigger(target, '', key);
            }
            return res
        },
        get(target,key,receiver){
            const res=Reflect.get(target,key,receiver);
//++++++++++++++++++++++++++++++++++++++++++++++++
            // 收集依賴 訂閱 ,把當前的key和effect對應起來
            track(target,key) // 如果target上的這個key變化了重新讓數(shù)組中的effect執(zhí)行即可昼钻,所以需要建立target中的key于effect的關聯(lián)(關聯(lián)的結構如下圖所示)
//++++++++++++++++++++++++++++++++++++++++++++++++            
            if(isObject(res)){
                return reactive(res)
            }
            return res;
        },
        deleteProperty(target,key){
            return Reflect.deleteProperty(target,key);
        }
    }
    let observer=new Proxy(target,handler);
    toProxy.set(target,observer);//記錄原對象代理過的結果
    toRaw.set(observer,target);
    return observer;
}

track用于對應target中的key與effect的關聯(lián)


圖片來源于jw
let targetMap=new WeakMap();

function track(target,key){//如果這個target中的key變化了 就執(zhí)行數(shù)組里activeEffectStacks的方法掸屡。
  let effect=activeEffectStacks[activeEffectStacks.length-1];
  if(effect){//有對應的關系 才創(chuàng)建關聯(lián)
    let depsMap=targetMap.get(target);
    if(!depsMap){
       targetsMap.set(target,depsMap=new Map())
    }
    let deps=depsMap.get(key);
    if(!deps){
        depsMap.set(key,deps=new Set());
    }
    if(!deps.has(effect)){
      deps.add(effect);
    }
  }
  // 以上是動態(tài)創(chuàng)建依賴關系。
}

trigger 觸發(fā)key對應的effect然评,更新視圖

function trigger(target,type,key){
  let depsMap=targetMap.get(target);
  if(depsMap){
    let deps=depsMap.get(key);
    if(deps){//將當前key對應的effect依此執(zhí)行
     deps.forEach(effect=>effect())
    }
  }
}

完整vue3.0響應式代碼

const toProxy = new WeakMap()
const toRaw = new WeakMap()
function isObject(target) {
  return typeof target === 'object' && target !== null;
}
function reactive(target) {
  if (!isObject(target)) {
    return target;
  }
  // 如果代理表中存在target了就說明仅财,target已經(jīng)被代理過了,就返回代理過的對象
  if (toProxy.get(target)) {
    console.log('target已經(jīng)被代理過了碗淌,是一個obj對象')
    return toProxy.get(target)
  }
  if (toRaw.has(target)) { // 如果這個對象被代理過了盏求,就把對象原封不動的返回
    console.log('target已經(jīng)被代理過了,是一個proxy對象')
    return target
  }
  const handler = {
    set(target, key, value, receiver) {
      // const oldValue = target[key];
      const res = Reflect.set(target, key, value, receiver)
      // 如果觸發(fā)的是私有屬性的話,觸發(fā)視圖的更新

      // if (!target.hasOwnProperty(key)) { // 添加新屬性亿眠,用hasOwnProperty是因為可以屏蔽一些無用的屬性更新碎罚,像數(shù)組的length等(經(jīng)測試會引起bug)
      //   trigger(target, 'add', key);
      // } else if (value !== oldValue) { // set的值變化,更新屬性
      //   console.log(target, key, oldValue, value, 24)
      //  trigger(target, 'set', key);
      // }
      if (target.hasOwnProperty(key)) {
        trigger(target, '', key);
      }
      return res
    },
    get(target, key, receiver) {
      const res = Reflect.get(target, key, receiver);
      // 收集依賴 訂閱 纳像,把當前的key和effect對應起來
      track(target, key) // 如果target上的這個key變化了重新讓數(shù)組中的effect執(zhí)行即可荆烈,所以需要建立target中的key于effect的關聯(lián)
      if (isObject(res)) {
        return reactive(res)
      }
      return res;
    },
  }
  const observer = new Proxy(target, handler);
  toProxy.set(target, observer);// 記錄原對象代理過的結果
  toRaw.set(observer, target);
  return observer;
}

// 棧 先進后出
const activeEffectStacks = []; //
// 響應式  副作用
function effect(fn) {
  // 需要把fn這個函數(shù)包裝成響應式的函數(shù),在把這個函數(shù)默認先執(zhí)行一次
  const effect = createReactiveEffect(fn);
  effect()// 默認執(zhí)行一次
}
function createReactiveEffect(fn) {
  const effect = function () { // 這個就是創(chuàng)建的響應式的effect
    return run(effect, fn) // 運行 1.讓fn執(zhí)行 2.把這個effect存入到棧中
  }
  return effect;// 返回這個包裝好的函數(shù)
}
function run(effect, fn) { // 運行fn并把effect存起來
  try { // try包裹防止fn內(nèi)報錯
    activeEffectStacks.push(effect);
    fn();// 利用了js是單線程的。
    // fn觸發(fā),里邊讀取了proxy的屬性憔购,所以會進入到reactive的get中宫峦。
  } finally {
    activeEffectStacks.pop();// 用完釋放
  }
}

const targetMap = new WeakMap();
function track(target, key) { // 如果這個target中的key變化了 就執(zhí)行數(shù)組里activeEffectStacks的方法。
  console.log(activeEffectStacks, target, '-----------track')
  const effect = activeEffectStacks[activeEffectStacks.length - 1];
  if (effect) { // 有對應的關系 才創(chuàng)建關聯(lián)
    let depsMap = targetMap.get(target);
    if (!depsMap) {
      targetMap.set(target, depsMap = new Map())
    }
    let deps = depsMap.get(key);
    if (!deps) {
      depsMap.set(key, deps = new Set());
    }
    if (!deps.has(effect)) {
      deps.add(effect);
    }
  }
}
// 以上是動態(tài)創(chuàng)建依賴關系玫鸟。
function trigger(target, type, key) {
  const depsMap = targetMap.get(target);
  console.log(target, depsMap, '--------trigger')
  if (depsMap) {
    const deps = depsMap.get(key);
    if (deps) { // 將當前key對應的effect依此執(zhí)行
      console.log(deps.size)
      deps.forEach(effect => effect())
    }
  }
}

let obj={name:'haha'};
let observer= reactive(obj);
effect(()=>{
  console.log(observer.name);
})
observer.name='xixi';
observer.name='xixi';
//執(zhí)行兩次haha和xixi

(以下不會再次觸發(fā)更新的情況)

//1.對象只監(jiān)聽到了外層
const obj = { name: { n: 'haha' } };
const observer = reactive(obj);
effect(() => {
  console.log(observer.name, '-------------108');
})
observer.name.n = 'xixi';
//必須這樣監(jiān)聽
const obj = { name: { n: 'haha' } };
const observer = reactive(obj);
effect(() => {
  console.log(observer.name.n);
})
observer.name.n = 'xixi';

//2.對象內(nèi)層的數(shù)組操作問題
const obj1 = { a: [1, 2, 3] };
const oarr = reactive(obj1);
effect(() => {
    oarr.a.forEach((item)=>{
        console.log(item,'更新視圖');
    })
})
oarr.a[0]=2;
oarr.a.push(4);

原因是:

set(target, key, value, receiver) {
      // const oldValue = target[key];
      const res = Reflect.set(target, key, value, receiver)

      // 添加的新屬性或數(shù)組的項會導致更新失效
      //   if (!target.hasOwnProperty(key)) { 
      //     // trigger(target, 'add', key);
      //   } else if (value !== oldValue) {
      //     console.log(target, 'key:', key, oldValue, value)
      //     trigger(target, 'set', key);
      //     console.log(value, oldValue)
      //   }
      //以上處理方式需要改為 
      //如果數(shù)組方法過濾掉length等屬性會出問題
      if (target.hasOwnProperty(key)) {
        trigger(target, '', key);
      }
      return res
    },

備上html測試方式

<div id="box" style="margin-left: 20px;">
    <span id="app"></span> 
    <span id="add" style="margin-left: 10px;display: inline-block;width: 10px;height: 10px;cursor: pointer;">+</span>
</div>
<script src="./vue3.js"></script>
<script>
    let ele = document.getElementById('app');
    let btn = document.getElementById('add');
    let o = {
        number:{name:1},
    }
    let i=0;
    let reactiveData = reactive(o);
    effect(()=>{
        console.log(reactiveData.number,'------')
    })
    btn.addEventListener('click', () => {
        reactiveData.number.name=i++;
   },false)
    // let o = {
    //     number: [1],
    // }
    // let i=0;
    // let reactiveData = reactive(o);
    // effect(()=>{
    //     console.log(reactiveData.number,'------')
    //     ele.innerHTML = reactiveData.number
    // })
    // btn.addEventListener('click', () => {
    //     reactiveData.number.push(i++);
    // },false)
</script>
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末导绷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子屎飘,更是在濱河造成了極大的恐慌妥曲,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钦购,死亡現(xiàn)場離奇詭異檐盟,居然都是意外死亡,警方通過查閱死者的電腦和手機肮雨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門遵堵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人怨规,你說我怎么就攤上這事陌宿。” “怎么了波丰?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵壳坪,是天一觀的道長。 經(jīng)常有香客問我掰烟,道長爽蝴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任纫骑,我火速辦了婚禮蝎亚,結果婚禮上,老公的妹妹穿的比我還像新娘先馆。我一直安慰自己发框,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布煤墙。 她就那樣靜靜地躺著梅惯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仿野。 梳的紋絲不亂的頭發(fā)上铣减,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音脚作,去河邊找鬼葫哗。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的劣针。 我是一名探鬼主播桨螺,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酿秸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起魏烫,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辣苏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哄褒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稀蟋,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年呐赡,在試婚紗的時候發(fā)現(xiàn)自己被綠了退客。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡链嘀,死狀恐怖萌狂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情怀泊,我是刑警寧澤茫藏,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站霹琼,受9級特大地震影響务傲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枣申,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一售葡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忠藤,春花似錦挟伙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓜贾,卻和暖如春诺祸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祭芦。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工筷笨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓胃夏,卻偏偏與公主長得像轴或,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仰禀,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

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