一. 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'); } } }
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')
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>
父組件

<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>
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>
父組件

<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>
剖析:
這里點擊按鈕,頁面上顯示的值沒有響應式的增加,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>
五. 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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。