Vue3 | Composition API 包括setup训堆、ref等新特性詳解 與 實(shí)戰(zhàn)

完整原文地址見簡(jiǎn)書

更多完整Vue筆記目錄敬請(qǐng)見《前端 Web 筆記 匯總目錄(Updating)》



本文內(nèi)容提要

  • Composition API 的作用
  • setup函數(shù)
  • 例程露戒,打印查看setup內(nèi)容
  • 非響應(yīng)引用的案例
  • ref()概念椒功、原理 與 實(shí)戰(zhàn)
  • reactive()概念捶箱、原理 與 實(shí)戰(zhàn)
  • 使用readonly限制對(duì)象的訪問權(quán)限
  • 使用toRefs()對(duì)reactive對(duì)象進(jìn)一步封裝
  • 多個(gè)屬性進(jìn)行解構(gòu)
  • 多個(gè)屬性 配合toRefs() 進(jìn)行解構(gòu)
  • toRefs()無法處理 undefined的鍵值
  • 使用toRef()應(yīng)對(duì)上述問題
  • 關(guān)于setup函數(shù)的三個(gè)參數(shù)【attrs、slots动漾、emit】
  • 回顧 沒有 CompositionAPI時(shí)丁屎,emit的用法
  • 使用setup的 context.emit 替代 this.$emit
  • 使用Composition API開發(fā) todoList
  • 完善toDoList案例
  • 優(yōu)化上例的邏輯結(jié)構(gòu)!
  • setup的 computed 計(jì)算屬性
  • 當(dāng)然以上是computed 的默認(rèn)用法旱眯,實(shí)際上它可以接收一個(gè)對(duì)象
  • 將上例的處理值換成 Object類型悦屏,再例
  • setup 中的 watch 監(jiān)聽
  • setup 中的 watch 監(jiān)聽:監(jiān)聽Object類型
  • setup 中的 watch 監(jiān)聽:監(jiān)聽Object類型的 多個(gè)屬性
  • setup 中的 watchEffect監(jiān)聽 以及 與 watch 的異同比較
  • 兩者都可以用以下的方式,在一個(gè)設(shè)定的時(shí)延之后键思,停止監(jiān)聽
  • 為 watch 配置 immediate屬性础爬,可使具備同watchEffect的 即時(shí)性
  • setup 中的 生命周期
  • setup中的provide、inject用法
  • 配合上ref實(shí)現(xiàn) 響應(yīng)特性 以及 readonly實(shí)現(xiàn) 單向數(shù)據(jù)流
  • setup結(jié)合ref指令


Composition API 的作用

使得相同的吼鳞、相關(guān)的功能代碼 可以比較 完整地聚合起來看蚜,
提高可維護(hù)性、可讀性赔桌,提高開發(fā)效率供炎;

規(guī)避 同一個(gè)功能的代碼,
卻散落在 組件定義中的data疾党、methods音诫、computed、directives雪位、template竭钝、mixin等各處 的問題;


setup函數(shù)

--- Composition API 所有代碼編寫之前雹洗,
都要 建立在setup函數(shù) 之上香罐;

--- 在created 組件實(shí)例 被完全初始化之前 回調(diào);
(所以注意在setup函數(shù)中时肿,
使用與this相關(guān)的調(diào)用是沒有用的)

