第十節:復習mixin/extends 和 開啟Vue3的Composition Api(setup、reactiveApi、refApi、readonly)


一. Vue2.x的mixin和extends

1. mixin簡介

(1). 目前我們是使用組件化的方式在開發整個Vue的應用程序,但是組件和組件之間有時候會存在相同的代碼邏輯,我們希望對相同的代碼邏輯進行抽取

(2). 在Vue2和Vue3中都支持的一種方式就是使用Mixin來完成:

 A. Mixin提供了一種非常靈活的方式,來分發Vue組件中的可復用功能

 B. 一個Mixin對象可以包含任何組件選項

 C. 當組件使用Mixin對象時,所有Mixin對象的選項將被 混合 進入該組件本身的選項中

2. mixin入門和合並規則

(1). 合並規則

(2). 代碼實戰 

A. 封裝方法common1.js

export const myCommon1 = {
    created() {
        console.log('---------------我是created2執行--------------')
    },
    data() {
        return {
            msg: 'hello msg2',
            title: 'hello title2',
            name:'hello name2',
        };
    },
    methods: {
        Test1() {
            console.log('test1111111');
        },
        Test2() {
            console.log('test222222');
        }
    }
}
View Code

B. 組件代碼 

<template>
    <div>
        <h4>{{msg}}</h4>
        <h4>{{title}}</h4>
        <h5><button @click="Test1">點我1</button></h5>
        <!--下面代碼引入common1.js的時候再打開,否則注釋掉 -->
        <h4>{{name}}</h4>
        <h5><button @click="Test2">點我2</button></h5>
    </div>
</template>

<script> import { myCommon1 } from './common1.js'

    export default {
      mixins:[myCommon1],
        created() {
            console.log('---------------我是created1執行--------------')
        },
        data() {
            return {
                msg: 'hello msg1',
                title: 'hello title1'
            };
        },
        methods: {
            Test1() {
                console.log('test1');
            }
        }

    }
</script>

運行結果:msg、title、和Test1方法,調用的都是組件自身定義的內容; name 和Test2方法則走的是common1.js中的內容。

3. mixin的全局混入

 如果組件中的某些選項,是所有的組件都需要擁有的,那么這個時候我們可以使用全局的mixin:

A. 全局的Mixin可以使用 應用app的方法 mixin 來完成注冊;

B. 一旦注冊,那么全局混入的選項將會影響每一個組件;

main.js中代碼:

const app = createApp(App);

// 測試mixin的全局混入
app.mixin({
    created() {
        console.log('---------------我是created2執行--------------')
    },
    data() {
        return {
            msg: 'hello msg2',
            title: 'hello title2',
            name:'hello name2',
        };
    },
    methods: {
        Test1() {
            console.log('test1111111');
        },
        Test2() {
            console.log('test222222');
        }
    }
});


app.mount('#app')
View Code

4. extends的使用

 另外一個類似於Mixin的方式是通過extends屬性:允許聲明擴展另外一個組件,類似於Mixins;

抽離的組件

<template>
    <div>
            
    </div>
</template>

<script>
    export default {
        data() {
            return {
                msg: 'hello msg2',
                title: 'hello title2',
                name:'hello name2',
            };
        },
        methods: {
            Test1() {
                console.log('test1111111');
            },
            Test2() {
                console.log('test222222');
            }
        }
    }
</script>

<style scoped>
</style>
View Code

父組件

<template>
    <div>
        <h4>{{msg}}</h4>
        <h4>{{title}}</h4>
        <h5><button @click="Test1">點我1</button></h5>
    </div>
</template>

<script>
    import BasePage from './BasePage.vue';
    
    export default {
        extends:BasePage,
        created() {
            console.log('---------------我是created1執行--------------')
        },
        data() {
            return {};
        },
        methods: {

        }

    }
</script>

<style scoped>
</style>
View Code

5. 總結 

 在開發中extends用的非常少,在Vue2中比較推薦大家使用Mixin,而在Vue3中推薦使用Composition API。 

 

二. ComPosition Api-setup

1. Options Api的缺點

(1). 在Vue2中,我們編寫組件的方式是Options API:

 Options API的一大特點就是在對應的屬性中編寫對應的功能模塊;

 比如data定義數據、methods中定義方法、computed中定義計算屬性、watch中監聽屬性改變,也包括生命周期鈎子(composition api中這些統統干掉,寫在setup中了)

(2). 這種代碼有一個很大的弊端:

 當我們實現某一個功能時,這個功能對應的代碼邏輯會被拆分到各個屬性中

 當我們組件變得更大、更復雜時,邏輯關注點的列表就會增長,那么同一個功能的邏輯就會被拆分的很分散

 尤其對於那些一開始沒有編寫這些組件的人來說,這個組件的代碼是難以閱讀和理解的(閱讀組件的其他人);

