什么是Mixin(混入)
Mixin是一種思想,用來實現代碼高度可復用性,可以針對屬性復制實現代碼復用的想法進行一個擴展,就是混入(mixin)。混入並不是復制一個完整的對象,而是從多個對象中復制出任意的成員並將這些成員組合成一個新的對象。
1 const obj1 = {a:1,b:2,c:3,d:4,e:5};
2 const obj2 = {f:6,g:7,h:8,i:9,a:10};
3 let obj3 = {};
4 Object.assign(obj3,obj1,obj2);
5 console.log(obj3);
6 // 輸出結果 {"a":10,"b":2,"c":3,"d":4,"e":5,"f":6,"g":7,"h":8,"i":9};
上面是一個對象混入,把兩個對象,合並成一個對象,如果有相同屬性存在則后者會覆蓋原有的屬性值,如果存在方法名相同同樣會覆蓋,因為在對象內部是以key來區分的,並且每個key是唯一的。
為什么要使用混入
Mixin的優點
混入可以減少程序中重復的功能,增強函數的復用性。當一個應用程序可能需要在各種對象中共享行為時,我們可以通過Mixin中維持這種共享功能並專注於程序中真正不同的功能,輕松避免任何重復,使項目結構更加清晰更加易於維護。
Mixin的缺點
Mixin是一種很靈活的代碼復用方式,但把功能屬性和方法導入,如果文件過多,會導致屬性方法來源方面的不確定性,在大型系統中需要對項目文件的划分以及整體的掌控。
Vue Mixins 應用
官方解釋
混入 (mixins) 是一種分發 Vue 組件中可復用功能的非常靈活的方式。混入對象可以包含任意組件選項。當組件使用混入對象時,所有混入對象的選項將被混入該組件本身的選項。
mixins.js
export default { data: function () { return { message: 'hello', foo: 'abc' } } }
index.js
new Vue{ mixins: [mixin], data: function () { return { message: 'goodbye', bar: 'def' } }, created: function () { console.log(this.$data) // => { message: "goodbye", foo: "abc", bar: "def" } } }
在實際項目中可以根據業務領域把相同的業務場景按文件進行划分,比如好多頁面都需要插入/刪除/更改,這些方法都是相同的,可以單獨放在一個js文件里面去統一的管理。
再舉一個例子,比如有很多頁面里面都有表單,然而這些頁面內的表單都需要方法去校驗,每個頁面都寫一次的話,會導致大量的重復代碼的出現,而且不易於管理,維護,我們可以把所有的表單的校驗方法放在一個js文件里面導出去,哪些組件或者頁面需要進行格式校驗,使用mixin混入這些方法。即使是以后需要添加或者更改校驗方法就直接更改這個js文件就好了。
唯一一點很重要的就是,混入的方法會覆蓋掉之前頁面內部的同名屬性或方法。
官方文檔:Vue Mixins 文檔
小程序Mixins應用
小程序的Page並沒有給出混入的API以及解方案,如果想要實現混入,需要自己封裝Mixins方法,來實現對象方法以及屬性的混入,達到代碼的復用的目的。
Mixins實現
在小程序初始化使用App去創建每個頁面,通過Page可以獲取到創建頁面時所需要的屬性和方法,如果把Page看做是一個函數,然而在函數里面寫的方法或者屬性,其實都放在了一個對象里面,data也好方法也好其實都是這個對象里面唯一的屬性和方法,為頁面提供消費。
export let CreatePage = (obj) => { let {mixins} = obj; Reflect.deleteProperty(obj, 'mixins'); obj.data = obj.data || {}; mixins.forEach((el) => { merge(el,obj) }) return obj; } let merge = (el,obj) => { let elKeys = Object.keys(el); el.data = el.data || {}; elKeys.forEach((e) => { e === "data" && Object.assign(obj.data,el.data); (e !== "data") && (obj[e] = el[e]); }) }
上面代碼中創建了一個名為CreatePage的函數,函數接收了一個對象作為參數,然而這個對象就是我們在使用Page的時候傳入供頁面消費的對象,在對象內部添加了一個屬性Mixins,這個屬性是一個數組,里面的每一項都是需要混入到里面的屬性和方法。遍歷Mixins數組,獲取到其中的每一項,使用merge函數把里面的每一項都進行合並。由於data是一個對象,直接和並可能會導致只有合並后的data數據,合並之前的數據丟失,所以做了判讀如果遇到data就單獨去處理data的合並。其他的屬性或者方法直接合並到obj里面,最后把obj返回出去。
應用
index.js
// 引入mixin import {CreatePage} from "../../utils/mixin.js"; // 引入靜態數據 mixin1 import mixin1 from "../../mixins/public/mixin1.js"; Page(CreatePage({ mixins:[mixin1], data:{ one:"我是index里面的數據" }, a(){ console.log("我是a函數",this.data.two) }, onLoad(){ this.a(); this.b(); } }))
mixin1.js
module.exports = { data:{ two:"我是Mixin里面的數據" }, b(){ console.log("我是b函數",this.data.one) } }
通過這種方法可以把項目中業務重復的方法以及靜態數據放到mixins文件中,統一管理統一維護。一旦項目需求發生變化更改一個地方就可以了,沒有必要在成百行代碼中找到某行代碼,無疑是增加大量的查找以及修改的作業。
mixins 改進
let native = Page; Page = (obj) => { let {mixins = []} = obj; let merge = new Merge(); Reflect.deleteProperty(obj, "mixins"); let pageData = mixins.length<=0?obj:merge.start(obj,...mixins); native(pageData); } class Merge { constructor(){} start(rootObj,...objs){ let root = {}; objs.forEach((el) => { root = Merge.recursive(rootObj,el); }) return root; } static recursive = (rootObj,obj) => { for(let attr in obj){ if(rootObj[attr] === undefined){ rootObj[attr] = obj[attr]; } else if(Merge.isObject(obj[attr])){ Merge.recursive(rootObj[attr],obj[attr]) } else{ rootObj[attr] = obj[attr]; } } return rootObj; } static isObject = (obj) => { return Object.prototype.toString.call(obj).includes("Object"); } }
在第一種方法上做了進一步的改進,上面的代碼中首先存儲了一份Page函數,然后獲取到Page里面的參數進行深度拷貝處理,深度拷貝完畢之后,把拷貝后的對象return出去。把存儲的Page執行一下,最后把合並后的data傳進去就好了。
改進應用
index.js
// 引入靜態數據 mixin1 import mixin1 from "../../mixins/public/mixin1.js"; Page({ mixins:[mixin1], data:{ one:"我是index里面的數據" }, a(){ console.log("我是a函數",this.data.two) }, onLoad(){ this.a(); this.b(); } })
mixin1.js
module.exports = { data:{ two:"我是Mixin里面的數據" }, b(){ console.log("我是b函數",this.data.one) } }
改進之后無需再使用createPag函數,之前使用Page的時候,使用方法是一樣的,只是里面添加一個`mixins`屬性而已,用來混入那些公用的屬性或者方法。
優點
代碼改進后不僅僅只有Page可以使用呢mixins,`Component`也可以通過上面的方法,重新改寫一下`Component`在組件中使用mixins。
總結
隨然mixins模式,使代碼變得復用性很強,但是任何事物都有兩面性,如果只是為了應用而應用的話很可能會適得其反,沒有達到理想的效果。個人認為,強大的文檔有助於將與混入函數來源有關的困惑減至最低,但對於每一種模式,如果在實現期間多加注意,多做考慮,一定會應用的很順利的。