--- setup函數(shù)中的內(nèi)容庇茫,
可以在 該組件的 模板template 中直接使用;
(如下例程)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div @click="handleClick">{{name}}</div>    
        `,

        setup(props, context) {
            return {
                name: 'zhao',
                handleClick: () => {
                    alert(666);
                }
            }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:


例程螃成,打印查看setup內(nèi)容

<script>

    const app = Vue.createApp({

        template: `
            <div @click="handleClick">{{name}}</div>    
        `,
        methods: {
            test() {
                console.log(this.$options);
            }
        },
        mounted() {
            this.test();
        },
        setup(props, context) {
            return {
                name: 'zhao',
                handleClick: () => {
                    alert(666);
                }
            }
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果:


由于調(diào)用時(shí)序的關(guān)系旦签,setup中 無法調(diào)用this等相關(guān) 如變量、methods中 等 其他內(nèi)容寸宏,但是其他內(nèi)容 卻可以調(diào)用 setup函數(shù)D拧!【setup生時(shí)眾為生击吱,眾生時(shí)setup已生】

<script>

    const app = Vue.createApp({

        template: `
            <div @click="handleClick">{{name}}</div>    
        `,
        methods: {
            test() {
                console.log(this.$options.setup());
            }
        },
        mounted() {
            this.test();
        },
        setup(props, context) {
            return {
                name: 'zhao',
                handleClick: () => {
                    alert(666);
                }
            }
        }
    });

    const vm = app.mount('#heheApp');
</script>


非響應(yīng)引用的案例

如下淋淀,這樣沒有使用ref/reactive的寫法,是不會(huì)有響應(yīng)的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
        `,
        setup(props, context) {
            let name = 'guan';
            setTimeout(() => {
                name = 'zhao';
            }, 2000);
            return { name }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

如下,運(yùn)行之后朵纷,兩秒延時(shí)之后炭臭,DOM文本展示并不會(huì)自動(dòng)改成zhao,而是一直展示初始化的guan


ref()概念袍辞、原理 與 實(shí)戰(zhàn)

使用ref可以 用于處理 基礎(chǔ)類型的數(shù)據(jù)鞋仍,賦能響應(yīng)式

原理:通過 proxy 將 數(shù)據(jù) 封裝成
類似 proxy({value: '【變量值】'})這樣的一個(gè)響應(yīng)式引用搅吁,
當(dāng)數(shù)據(jù)變化時(shí)威创,就會(huì) 觸發(fā)template等相關(guān)UI的更新

【賦予 非data中定義的變量 以響應(yīng)式的能力 ——
原先,我們是借助Vue的data函數(shù)谎懦,完成響應(yīng)式變量的定義的肚豺;
有了ref之后,我們可以不借助data中的定義界拦,
而直接在普通的函數(shù)中對(duì)js變量proxy封裝吸申,
就可以對(duì) 普通的js引用 賦能響應(yīng)式了】;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
        `,
        setup(props, context) {
            const { ref } = Vue;
            let name = ref('guan');
            setTimeout(() => {
                name.value = 'zhao';
            }, 2000);
            return { name }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:


兩秒后自動(dòng)變化:


reactive()概念享甸、原理 與 實(shí)戰(zhàn)

使用reactive()用于處理 非基礎(chǔ)類型的數(shù)據(jù)(如Object截碴、Array),賦能響應(yīng)式蛉威;
原理類似ref()日丹,只是處理的數(shù)據(jù)格式不同而已;

如下蚯嫌,普通的Object類型是沒有響應(yīng)式的效果的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj.name}}</div>    
        `,
        setup(props, context) {
            // const { ref } = Vue;
            const nameObj = { name: 'guan'};
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);
            return { nameObj }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果哲虾,兩秒后無反應(yīng):

使用reactive()處理Object類型后,具備響應(yīng)式的能力:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj.name}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive({ name: 'guan'});
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);
            return { nameObj }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:


兩秒后自動(dòng)變化:

使用reactive()處理Array類型數(shù)據(jù)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj[0]}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive([123, 99, 567]);
            setTimeout(() => {
                nameObj[0] = 666;
            }, 2000);
            return { nameObj }
        }
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:


兩秒后自動(dòng)變化:


使用readonly限制對(duì)象的訪問權(quán)限

使用readonly()封裝對(duì)象齐帚,可以限制對(duì)象為只讀權(quán)限妒牙;

<script>

    const app = Vue.createApp({

        template: `
            <div>{{nameObj[0]}}</div>    
            <div>{{copyNameObj[0]}}</div> 
        `,
        setup(props, context) {
            const { reactive, readonly } = Vue;
            const nameObj = reactive([123, 99, 567]);
            const copyNameObj = readonly(nameObj);//----
            setTimeout(() => {
                nameObj[0] = 666;
                copyNameObj[0] = 666;//----
            }, 2000);
            return { nameObj, copyNameObj }//----
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行兩秒之后會(huì)有相應(yīng)的報(bào)錯(cuò):


錯(cuò)誤的解構(gòu)案例

如下的解構(gòu)是行不通的,
const { name } = nameObj;只能拿到nameObj的值对妄,拿不到proxy對(duì)象;

<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive({ name: 'guan'});
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);

            const { name } = nameObj;
            return { name }
        }
    });

    const vm = app.mount('#heheApp');
</script>


注意解構(gòu)技巧

賦值時(shí) 加上{}敢朱,會(huì) 直取 其 賦值右側(cè) Object的值剪菱;

<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive({ name: 'guan'});
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);

            const { name } = nameObj;
            console.log(name);
            return { name }
        }
    });

    const vm = app.mount('#heheApp');
</script>



直接賦值,便是直接賦值一份引用拴签;

<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
        `,
        setup(props, context) {
            const { reactive } = Vue;
            const nameObj = reactive({ name: 'guan'});
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);

            const name  = nameObj;
            console.log(name);
            return { name }
        }
    });

    const vm = app.mount('#heheApp');
</script>


使用toRefs()對(duì)reactive對(duì)象進(jìn)一步封裝

--- toRefs() expects a reactive object孝常;
首先,toRefs()需要接受一個(gè)reactive對(duì)象蚓哩;
即构灸,傳給toRefs()之前,需要先用reactive()進(jìn)行封裝岸梨;

--- 使用toRefs()對(duì)reactive封裝的對(duì)象 進(jìn)一步封裝喜颁,
便可以順利解構(gòu)稠氮;

--- 原理:toRefs()將類似proxy({name:'guan'})的結(jié)構(gòu),
轉(zhuǎn)化成類似{ name: proxy( {value:'guan'}) }的結(jié)構(gòu)半开,
這里可以看到name鍵的值隔披,其實(shí)就類似于ref的處理結(jié)果;

然后使用const { name }對(duì){ name: proxy( {value:'guan'}) }進(jìn)行解構(gòu)賦值寂拆,
左側(cè)name變量 拿到的就是proxy( {value:'guan'})這一部分的值奢米,
所以放入DOM節(jié)點(diǎn)展示時(shí)候,
直接使用name即可纠永;

--- w蕹ぁ!尝江!注意:
類似reactive()的處理結(jié)果涉波,
proxy({name:'guan'})的結(jié)構(gòu),
放入DOM節(jié)點(diǎn)展示時(shí)候茂装,需要使用nameObj.name的格式怠蹂;
而類似ref()的處理結(jié)果,
proxy( {value:'guan'})的結(jié)構(gòu)少态,
放入DOM節(jié)點(diǎn)展示時(shí)候城侧,直接使用nameObj的格式即可;

<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
        `,
        setup(props, context) {
            const { reactive, toRefs } = Vue;
            const nameObj = reactive({ name: 'guan'});
            setTimeout(() => {
                nameObj.name = 'zhao';
            }, 2000);

            const { name } = toRefs(nameObj);
            console.log(name);
            return { name }
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行兩秒后彼妻,自動(dòng)更新UI:


多個(gè)屬性進(jìn)行解構(gòu)

注意多個(gè)屬性解構(gòu)時(shí)的寫法 以及
return時(shí)的寫法嫌佑;

<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
            <div>{{age}}</div>    
            <div>{{address}}</div>    
        `,
        setup(props, context) {
            const { reactive, toRefs } = Vue;
            const nameObj = reactive({ name: 'guan', age: 22, address:'chaozhou'});
            setTimeout(() => {
                nameObj.name = 'zhao';
                nameObj.age = 66;
                nameObj.address = 'guangzhou';
            }, 2000);

            const { name, age, address } = (nameObj);
            console.log(name, age, address);
            return { name, age, address }
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行結(jié)果(當(dāng)然不會(huì)自動(dòng)更新):


多個(gè)屬性 配合toRefs() 進(jìn)行解構(gòu)

<script>

    const app = Vue.createApp({

        template: `
            <div>{{name}}</div>    
            <div>{{age}}</div>    
            <div>{{address}}</div>    
        `,
        setup(props, context) {
            const { reactive, toRefs } = Vue;
            const nameObj = reactive({ name: 'guan', age: 22, address:'chaozhou'});
            setTimeout(() => {
                nameObj.name = 'zhao';
                nameObj.age = 66;
                nameObj.address = 'guangzhou';
            }, 2000);

            const { name, age, address } = toRefs(nameObj);
            console.log(name, age, address);
            return { name, age, address }
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行結(jié)果:

兩秒后自動(dòng)刷新:


toRefs()無法處理 undefined的鍵值

如果意圖解構(gòu)的鍵,
不存在于toRefs()封裝的對(duì)象中侨歉,
使用時(shí)會(huì)報(bào)錯(cuò):

<script>

    const app = Vue.createApp({

        template: `
            <div>{{age}}</div>    
        `,
        setup(props, context) {
            const { reactive, toRefs } = Vue;
            const data = reactive({ name: 'guan'});
            const { age } = toRefs(data);
            setTimeout(() => {
                age.value = 'zhao';
            }, 2000);

            return { age }
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行結(jié)果:


使用toRef()應(yīng)對(duì)上述問題

toRef(data, key)會(huì)嘗試從data中讀取key對(duì)應(yīng)的鍵值屋摇,
如果讀得到,就直接取值幽邓,
如果讀不到炮温,會(huì)賦值undefined,后續(xù)可以為之賦實(shí)值:

<script>

    const app = Vue.createApp({

        template: `
            <div>{{age}}</div>    
        `,
        setup(props, context) {
            const { reactive, toRef } = Vue;
            const data = reactive({ name: 'guan'});
            const age = toRef(data, 'age');
            setTimeout(() => {
                age.value = 'zhao';
            }, 2000);

            return { age }
        }
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行兩秒后自動(dòng)更新:


關(guān)于setup函數(shù)的三個(gè)參數(shù)

setup函數(shù)的context參數(shù)中的三個(gè)屬性牵舵,即attrs, slots, emit柒啤;
獲取方法(解構(gòu)context參數(shù)):

setup(props, context) {
           const { attrs, slots, emit } = context;
           return { };
       }
attrs

-- 是一個(gè)Proxy對(duì)象;
-- 子組件的none-props屬性都存進(jìn)attrs中畸颅;

如下担巩,父組件調(diào)用子組件,傳遞myfield屬性没炒,
子組件沒有用props接收涛癌,則myfield作為none-props屬性被 子組件承接,
這時(shí)候會(huì)存進(jìn)setup函數(shù)的attrs中:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({
        template: `
            <child myfield='heheda' />    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(attrs);
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:可以看到attrs也是一個(gè)Proxy對(duì)象


(attrs.myfield)打印取值:

<script>

    const app = Vue.createApp({
        template: `
            <child myfield='heheda' />    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(attrs.myfield);
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>


slots

-- 是一個(gè)Proxy對(duì)象拳话;
-- 其中有一個(gè) 以為default為鍵的函數(shù)先匪,
這個(gè)函數(shù)會(huì)以 虛擬DOM的形式,
返回 傳給 子組件的slot插槽組件假颇;



打印slots屬性:

<script>

    const app = Vue.createApp({
        template: `
            <child>hehehe</child>    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
            <div><slot/></div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(slots);
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>

打印slots屬性中的 default函數(shù)胚鸯, 可見其返回的是虛擬DOM:

<script>

    const app = Vue.createApp({
        template: `
            <child>hehehe</child>    
        `
    });

    app.component('child', {
        template:`
            <div>child</div>
            <div><slot/></div>
        `,
        setup(props, context) {
            const { attrs, slots, emit } = context;
            console.log(slots.default());
            return { };
        }
    })

    const vm = app.mount('#heheApp');
</script>


使用setup中 context參數(shù)的 slots屬性中的 default方法所返回的 虛擬DOM,通過render函數(shù)的形式笨鸡,在setup函數(shù)返回姜钳,可以覆蓋template的內(nèi)容,渲染UI
<script>

    const app = Vue.createApp({
        template: `
            <child>hehehe</child>    
        `
    });

    app.component('child', {
        template:`
            <div>child Text</div>
            <div><slot/></div>
        `,
        setup(props, context) {
            const { h } = Vue;
            const { attrs, slots, emit } = context;
            return () => h('div', {}, slots.default());
        }
    })

    const vm = app.mount('#heheApp');
</script>


補(bǔ)充:不使用 Vue3 的這個(gè)compositionAPI形耗,子組件只能這樣去獲取slots內(nèi)容

通過在其他函數(shù)中哥桥,使用this.$slots的方式調(diào)用到slots內(nèi)容

<script>

    const app = Vue.createApp({
        template: `
            <child>hehehe</child>    
        `
    });

    app.component('child', {
        mounted () {
            console.log("this.$slots --- ",this.$slots);
        },
        setup(props, context) {
            console.log("context.slots --- ", context.slots);

            const { h } = Vue;
            const { attrs, slots, emit } = context;
            return () => h('div', {}, slots.default());
        }
    })

    const vm = app.mount('#heheApp');
</script>

運(yùn)行結(jié)果如下,可以看到跟setup函數(shù)的context.slots是一樣的:


回顧 沒有 CompositionAPI時(shí)激涤,emit的用法
<script>

    const app = Vue.createApp({
        methods: {
            handleChange() {
                alert('hehehe');
            }
        },
        template: `
            <child @change="handleChange">hehehe</child>    
        `
    });

    app.component('child', {
        template: `
            <div  >hehehe</div>    
        `,
        mounted () {
            this.$emit('change');
        }
    })

    const vm = app.mount('#heheApp');
</script>


使用setup的 context.emit 替代 this.$emit
<script>

    const app = Vue.createApp({
        methods: {
            handleChange() {
                alert('hehehe');
            }
        },
        template: `
            <child @change="handleChange">hehehe</child>    
        `
    });

    app.component('child', {
        template: `
            <div @click="handleClick">6666</div>    
        `,
        setup(props, context) {
            const { h } = Vue;
            const { attrs, slots, emit } = context;
            function handleClick() {emit('change');}
            return { handleClick };
        }
    })

    const vm = app.mount('#heheApp');
</script>

運(yùn)行拟糕,點(diǎn)擊文本:


使用Composition API開發(fā) todoList

調(diào)測(cè)input框事件

setup中,
--- const inputValue = ref('6666');定義到一個(gè)proxy對(duì)象倦踢,
可以將此對(duì)象 用于setup外的 template中送滞,
完成雙向綁定中的一環(huán)——數(shù)據(jù)字段映射到 UI!H杌印@缧帷;

--- handleInputValueChange定義一個(gè)方法晤碘;
運(yùn)行時(shí)褂微,將對(duì)應(yīng)觸發(fā)組件內(nèi)容
賦值給 inputValue园爷,
完成雙向綁定中的另一環(huán)——UI 映射到數(shù)據(jù)3杪臁!M纭求厕;
template中,
:value="inputValue"使得對(duì)象的內(nèi)容 初始化顯示inputValue的內(nèi)容扰楼;

--- @input="handleInputValueChange"使得輸入框被用戶輸入新的內(nèi)容時(shí)甘改,
會(huì)調(diào)用對(duì)應(yīng)的方法,這里調(diào)用:

<script>

    const app = Vue.createApp({
        setup() {
            const { ref } = Vue;
            const inputValue = ref('6666');
            const handleInputValueChange = (e) => {
                console.log("e --- ",e);
                console.log("e.target.value", e.target.value);

                inputValue.value = e.target.value;
            }
            return {
                inputValue,
                handleInputValueChange
            }
        },
        template: `
            <div>
                <div>
                    <input :value="inputValue" @input="handleInputValueChange"/>
                    <div>{{inputValue}}</div>
                    <button>提交</button>
                </div>
                <ul>
                    <li>1</li>
                    <li>2</li>
                </ul>                        
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果:


完善toDoList案例

--- setup 中定義 數(shù)組list灭抑,以及 handleSubmit處理函數(shù),
并都在return中返回抵代;

--- template中腾节,
button添加click事件回調(diào);
li 標(biāo)簽,使用v-for案腺,完成列表渲染:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({
        setup() {
            const { ref, reactive } = Vue;
            const inputValue = ref('6666');
            const list = reactive([]);
            const handleInputValueChange = (e) => {
                inputValue.value = e.target.value;
            }
            const handleSubmit = () => {
                console.log("now, inputValue.value --- ", inputValue.value);
                list.push(inputValue.value);
            }
            return {
                list,
                inputValue,
                handleInputValueChange,
                handleSubmit
            }
        },
        template: `
            <div>
                <div>
                    <input :value="inputValue" @input="handleInputValueChange"/>
                    <button @click="handleSubmit">提交</button>
                </div>
                <ul>
                    <li v-for="(item, index) in list" :key="index">{{item}}</li>
                </ul>                        
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:


優(yōu)化上例的邏輯結(jié)構(gòu)庆冕!

如下,將 setup()中劈榨,
--- 與 list 相關(guān)的定義和操作函數(shù)访递,
封裝到 listHandleAction中,然后return同辣;

--- 與 inputValue 相關(guān)的定義和操作函數(shù)拷姿,
封裝到 inputHandleAction中,然后return旱函;

--- 最后 setup() 調(diào)用以上兩個(gè)業(yè)務(wù)模塊封裝函數(shù)响巢,
解構(gòu)返回的內(nèi)容,進(jìn)行中轉(zhuǎn)調(diào)用棒妨;

【分模塊 聚合業(yè)務(wù)邏輯踪古,只留一個(gè)核心方法進(jìn)行統(tǒng)籌調(diào)度,有MVP那味了】
【這樣設(shè)計(jì)券腔,業(yè)務(wù)分明伏穆,方便定位問題,可讀性纷纫、可維護(hù)性高U砩ā!

<script>

    const listHandleAction = () => {
        const { reactive } = Vue;
        const list = reactive([]);
        const addItemToList = (item) => {
            console.log("now, item --- ", item);
            list.push(item);
        }
        return { list, addItemToList }
    }

    const inputHandleAction = () => {
        const { ref } = Vue;
        const inputValue = ref('');
        const handleInputValueChange = (e) => {
            inputValue.value = e.target.value;
        }
        return { inputValue, handleInputValueChange }
    }

    const app = Vue.createApp({
        setup() {
            const { list, addItemToList } = listHandleAction();
            const { inputValue, handleInputValueChange } = inputHandleAction();
            return {
                list, addItemToList,
                inputValue, handleInputValueChange
            }
        },
        template: `
            <div>
                <div>
                    <input :value="inputValue" @input="handleInputValueChange"/>
                    <button @click="addItemToList(inputValue)">提交</button>
                </div>
                <ul>
                    <li v-for="(item, index) in list" :key="index">{{item}}</li>
                </ul>                        
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果:


setup的 computed 計(jì)算屬性

<script>

    const app = Vue.createApp({
        setup() {
            const { ref, computed } = Vue;
            const count = ref(0);
            const handleClick = () => {
                count.value += 1;
            }
            const countAddFive = computed(() => {
                return count.value + 5;
            })
            return { count, countAddFive, handleClick }
        },
        template: `
            <div>
                <div>
                    <span @click="handleClick">{{count}}</span> --- {{countAddFive}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行結(jié)果:


當(dāng)然以上是computed 的默認(rèn)用法涛酗,實(shí)際上它可以接收一個(gè)對(duì)象

這個(gè)對(duì)象包含兩個(gè)函數(shù)屬性铡原,
第一個(gè)是get函數(shù),其內(nèi)容即取值時(shí)候返回的內(nèi)容商叹,同默認(rèn)用法燕刻;
第二個(gè)是set函數(shù),當(dāng)試圖修改computed變量的值時(shí)剖笙,就會(huì)回調(diào)這個(gè)方法卵洗,
接收的參數(shù),即試圖修改的值

如下弥咪,試圖在3秒后修改computed變量countAddFive的值过蹂,
這時(shí)回調(diào)set方法:

<script>

    const app = Vue.createApp({
        setup() {
            const { ref, computed } = Vue;
            const count = ref(0);
            const handleClick = () => {
                count.value += 1;
            }
            const countAddFive = computed({
                get: () => {
                    return count.value + 5;
                },
                set: (param) => {
                    count.value = param -5;
                }
            })

            setTimeout(() => {
                countAddFive.value = 1000;
            }, 2000);

            return { count, countAddFive, handleClick }
        },
        template: `
            <div>
                <div>
                    <span @click="handleClick">{{count}}</span> --- {{countAddFive}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行3s后:


將上例的處理值換成 Object類型涮毫,再例

<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, computed } = Vue;
            const countObj = reactive({ count: 0 });
            const handleClick = () => {
                countObj.count += 1;
            }
            const countAddFive = computed({
                get: () => {
                    return countObj.count + 5;
                },
                set: (param) => {
                    countObj.count = param -5;
                }
            })

            setTimeout(() => {
                countAddFive.value = 1000;
            }, 2000);

            return { countObj, countAddFive, handleClick }
        },
        template: `
            <div>
                <div>
                    <span @click="handleClick">{{countObj.count}}</span> --- {{countAddFive}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果同上例涧团;


setup 中的 watch 監(jiān)聽

如下凉泄,
---watch一個(gè)參數(shù)為要監(jiān)聽的引用盲赊,
第二個(gè)參數(shù)為函數(shù)類型候生,當(dāng)監(jiān)聽的引用發(fā)生變化時(shí)會(huì)回調(diào)私爷,
其有兩個(gè)參數(shù)扫尖,一個(gè)是當(dāng)前(變化后的)值伐坏,一個(gè)是變化前的值;

--- input組件中击胜,v-model完成雙向綁定?髡!偶摔!

--- input輸入內(nèi)容時(shí)暇唾,觸發(fā) 雙向綁定的特性,
內(nèi)容映射到name引用上辰斋,
ref響應(yīng)特性策州,name的內(nèi)容又映射到{{name}}DOM節(jié)點(diǎn)上:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({
        setup() {
            const { ref, watch } = Vue;
            const name = ref('heheda')
            watch(name, (currentValue, prevValue)  => {
                console.log(currentValue, prevValue);
            })
            return { name }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行效果:


setup 中的 watch 監(jiān)聽:監(jiān)聽Object類型

注意setup的watch的 可監(jiān)聽數(shù)據(jù)類型


所以,這里主要是
---將watch的 第一個(gè)參數(shù)改成 函數(shù)亡呵;
---使用toRefs抽活,簡(jiǎn)化傳遞過程;
(不然template要接收和處理的就是 nameObject.name了锰什,而不是這里的name)

<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, watch, toRefs } = Vue;
            const nameObj = reactive({name: 'heheda'});
            watch(() => nameObj.name, (currentValue, prevValue)  => {
                console.log(currentValue, prevValue);
            });

            const { name } = toRefs(nameObj);
            return { name }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果同上例下硕;


setup 中的 watch 監(jiān)聽:監(jiān)聽Object類型的 多個(gè)屬性

注意watch的參數(shù)寫法,
一參寫成汁胆,以函數(shù)類型元素數(shù)組梭姓;
二參,參數(shù)列表寫成兩個(gè)數(shù)組嫩码,
第一個(gè)為current值數(shù)組誉尖,第二個(gè)為prev值數(shù)組;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, watch, toRefs } = Vue;
            const nameObj = reactive({name: 'heheda', englishName:"lueluelue"});
            watch([() => nameObj.name, () => nameObj.englishName],
                 ([curName, curEngN], [prevName, prevEngN])  => {
                console.log(curName, prevName, curEngN, prevEngN);
            });

            const { name, englishName } = toRefs(nameObj);
            return { name, englishName }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
                <div>
                    EnglishName:<input v-model="englishName">
                </div>
                <div>
                    EnglishName is {{englishName}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行铸题,先在Name輸入框輸入铡恕,后再EnglishName框輸入,效果:


setup 中的 watchEffect監(jiān)聽 以及 與 watch 的異同比較

函數(shù)中丢间,使得純函數(shù) 變成 非純函數(shù)的 異步處理等部分邏輯塊探熔,
稱之為effect塊

  • --- watch惰性的烘挫,只有watch監(jiān)聽的字段發(fā)生變化時(shí)诀艰,
    watch的處理邏輯才會(huì)被回調(diào);
    --- watchEffect即時(shí)性的饮六,也就是除了watch回調(diào)特性其垄,
    watchEffect處理邏輯還會(huì)在頁面渲染完成時(shí)立馬先執(zhí)行一次,
    watchEffect監(jiān)聽的字段未曾改變卤橄,
    watchEffect就已經(jīng)執(zhí)行了一次绿满;

    (實(shí)例可以見下例運(yùn)行效果)

  • watch需要寫明監(jiān)聽字段
    watchEffect不需要窟扑,直接寫處理邏輯即可棒口,
    底層封裝會(huì)寄月!自動(dòng)監(jiān)聽!所寫處理邏輯中用到的无牵!所有字段!厂抖;


    如下例子中茎毁,
    watchEffect的處理邏輯——console.log(nameObj.name, nameObj.englishName);
    僅一行代碼忱辅,
    完成對(duì)nameObj.namenameObj.englishName兩個(gè)字段的監(jiān)聽七蜘,
    并完成了處理邏輯;

  • watch可以直接從參數(shù)列表中獲取到之前(變化前)的值當(dāng)前(變化后)的值墙懂,
    watchEffect不行橡卤,處理邏輯中拿到的直接就是當(dāng)前(變化后)的值


  • 兩者都可以用以下的方式损搬,在一個(gè)設(shè)定的時(shí)延之后碧库,停止監(jiān)聽
  • watch 配置 immediate屬性,可使具備同watchEffect的 即時(shí)性
<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, watch, watchEffect, toRefs } = Vue;
            const nameObj = reactive({name: 'heheda', englishName:"lueluelue"});

            watchEffect(() => {
                console.log(nameObj.name, nameObj.englishName);
            })

            const { name, englishName } = toRefs(nameObj);
            return { name, englishName }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
                <div>
                    EnglishName:<input v-model="englishName">
                </div>
                <div>
                    EnglishName is {{englishName}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

跟緊console.log(nameObj.name, nameObj.englishName);巧勤,
先在Name輸入框輸入123嵌灰,后再EnglishName框輸入456,運(yùn)行效果:

注意第一行打印颅悉,第一行是頁面渲染完成時(shí)立馬執(zhí)行沽瞭,
用戶未曾輸入內(nèi)容,watchEffect監(jiān)聽的字段未曾改變剩瓶,
watchEffect就已經(jīng)執(zhí)行了一次驹溃,體現(xiàn)watchEffect即時(shí)性!Q邮铩豌鹤!


兩者都可以用以下的方式,在一個(gè)設(shè)定的時(shí)延之后搂鲫,停止監(jiān)聽

watch / watchEffect函數(shù)返回值 賦給一個(gè)字段(如下stopWatch / stopWatchEffect)傍药;
接著在watch / watchEffect處理邏輯中,
編寫類似setTimeout的異步函數(shù)魂仍,
并在其中調(diào)用 與 剛剛定義的字段 同名的 函數(shù)(如下stopWatch() / stopWatchEffect())拐辽,
即可停止對(duì)應(yīng)的監(jiān)聽

<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, watch, watchEffect, toRefs } = Vue;
            const nameObj = reactive({name: 'heheda', englishName:"lueluelue"});
            const stopWatch = watch([() => nameObj.name, () => nameObj.englishName],
                 ([curName, curEngN], [prevName, prevEngN])  => {
                console.log(curName, prevName, curEngN, prevEngN);
                setTimeout(() => {
                    stopWatch();
                }, 3000);
            });

            const stopWatchEffect = watchEffect(() => {
                console.log(nameObj.name, nameObj.englishName);
                setTimeout(() => {
                    stopWatchEffect();
                }, 3000);
            })

            const { name, englishName } = toRefs(nameObj);
            return { name, englishName }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
                <div>
                    EnglishName:<input v-model="englishName">
                </div>
                <div>
                    EnglishName is {{englishName}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行擦酌,可以看到俱诸,
前3s(我們?cè)O(shè)定的時(shí)延)兩個(gè)輸入框是可以響應(yīng)監(jiān)聽的,
但是3s之后赊舶,無論怎么輸入內(nèi)容睁搭,console也不會(huì)打印log赶诊,
因?yàn)檫@時(shí)兩個(gè)監(jiān)聽效果已經(jīng)取消了:


watch 配置 immediate屬性,可使具備同watchEffect的 即時(shí)性

如下园骆,使用{ immediate: true}watch 配置 immediate屬性舔痪,
可使具備同watchEffect的 即時(shí)性:

<script>

    const app = Vue.createApp({
        setup() {
            const { reactive, watch, toRefs } = Vue;
            const nameObj = reactive({name: 'heheda', englishName:"lueluelue"});
            const stopWatch = watch([() => nameObj.name, () => nameObj.englishName],
                 ([curName, curEngN], [prevName, prevEngN])  => {
                console.log(curName, prevName, curEngN, prevEngN);
            }, { immediate: true});

            const { name, englishName } = toRefs(nameObj);
            return { name, englishName }
        },
        template: `
            <div>
                <div>
                    Name:<input v-model="name">
                </div>
                <div>
                    Name is {{name}}
                </div>
                <div>
                    EnglishName:<input v-model="englishName">
                </div>
                <div>
                    EnglishName is {{englishName}}
                </div>
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果如下,undefined是因第一次執(zhí)行時(shí)锌唾,
監(jiān)聽的變量還沒有變化锄码,
所以就沒有先前值(prevValue)的說法,只有當(dāng)前值(currentValue)


setup 中的 生命周期

--- Vue3.0提供了一些對(duì)應(yīng)生命周期的晌涕,可以寫在setup函數(shù)中的回調(diào)方法滋捶;
(具體請(qǐng)看 下方例程)

--- setup函數(shù)的執(zhí)行時(shí)間點(diǎn) 在于beforeCreateCreated之間,
所以CompositionAPI里邊是沒有類似onBeforeCreateonCreated的方法的余黎,
要寫在這兩個(gè)周期中的邏輯重窟,
直接寫在setup中即可;

下面是兩個(gè)Vue3.0引入的新鉤子:
--- onRenderTracked
渲染跟蹤惧财,
跟蹤 收集響應(yīng)式依賴的時(shí)機(jī)巡扇,
每次準(zhǔn)備開始渲染時(shí)(onBeforeMount后,onMounted前)回調(diào)可缚;

--- onRenderTriggered
渲染觸發(fā)霎迫,
每次觸發(fā)頁面重新渲染時(shí)回調(diào),
回調(diào)后帘靡,下一輪的 onBeforeMount緊跟其后知给;

<script>

    const app = Vue.createApp({
        setup() {
            const { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
                 onRenderTracked, onRenderTriggered } = Vue;
            const heheda = ref('heheda');
            onBeforeMount(() => {
                console.log('onBeforeMount');
            }),
            onMounted(() => {
                console.log('onMounted');
            }),
            onBeforeUpdate(() => {
                console.log('onBeforeUpdate');
            }),
            onUpdated(() => {
                console.log('onUpdated');
            }),
            onRenderTracked((event) => {
                console.log('onRenderTracked', event);
            }),
            onRenderTriggered((event) => {
                console.log('onRenderTriggered', event);
            })
            const handleClick = () => {
                heheda.value = 'lululu';
                console.log("you had clicked the text!");
            }
            return { heheda, handleClick }
        },
        template: `
            <div @click="handleClick">
                {{heheda}}
            </div>    
        `
    });

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果:


setup中的provide、inject用法

--- 父組件拿出provide描姚,提供鍵值對(duì)涩赢;
--- 子組件拿出inject,一參為接收鍵轩勘,二參為默認(rèn)值筒扒;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World! heheheheheheda</title>
    <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
    <div id="heheApp"></div>
</body>
<script>

    const app = Vue.createApp({
        setup() {
            const { provide } = Vue;
            provide('name', 'li');
            return { }
        },
        template: `
            <div>
                <child />
            </div>    
        `
    });

    app.component('child', {
        setup() {
            const { inject } = Vue;
            const name = inject('name', 'default');
            return {name}
        },
        template: `
            <div>{{name}}</div>    
        `
    })

    const vm = app.mount('#heheApp');
</script>
</html>

運(yùn)行結(jié)果:


配合上ref實(shí)現(xiàn) 響應(yīng)特性 以及 readonly實(shí)現(xiàn) 單向數(shù)據(jù)流

--- setup中, 借provide傳輸?shù)?數(shù)據(jù)字段 配合上ref绊寻,
使之具備響應(yīng)的特性花墩;
--- 父組件提供 更改數(shù)據(jù)的接口,
在使用provide傳遞 數(shù)據(jù)字段時(shí)澄步,加上 readonly包裹冰蘑,
使得子組件 需要更改 父組件傳遞過來的數(shù)據(jù)字段 時(shí),
無法直接 修改字段村缸,
需調(diào)用 父組件的接口方法 更改祠肥,
按 單向數(shù)據(jù)流 規(guī)范編程;

<script>

    const app = Vue.createApp({
        setup() {
            const { provide, ref, readonly } = Vue;
            const name = ref('li');
            provide('name', readonly(name));
            provide('changeName', (value) => {
                name.value = value;
            })
            return { }
        },
        template: `
            <div>
                <child />
            </div>    
        `
    });

    app.component('child', {
        setup() {
            const { inject } = Vue;
            const name = inject('name', 'default');
            const changeName = inject('changeName');
            const handleClick = () => {
                changeName('lin');
            }
            return { name, handleClick }
        },
        template: `
            <div @click="handleClick">{{name}}</div>    
        `
    })

    const vm = app.mount('#heheApp');
</script>

運(yùn)行效果:

點(diǎn)擊:


setup結(jié)合ref指令

前面筆記《Vue3 | Mixin梯皿、自定義指令仇箱、Teleport傳送門县恕、Render函數(shù)、插件 詳解 及 案例分析》有寫到普通場(chǎng)景的ref指定剂桥;

--- setup中的ref是前面說的生成響應(yīng)式字段的意思忠烛;
--- template中的ref是獲取對(duì)應(yīng)的dom節(jié)點(diǎn);
--- 而當(dāng) template中的ref指定的字段名渊额,
跟setup中的ref生成的響應(yīng)式字段名一樣的時(shí)候况木,兩者就會(huì)關(guān)聯(lián)起來;

如下旬迹,
template中和setup中的字段heheda關(guān)聯(lián)起來,
在setup的onMounted中求类,使用這個(gè)DOM節(jié)點(diǎn)字段奔垦,
打印DOM代碼:

<script>

    const app = Vue.createApp({
        setup() {
            const { ref, onMounted } = Vue;
            const heheda = ref(null);
            onMounted(() => {
                console.log(heheda);
                console.log(heheda.value);
            })
            return { heheda }
        },
        template: `
            <div>
                <div ref="heheda">lueluelue</div>
            </div>    
        `
    });


    const vm = app.mount('#heheApp');
</script>

運(yùn)行結(jié)果:






最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市尸疆,隨后出現(xiàn)的幾起案子椿猎,更是在濱河造成了極大的恐慌,老刑警劉巖寿弱,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犯眠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡症革,警方通過查閱死者的電腦和手機(jī)筐咧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來噪矛,“玉大人量蕊,你說我怎么就攤上這事⊥Оぃ” “怎么了残炮?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)缩滨。 經(jīng)常有香客問我势就,道長(zhǎng),這世上最難降的妖魔是什么脉漏? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任苞冯,我火速辦了婚禮,結(jié)果婚禮上鸠删,老公的妹妹穿的比我還像新娘抱完。我一直安慰自己,他們只是感情好刃泡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布巧娱。 她就那樣靜靜地躺著碉怔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禁添。 梳的紋絲不亂的頭發(fā)上撮胧,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音老翘,去河邊找鬼芹啥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛铺峭,可吹牛的內(nèi)容都是我干的墓怀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼卫键,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼傀履!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起莉炉,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤钓账,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后絮宁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梆暮,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年绍昂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啦粹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡治专,死狀恐怖卖陵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情张峰,我是刑警寧澤泪蔫,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站喘批,受9級(jí)特大地震影響撩荣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饶深,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一餐曹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧敌厘,春花似錦台猴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曹步。三九已至,卻和暖如春休讳,著一層夾襖步出監(jiān)牢的瞬間讲婚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工俊柔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筹麸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓雏婶,卻偏偏與公主長(zhǎng)得像物赶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子留晚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345