2. Composition Api簡介

 Vue Composition API(VCA)的思想就是:同一個邏輯關注點相關的代碼收集在一起會更好。

 在Vue組件中,Composition Api將所有的代碼都寫在setup函數中。 用它來替代之前所編寫的大部分其他選項;比如methods、computed、watch、data、生命周期等等;

3. Composition Api入門-setup

(1). 參數

(2). 返回值 

 setup的返回值可以在模板template中被使用;即之前的data選項、methord都是在返回值暴露。

(3). 代碼分享

子組件

<template>
    <div>
        <h3>我是child1組件</h3>
        <h4>{{msg}}</h4>
        <h4>{{counter}}</h4>
        <h4><button @click="AddNum()">加1</button></h4>
    </div>
</template>

<script>
    export default {
        props: {
            msg: {
                type: String,
                default: 'Hello Child1'
            }
        },
        /* 
         參數說明:
         1. props:獲取父組件傳遞過來的props中定義的屬性
         2. context (或寫成   {attrs, slots, emit}    )
            A.attrs:所有的非prop的attribute;
            B.slots:父組件傳遞過來的插槽(這個在以渲染函數返回時會有作用,后面會講到);
            C.emit:當我們組件內部需要發出事件時會用到emit(因為我們不能訪問this,所以不可以通過 this.$emit發出事件);
         注:setup中不能使用this關鍵字
         
         結果剖析:
            這里點擊按鈕,頁面上顯示的值沒有響應式的增加,console.log中增加了。
            原因:對於一個定義的變量來說,默認情況下,Vue並不會跟蹤它的變化,來引起界面的響應式操作。
              
         */
        // setup(props, context) {
        setup(props, { attrs, slots, emit }) {
            // 1. 測試setup的相關參數
            console.log(props.msg);
            console.log(attrs.id, attrs.class);
            console.log(slots);
            console.log(emit);

            // 2.編寫相關業務
            let counter = 100;
            const AddNum = () => {
                counter++;
                console.log(counter);
            };

            // 3. 測試setup的返回值
            return {
                title: 'hello title1',
                counter,
                AddNum
            }
        }
    }
</script>

<style scoped>
</style>
View Code

父組件

<template>
    <div>
        <child1 msg="hello setup" id="j_child1" class="ypfClass"></child1>    
    </div>
</template>

<script>
    import child1 from './child1.vue';
    export default {
        components: {
            child1
        },
        data() {
            return {};
        }
    }
</script>

<style scoped>
</style>
View Code

剖析: 

  這里點擊按鈕,頁面上顯示的值沒有響應式的增加,console.log中增加了。

  原因:對於一個定義的變量來說,默認情況下,Vue並不會跟蹤它的變化,來引起界面的響應式操作。

注:setup中不可以使用this 

 

三. reactiveApi

1. 用法

作用:為在setup中定義的數據提供響應式的特性

代碼如下:

<template>
    <div>
        <h3>我是child1組件</h3>
        <h4>{{myData.counter}}</h4>
        <h4><button @click="AddNum()">加1</button></h4>
    </div>
</template>

<script> import { reactive } from 'vue';

    export default {
        props: {
            msg: {
                type: String,
                default: 'Hello Child1'
            }
        },
        setup(props, { attrs, slots, emit }) {
            // reactiveApi寫法
            const myData = reactive({ counter: 100 });

            const AddNum = () => {
              myData.counter++;
                console.log(myData.counter);
            };

            return {
              myData,
                AddNum
            }
        }
    }
</script>

2. 原理

 這是因為當我們使用reactive函數處理我們的數據之后,數據再次被使用時就會進行依賴收集

 當數據發生改變時,所有收集到的依賴都是進行對應的響應式操作(比如更新界面);

 事實上,我們編寫的data選項,也是在內部交給了reactive函數將其編程響應式對象的

3. reactive Api對傳入的類型是有限制的

4. reactive其它Api補充

 

 

四. refApi

1. 簡介

 ref 會返回一個可變的響應式對象,該對象作為一個 響應式的引用 維護着它內部的值,這就是ref名稱的來源;它內部的值是在ref的 value 屬性中被維護的;

注:

(1)在模板template中引入ref的值時,Vue會自動幫助我們進行解包操作,所以我們並不需要在模板中通過 ref.value 的方式來使用;

(2)在 setup 函數內部,它依然是一個 ref引用, 所以對其進行操作時,我們依然需要使用 ref.value的方式;

(3)模板中的解包是淺層的解包,如果放在對象里,則不能解包;但是我們將ref放到一個reactive的屬性當中,那么在模板中使用時,它會自動解包

2. 實戰

