前端框架中 「類mixin」 模式的思考

「類 mixin」 指的是 Vue 中的 mixin,Regular 中的 implement

使用 Mixin 的目的

首先咱們須要知道爲何會有 mixin 的存在?

  1. 爲了擴展對象(組件)的功能javascript

    擴展對象功能的需求是很容易的理解的。好比業務開發時會碰到跨模塊傳遞消息的需求,經常使用的方法是使用 「發佈-訂閱模式」 來建立一個全局的EventEmitter。不使用 mixin 時的使用方法以下html

    // global/eventEmitter
    
    class EventEmitter {
        on(eventName, handle) {
            
        }
        
        emit(eventName) {
            
        }
    }
    
    
    // ComponentA
    import EventEmitter from 'global/eventEmitter'; // 全局的 EventEmitter 對象
    
    class ComponentA {
        constructor() {
            EventEmitter.on('event', () => {});
        }
    }
    
    // ComponentB
    import EventEmitter from 'global/eventEmitter';
    
    class ComponentB {
        constructor() {
            EventEmitter.emit('event');
        }
    }

    咱們須要在不一樣的組件中引入 EventEmitter 來使用它前端

    若是使用 mixin 的話,咱們能夠這樣作java

    // eventEmitter mixin
    import EventEmitter from 'global/eventEmitter'; // 全局的 EventEmitter 對象
    
    const eventEmitterMixin = {
        on: (eventName, handle) => {EventEmitter.on(eventName, handle)},
        fire: () => {EventEmitter.emit(eventName)}
    }
    
    // Root Component
    Component.mixin(eventEmitterMixin);
    
    // ComponentA
    class ComponentA {
        constructor() {
            this.on('event', () => {});
        }
    }
    
    // ComponentB
    class ComponentB {
        constructor() {
            this.emit('event');
        }
    }

    這樣經過mixin來擴展了組件的功能,是每一個組件均可以方便的使用 EventEmitter 的功能react

  2. 爲了複用代碼

    軟件開發中的 DRY 原則仍是有必要遵照的。過多的重複代碼會致使維護上的麻煩,經過 mixin ,咱們能夠在不一樣的對象上使用同一份代碼來完成相同的功能,減輕咱們維護的壓力。git

複用 VS 擴展

其實這兩個沒有可比性,但在咱們決定是否須要將一個對象經過 mixin 的方式混入到其餘對象時就應該考慮這個問題。github

原則上 複用 > 擴展數據庫

若是一個 mixin 只是爲了擴展單個對象的功能,而擴展的功能並不能複用到其餘的對象時,就不該該使用 mixin,而是直接寫在那個對象上更好。設計模式

若是擴展的功能能夠被複用的話,那麼能夠考慮使用 mixin數據結構

mixin 的缺點

mixins-considered-harmful 這篇文章已經列舉了一些問題,我簡單的列舉下
  1. mixin 會引入隱性的依賴關係
  2. mixin 會致使命名衝突
  3. mixin 會增長項目的複雜性

如何正確的使用 Mixin

雖然 mixin 有一些缺點,但正確的使用仍是能夠方便咱們的開發

沒有不合理的設計模式,只有不合理的使用設計模式

首先咱們須要解決混入的 mixin 可能會形成隱性的依賴關係,而造成這種依賴關係多半是 mixin 中的擴展功能依賴了被擴展對象想內部數據,例如:

// 一個數據庫查看組件

class DatabaseView {
    state = {
        database: [{name: 'db1', id: 1, tables: [{name: 'table1', id: 1}]}]
    }
}

DatabaseView.mixin(viewTabelDetails);

// 把查看錶詳情彈窗做爲 mixin 混入
const mixin = {
    viewTabelDetails: (dbId, tableId) => {
        const db = this.state.database.filter(db => db.id === dbId)[0];
        
        if (db) {
            const table = db.tables.filter(table => table.id === tableId)[0];
               
            if (table) {
                Model.show(table);
            }
        }
    }
}

上面的例子中,mixin 依賴的 DarabaseView 中的 database 數據。這就致使了若是頁面中其餘的組件也須要一個查看錶詳情彈窗的功能,那麼這個組件也必須有相似的 database 數據,造成了一個隱性的依賴關係。

避免的方式就是 mixin 對象中的功能不要與被擴展對象發生依賴,而在組件開發中這個依賴多半就是使用了被擴展對象的 state 產生的

至於命名衝突和增長項目的複雜性能夠經過其餘的方式解決,相比於上面的問題還算簡單

總結

  1. mixin 只是用來擴展對象功能的,並且這個擴展功能是能夠被複用的,不然你應該直接寫在對象裏面
  2. 擴展功能的同時,mixin提供的函數不該該依賴被擴展對象的內部數據。由於若是依賴的被擴展對象的內部數據,會使這個 mixin 只能被包含特定數據對象的對象複用,影響 mixin 的複用

我的主觀觀點

其實我以爲在前端的開發中,咱們應該避免使用 mixin 去擴展組件的功能

例如咱們徹底可使用函數調用(顯性的)去調用 mixin 對象中的方法,而不是一股腦的將 mixin 對象混合到組件中

每次修改使用了 implement 組件都會特別痛苦(這樣導入的方法 WebStorm 根本不識別),極大了增長維護的工做量。同時因爲有的 mixin 對象依賴的被擴展對象的內部數據,致使想複用的話還得有相同的數據結構(那還複用個錘子啊)

因此,不要用 mixin !!!

更多文章能夠查看本人的博客 https://lleohao.github.io
相關文章
相關標籤/搜索