🏗️

Maquette 個人的利用方針

2022/01/01に公開
1

Maquette 個人的利用方針

習作 UTX viewerにて利用した Maquetteの使用所感を残す. なお, これらは npm, React, Vue, jQuery を一切またはほとんど知らない人間が記述している ことに留意せよ. そして, 全ては主観的な偏見に基づいている.

基礎ページ構成

createProjectorで作った管理体にappendで登録する.

const vPage = maquette.createProjector();
const h = maquette.h;

/* h()による最小要素~ページ固有機能要素定義 */
// ...

const tMain = {
  frame: function(){return h('タグとセレクタ', {属性:}, []);}
};

document.addEventListener('DOMContentLoaded', function(){
  vPage.append(document.body, tMain.frame);
});

コンポーネント化構成

Component approachではrender関数を値に持つオブジェクトを返す関数を作ることが提案されている.

以下は拙作での利用箇所である. 上記提案ではkey属性に自身(this)を登録したりすることを提案されているが, 私のほうではidを指定する形にして, 個別指定の再利用ができるようにしていた.

なお, イベントの登録について, 古くはh('',{onclick:function(){}},[])が禁じられていたが現在はその制限が緩和されているらしい. しかし, 結局再描画のときの再評価が発生するともろもろもたつくため, 事前定義の登録がやはり良いようだ.

function orgLabeledCheckbox(id, labelText, changed = function(mark){return;}){
  function onChange(e){
    changed(document.getElementById(id).checked);
  }

  return {
    render: function(status){
      return h('label', {}, [
        h('input#' + id, {type:"checkbox", onchange: onChange, checked: status}, []),
        labelText
      ]);
    }
  };
}

コンポーネント化の指針

Atomciっぽい構成を目指して, 次のようにした.
単一, 複合を問わず ノードを返す関数であり, 移植可能.

  • atom :: 単一ノードでそのまま移植可能
  • mol :: 複合ノードでそのまま移植可能
  • org :: 単一, 複合を問わずノードを返す関数であり, 移植可能.
  • orga :: 単一, 複合を問わずノードを返す関数であり, ページ依存.
  • frame :: ページ一画面の1領域(ヘッダーとかフッター)を返す関数でvPage.appendされる

ユニークID割り振り

そもそものMaquette制約事項として, 同一なセレクタが列挙するようなケースではそれらのkey属性にユニークななにかを割り当てる必要がある. 公式ではthisを割り当てることが提案されている.

私は汎用関数として, ユニークID文字列の生成関数を作成してそれを設定するようにした. これにより, このIDを軸に再探索やDOM値取得がなせるようにしたつもりである. これはinput系の挙動でイベントが発生してくれなかったりしたことに由来する.

再描画

onclickonchangeなどで登録した関数に引っ掛けて, vPage.appendした関数が再度実行されるような形になる. 登録した関数の中で非同期的イベント登録(ファイル読み込み完了とか)を行うとそれは検出されない. そのときはvPage.scheduleRender()の実施で強制再描画を起こせる.

しかし, (いや, だからこそ) ノードの再呼び出しが重たい処理になってしまうとページの使用感としてえらい重たい. ノードを固定値のように扱えれば一番で, ノードの数が大きく変動するようなシーンは避けたほうが無難なようだ. 拙作では表示利用しないノードはCSS的なdisplay: noneで対処した.

ノード配列

Maquetteにはmaquette.createMapping(getSourceKey: function, createResult: function, updateResult: function)がある. ある配列を与えたときにこれで設定しておいた関数を適用できる. 拙作では用語集の設定表示と語の一覧表示に利用した. のべたにArrayのmapを利用するよりはわずかに効率的であるようだ.

// 配列利用前の事前定義.
var orgaGlossaryIgnoreMapping = maquette.createMapping(
  function getSourceKey(x){ return x.key}, // 配列要素のキー取得関数
  function createTarget(x){ // 配列要素に適用する関数. resultに適用後配列が格納される.
    function onClick(e){
      if(confirm("Deleting the " + x.info.id + ", really?")){
        shelf.remove(x.key);
        orgaRecordMapping.map(shelf.getRecord(appArgs.mem.searchWord));
        orgaGlossaryIgnoreMapping.map(shelf.getInfos());
      }
    }

    const ignore = orgLabeledCheckbox(x.key, (x.info.id).slice(0, 22) + " " + x.info.ver, function(mark){ shelf.glossary[shelf.yek[x.key]].ignore = mark;});
    const remove = mButtonWithAtom(onClick, aImg("img/delete.svg", "Remove glossary"));

    return {
      gIdx: x.key,
      render: function(status){
        return [ignore.render(status), remove];
      }
    };
  },
  function updateTarget(updatedSource, target){} // これだけ何をするものか不明.
);

// 配列登録(更新).
orgaGlossaryIgnoreMapping.map(shelf.getInfos());

// 利用(実際にはh()の子要素の位置)
orgaGlossaryIgnoreMapping.results.map(function(x){
                return h('li',{key:this}, [x.render(shelf.glossary[shelf.yek[x.gIdx]].ignore)]);
})

参考

Discussion