<template>
    <div>
        <h3>我是child1組件1111</h3>
        <!-- 1. refApi的在template中默認進行淺層解包,不寫.vaule -->
        <h4>1. refApi的淺層解包:{{counter}}</h4>
        <!-- 2. 對象包裹不能解包 -->
        <h4>2. 對象包裹不能解包:{{myInfo1.counter.value}}</h4>
        <!-- 3. 將ref放到一個reactive的屬性當中,那么在模板中使用時,它會自動解包 -->
        <h4>3. 將ref放到一個reactive的屬性當中,那么在模板中使用時,它會自動解包:{{myInfo2.counter}}</h4>
        <h4><button @click="AddNum()">加1</button></h4>
    </div>
</template>

<script>
    import { reactive, ref } from 'vue';

    export default {
        props: {
            msg: {
                type: String,
                default: 'Hello Child1'
            }
        },
        setup(props, { attrs, slots, emit }) {
            //1. refApi寫法
            let counter = ref(100); //2.普通對象包裹,則不能解包
            let myInfo1 = {
                counter
            }
            //3.reactive包裹ref對象,又會自動解包
            let myInfo2=reactive({
                counter
            })
            
            const AddNum = () => {
                counter.value++;
                console.log(counter.value);
            };
            return {
                counter,
                myInfo1,
                myInfo2,
                AddNum
            }
        }
    }
</script>

3. 補充ref其它Api

代碼分享:

<template>
    <div>
        <h3>{{info1}}</h3>
        <h3>{{info2}}</h3>

        <h3><button @click="Edit1">修改1</button></h3>
        <h3><button @click="Edit2">修改2</button></h3>
        <h3><button @click="Edit3">修改3</button></h3>
    </div>
</template>

<script>
    import { ref, shallowRef, triggerRef } from 'vue';

    export default {
        setup(props, context) {
            // 1. ref響應式對象
            var info1 = ref({ age1: 10 });        
            // 2. 淺層的ref對象
            var info2 = shallowRef({ age2: 20 });
            
            // 默認的ref支持響應式
            var Edit1 = () => {
                info1.value.age1++;    
            }
            // 下面的修改不支持響應式
            var Edit2 = () => {            
                info2.value.age2++;    
            }
            // 下面的修改支持響應式
            var Edit3= () => {
                info2.value.age2++;
                // 手動觸發和 shallowRef 相關聯的副作用
                triggerRef(info2);        
            }
            
            return {
                info1,
                info2,
                Edit1,
                Edit2,
                Edit3
            }
        }
    }
</script>
View Code

 

 

五. readonly

1. 背景

 我們通過reactive或者ref可以獲取到一個響應式的對象,但是某些情況下,我們傳入給其他地方(組件)的這個響應式對象希望在另外一個地方(組件)被使用,但是不能被修改,這個時候如何防止這種情況的出現呢?

(1). Vue3為我們提供了readonly的方法;

(2). readonly會返回原生對象的只讀代理(也就是它依然是一個Proxy,這是一個proxy的set方法被劫持,並且不能對其進行修改);

2. 使用規則

 在開發中常見的readonly方法會傳入三個類型的參數:普通對象、reactive返回的對象、ref的對象

在readonly的使用過程中,有如下規則:

(1). readonly返回的對象都是不允許修改的;

(2). 但是經過readonly處理的原來的對象是允許被修改的;

 A. 比如 const info = readonly(obj),info對象是不允許被修改的;

   B. 當obj被修改時,readonly返回的info對象也會被修改;

   C. 但是我們不能去修改readonly返回的對象info;

(3). 其實本質上就是readonly返回的對象的setter方法被劫持了而已;

核心代碼分享:

setup() {
            //1. 普通對象
            const info1 = { name: 'ypf' };
            const readonlyInfo1 = readonly(info1);
            const EditContent1 = () => {
                readonlyInfo1.name = 'ypf2';
                console.log(readonlyInfo1);
            };

            //2.reactive對象
            const info2 = reactive({
                name: 'ypf'
            });
            const readonlyInfo2 = readonly(info2);
            const EditContent2 = () => {
                readonlyInfo2.name = 'ypf2';
                console.log(readonlyInfo2);
            };

            //3.ref對象
            const info3 = ref('ypf');
            const readonlyInfo3 = readonly(info3);
            const EditContent3 = () => {
                readonlyInfo3.value = 'ypf2';
                console.log(readonlyInfo3);
            };

            return {
                EditContent1,
                EditContent2,
                EditContent3
            } 
}

運行結果:

3. 應用場景

 在我們傳遞給其他組件數據時,往往希望其他組件僅僅使用我們傳遞的內容,但是不允許它們修改傳遞的內容,就可以使用readonly了。

 如下:向home組件中傳遞了1個readonlyInfo對象,該對象只能被使用,不能被修改。

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM