第145篇:js設計模式註冊模式及相應實踐

养肥胖虎發表於2024-03-23

好傢伙,

0.索引

在阿里的低開專案中,使用這種形式去註冊元件,我不禁好奇,這到底是個什麼玩意

1.概念

在 JavaScript 中,註冊模式(Registry Pattern)是一種設計模式,它用於管理物件、函式或其他型別的例項,並提供一種機制來訪問它們。註冊模式通常用於將物件例項註冊到一箇中心登錄檔中,以便在需要時能夠輕鬆地檢索和使用這些例項。

註冊模式通常包含以下幾個核心元件:

  1. 登錄檔(Registry):登錄檔是一個儲存物件例項的集合,通常以鍵值對的形式儲存。可以將物件例項註冊到登錄檔中,也可以從登錄檔中檢索物件例項。

  2. 註冊(Registration):註冊是指將物件例項新增到登錄檔中的過程。通常會將物件例項與一個唯一的識別符號(如字串)相關聯,以便在需要時能夠透過該識別符號檢索物件例項。

  3. 檢索(Retrieval):檢索是指從登錄檔中獲取物件例項的過程。透過提供相應的識別符號,可以從登錄檔中檢索相應的物件例項。

使用註冊模式的一個常見場景是在應用程式中管理和訪問多個服務、外掛或元件。透過將這些服務、外掛或元件註冊到一箇中心登錄檔中,可以在需要時輕鬆地獲取它們,而無需直接引用它們的實現細節。

2.實踐

然後我發現我的低開物料庫也能這麼幹,

具體怎麼做呢?
1.寫個登錄檔

2.寫個註冊方法

3.寫出相應的檢索方法

3.登錄檔

const componentList = [];

以陣列的形式存在,可以直接push

4.註冊方法

function createEditorConfig() {
  const componentList = [];
  return {
    componentList,
    componentMap,
    register: (component) => {
      componentList.push(component);
    }
  };
}

5.檢索

function createEditorConfig() {
  const componentList = [];
  const componentMap = {};
  console.log(componentList,componentMap)
  return {
    componentList,
    componentMap,
    register: (component) => {
      componentList.push(component);
      componentMap[component.key] = component;
    }
  };
}

export let registerConfig = createEditorConfig();
registerConfig.register({
  label: '文字',
  preview: () => '預覽文字',
  render: function(h) {
    return h('div', '渲染文字');
  },
  key: 'text'
});
registerConfig.register({
  label: '按鈕',
  preview: () => <ElButton>預覽按鈕</ElButton>,
  render: function(h) {
    return <el-button>渲染按鈕</el-button>;
  },
  key: 'button'
});
registerConfig.register({
  label: '輸入框',
  preview: () => <ElInput placeholder="預覽輸入框">預覽按鈕</ElInput>,
  render: function(h) {
    return <el-input placeholder="渲染輸入框">預覽輸入框</el-input>;
  },
  key: 'input'
});

這裡,我們為每個註冊的元件配置配置相應的key值(比如輸入框元件的key值是input)

componentMap 是一個物件,用於將元件的 key與元件物件進行對映。

當註冊一個新元件時,會將元件物件以 key-value 的形式儲存在 componentMap 物件中,其中 key 是元件的唯一識別符號,通常用於查詢特定元件。

透過維護一個元件對映,可以快速透過 key 查詢到對應的元件物件。

6.使用登錄檔

既然登錄檔已經做好了,那麼我們去將這個登錄檔中的元件渲染出來

<div class="material" v-for="(component, key) in registerConfig.componentMap" :key="component.key">
                <span>{{ key }}</span>
                <component :is="component"></component>
            </div>


components: {
        lcEditor, lcRender, test, Material,
        component: {
            functional: true,
            render(h, { data }) {
                return h(registerConfig.componentMap[data.key]);
            }
        }
    },

成功渲染

7.疑問:為什麼要將componentList,componentMap包在方法內部?

componentListcomponentMap 包在 createEditorConfig 方法內部的主要目的是為了封裝這兩個變數,並且透過閉包的方式建立了一個私有作用域,避免了這兩個變數被外部直接訪問和修改。

具體原因如下:

  1. 封裝資料: 將 componentListcomponentMap 包在方法內部,可以將它們視為 createEditorConfig 方法的私有屬性,外部無法直接訪問或修改這兩個變數,只能透過 register 方法來操作它們,從而保證了資料的封裝性。

  2. 避免全域性汙染: 將變數包在方法內部可以避免將這些變數暴露在全域性作用域中,減少了全域性變數的數量,避免了可能出現的命名衝突和全域性汙染。

  3. 提供了私有作用域: 透過閉包的方式,register 方法可以訪問到 componentListcomponentMap 變數,但外部無法直接訪問這兩個變數,確保了資料的私有性。

  4. 簡化呼叫方式: 將 componentListcomponentMap 封裝在方法內部,使得建立配置物件和註冊元件的操作更加簡潔明瞭,只需呼叫 createEditorConfig() 方法即可獲得一個包含 componentListcomponentMap 的配置物件,然後透過 register 方法註冊元件。

總的來說,將 componentListcomponentMap 包在方法內部是一種良好的程式設計實踐,有利於提高程式碼的可維護性、可讀性,並且能夠更好地控制資料的訪問許可權和作用域。

相關文章