Webフロントエンドmvcライブラリ実装


Webフロントエンドmvcライブラリ実装
前言
フロントエンドアプリケーションが複雑化するにつれて、angular、vueのmvvmフレームワーク、virtual domベースのreactなどのフロントエンドライブラリが各社の第一選択となっている.当初最も流行していたトップ兄貴backboneに代表されるmvcライブラリは基本的に歴史の舞台を脱退した.
今ではmvvm/reactがいい、backboneが悪いと言われている時代です.筆者は他人の文章を読んで、見る時いつも少し道理があるような気がして、見終わった後に耳元の風のように左耳が入って、右耳が出ます.
so,痛恨の後,筆者は小さな目標を定め,簡易版backboneライブラリを実現した.他のタイプのライブラリの違いを設計、実装の観点から比較します.
OK、くだらないことは言わないで、料理を出します.
構想を整理する.
MVCはフロントエンドアプリケーションをModel,View,Controlの3つのモジュールとして抽象化する.Viewはユーザービューで、ブラウザイベントでユーザー入力を受け付けます.Modelはデータモデルで、バックエンドといつでも同期できます.Controlは,View配信を具体的に実現するイベントであり,Modelのデータを計算して変更する.
UIはテンプレート+データとして抽象化され、ユーザーがブラウザから提供される様々なイベントをトリガし、インタラクションが絶えず行われるにつれて、ControlはViewコマンドを受け入れてModelのデータを変更し、ViewはModelの変更に応答し、最終的にユーザーの前に現れる.
フローチャート:
モジュール分割
本論文の構想はbackboneから来ており,結合したバックエンド操作を破棄した.初期MVCはControlを厳密に区分していなかったが、データの変更計算はそれほど複雑ではなかったのか、Control機能はViewのイベント内で完了した.つまりViewモジュールにはControlの機能が結合されている.
しかし近年fluxのaction,storeの出現,View呼び出しaction,具体的なデータ変化計算はstore内部で実現され,Control機能をView内部から抽象化しているといえる.
Eventモジュール
オブジェクトにイベントの処理とコールバックを提供し、viewがmodelの変化を購読した場合、modelが変化した後にviewに通知するように、内部で観察者(購読者)モードを実現します.
基本的な方法.
  • on関数event名によりobjectにcallback関数をバインドし,コールバック関数を配列に格納する.
  • off関数objectにバインドされたcallback関数
  • を削除
  • event名で指定callbackを削除します.例えばobject.off("change", onChange)
  • event名ですべてのcallbackを削除します.例えばobject.off("change")

  • 指定したcallbackをすべて削除します.例えばobject.off(null, onChange);
  • 指定contextのcallbackをすべて削除します.例えばobject.off(null, null, context);
  • objectのすべてのcallbackをクリアします.例えばobject.off()
  • trigger関数はevent名を介してobjectに対応する配列を見つけ、すべての配列内のコールバック関数をトリガする.

  • 注意事項
    そのすべての方法はon(name,callback),on('name 1 name 2 name 3',callback),on({name 1:callback 1,name 2:callback 2})のようなものをサポートすべきである.
    この場合,内部共通メソッドを抽象化することができる.再帰的には、on({name 1:callback 1,name 2:callback 2})タイプの和on('name 1 name 2 name 3',callback)タイプが、最終的に最も基本的なon(name,callback)タイプに変換される.コアコードは次のとおりです.
    this.eventsApi = function (iteratee, name, callback, context) {
        let event;
        if (name && typeof name === 'object') {
          Object.keys(name).forEach(key=> {
            event = this.eventsApi(key, name[key], context);
          })
        } else if (SEPARATE.test(name)) {
          var keys = name.split(SEPARATE);
          keys.forEach(key=> {
            event = iteratee.call(this,key, name[key], context);
          });
        } else {
          event = iteratee.call(this,name, callback, context);
        }
        return event;
    };
    

    Viewモジュール
  • 無状態で、インスタンス化時に複数のモデルインスタンスに対応し、これらのモデルの変化を観察者として観察し、これらのモデルデータに指定されたテンプレートレンダリングdomを加えてUIを表示することができる.
  • 破棄時にすべてのモデルの観察を抹消し、関連モデルとの関連を取り消します.
  • がインスタンス化するときにイベント依頼によりブラウザイベント
  • を登録する.
    インプリメンテーション
  • _EnsureElement、Viewにdom小包があることを確認してください.elこのdomが存在しない場合、id,className,tagNameによってdomが作成され、thisに割り当てられます.el.
  • listenToは、modelをviewインスタンスに関連付け、関連modelを収集し、listenTo配列内に格納し、内部実装はmodelを呼び出すon関数
  • である.
  • stopListening,view破棄前に呼び出し,listenTo配列により関連モデルを見つけ,viewとこれらのモデルとの間の観察者関係をキャンセルした.
  • $domの検索をthis.$el下
  • delegateEvents,イベント依頼,{'click#toggle-all':'choose'}を例にとると,this.elサブノードのidはtoggle-allのdom登録clickイベントchoose関数に等しい.コアコードは次のとおりです:
  • delegateEvents: function (events) {
       var $el = this.$el;
       Object.keys(events).forEach(item=> {
         var arr = item.split(' ');
         if (arr.length === 2) {
           var event = arr[0];
           var dom = arr[1];
           $el.on(event + '.delegateEvents' + this.$id, dom, this[events[item]].bind(this));
         }
       })
     },
    
  • undelegateEvents、delegateEventsで登録されているdomイベント
  • をログアウト
    Modelモジュール
    ModelはbackboneでobjectタイプのModelとarrayタイプのCollectionとして抽象化されている
  • はアプリケーションの状態を担いでおり、いつでもバックエンドと同期を保つことができる.
  • 内部ではデータの変化に対する傍受が実現され、変化が発生すると観察者Viewに変化が通知される.

  • Model
    データの変化を傍受し、modelの変更を行い、削除した後に対応するtrigger関数を呼び出し、modelの変化のviewを購読したことを通知します.
  • set関数は、modelデータを変更し、changeイベント
  • をトリガする.
    set: function (obj) {
        this._changing = true;
        this.changed = obj;
        this._previousAttributes = Object.assign({}, this.attributes);
        this.attributes = Object.assign({}, this.attributes, obj);
        const keys = [];
        Object.keys(obj).forEach(key=> {
          keys.push(key);
          this.trigger('change:' + key, this);
        }, this);
    
        if (keys.length > 0) {
          this.trigger('change', this);
        }
        this._changing = false;
     },
    
  • destroy関数はdestroyイベント
  • をトリガする
    destroy: function () {
       this.stopListening();
       this.trigger('destroy', this);
    },
    

    Collection
    配列タイプmodelsのpush,unshift,pop,shift,remove,resetなどの機能を提供します.push,unshift実際にadd関数,pop,shift実際にremove関数を呼び出す.
  • add関数は、任意のインデックス挿入指定配列をサポートし、addイベントをトリガーします.コアのコードは次のとおりです:
  •     export const splice = (array, insert, at)=> {
          at = Math.max(0, Math.min(array.length, at));
          let len = insert.length;
          let tail = [];
          for (let i = at; i < array.length; i++) {
            tail.push(array[i]);
          }
          for (let i = 0; i < tail.length; i++) {
            array[i + at + len] = tail[i];
          }
          for (let i = 0; i < len; i++) {
            array[i + at] = insert[i];
          }
          return array;
        };
    
  • remove関数は、指定したmodelを削除し、updateイベントをトリガーすることをサポートします.
  • _addReferenceは、addメソッドを呼び出してモデルを追加すると、そのモデルとcollectionとの関連付けをオブザーバーモードで増加させることで、モデルの変化をcollectionに通知する.コアコードは次のとおりです:
  •     _addReference: function (model) {
          model.on('all',this._onModelEvent,this);
         }
  • _removeReference、removeを呼び出し、resetがmodelを削除すると、そのmodelがcollectionに関連付けられていないことをキャンセルします.コアコードは次のとおりです:
  •     _removeReference: function(model) {
           if (this === model.collection) delete model.collection;
           model.off('all', this._onModelEvent, this);
        }
    

    extend
    生産環境では、元のView、Modelクラスの機能を維持しながらビジネスを展開する必要があります.この場合、クラスの継承に使用する必要があります.
    es 6はextend継承をサポートしていますが、こちらは手書きです.構想は構造関数を返し、その関数の原型は新しいインスタンスオブジェクトpropsであり、propsの原型オブジェクトは親関数の原型である(少し拗ねて、自分でコードを見て理解する).コアコードは次のとおりです.
    export const extend = function (props) {
      var parent = this;
      var child = function () {
        parent.apply(this, arguments);
      };
      child.prototype = Object.assign(Object.create(parent.prototype), props, { constructor: child });
      return child;
    };

    todomvc効果図
    ソースコード
    Webフロントエンドmvc実装
    セクション
    文章全体は基本的に次の2点をめぐっている.
  • view-model,collection-modelのオブザーバーモードの実装展開,期間view,modelの破棄は関連オブジェクトの関係を取り消し,view破棄時に関連するmodelのコールバック関数を抹消する.
  • はデータの変化を傍受し、modelの変化後にtrigger('change')
  • をトリガするなどの応答を観察者に通知する.
    はい、文章はここまで书いて、各位の见官にお礼を言って、以上も纯个人の観点で、问题があれば各位のwebフロントエンドmvcの设计の指导を歓迎します.