フレームワーク無しのクライアント側JavaScriptデータベース化


最近、私は純粋なJavaScriptの機能についてたくさん考えています.これは、過去数年間で大幅に進化してきた言語です.多くの一般的なライブラリ(モジュールローダなど)とフレームワーク(角度、vuj . jsと反応のような)は、オリジナル、時代遅れの実装に存在した欠陥とギャップに対処するために作成されました.With ECMAScript 6 / 2015 私は、それらの制限の大部分が去ったと思っています.多くの重要な機能は、次のようなボックスから存在します.
  • サポートmodules 動的負荷
  • 経路を傍受し管理する能力
  • 内蔵DOM query mechanism それはjQueryの必要性を取り除く
  • ネイティブtemplate サポート
  • 再利用可能web components
  • 私は、過去に現代のウェブ開発の「3 Dの」について書きました:
    The Three D’s of Modern Web Development

    の歴史を学び、角度、反応のような現代のJavaScriptフレームワークを分解し、依存関係の注入、宣言構文、およびデータのバインドについて学ぶことによってVue.
    完全に最新のJavaScriptバージョンでネイティブにサポートされていない1つの機能は、Databindingです.しかし、どのようにハードを実装することですか?重いフレームワークを使用するためのあなたの唯一のモチベーションがDatabindingサポートであるならば、あなたは驚くかもしれません!私たちの袖をロールバックして試してみましょう.

    変化の観察


    必要なのは、変化を観察する能力です.これはObservable クラス.クラスには三つのことが必要です.
  • 値を追跡する
  • 変更を購読するリスナーを許可する
  • 値が突然変異するときにリスナを通知する
  • 以下に簡単な実装を示します:
    class Observable {
       constructor(value) {
          this._listeners = [];
          this._value = value;
       }
       notify() {
          this._listeners.forEach(listener => listener(this._value));
       }
       subscribe(listener) {
          this._listeners.push(listener);
       }
       get value() {
          return this._value;
       }
       set value(val) {
          if (val !== this._value) {
             this._value = val;
             this.notify();
          }
       }
    }
    
    
    このクラスは、組み込みクラスSUPORTTypeScript 必須)すべてうまく処理します.ここでは使用可能な新しいクラスの例を示します.
    const name = new Observable("Jeremy");
    name.subscribe((newVal) => console.log(`Name changed to ${newVal}`));
    name.value = "Doreen";
    // logs "Name changed to Doreen" to the console
    
    
    それは簡単でしたが、計算値はどうですか?たとえば、複数の入力に依存する出力プロパティを持つことができます.最初の名前と最後の名前を追跡する必要があると仮定します.どうやって動くの?

    計算された値


    JavaScriptの継承をサポートしているので、Observable 同様に計算された値を扱うクラス.このクラスは余分な作業をする必要があります.
  • 新しいプロパティを計算する関数を追跡する
  • 依存性を理解する.すなわち、計算されたプロパティは
  • 依存関係の変更を購読し、計算されたプロパティを再評価できます
  • このクラスは実装が少し簡単です.
    class Computed extends Observable {
       constructor(value, deps) {
          super(value());
          const listener = () => {
             this._value = value();
             this.notify();
          }
          deps.forEach(dep => dep.subscribe(listener));
       }
       get value() {
          return this._value;
       }
       set value(_) {
          throw "Cannot set computed property";
       }
    }
    
    
    これは関数と依存関係をとり、初期値をシードします.それは依存の変更をリッスンし、計算値を再評価します.最後に、それが読み込み専用(計算)であるので、例外を投げるためにセッターを上書きします.使用中です.
    const first = new Observable("Jeremy");
    const last = new Observable("Likness");
    const full = new Computed(
       () => `${first.value} ${last.value}`.trim(), 
       [first, last]);
    first.value = "Doreen";
    console.log(full.value);
    // logs "Doreen Likness" to the console
    
    
    今、我々はデータを追跡することができますが、HTML DOMはどうですか?

    双方向データ


    双方向データの場合は、値を変更してDOMプロパティを初期化し、その値が変更されたときに更新します.また、DOMがいつ更新されるかを検出する必要があるので、新しい値はデータに中継されます.組み込みのDOMイベントを使用すると、コードは、入力要素を持つ双方向データセットの設定になります.
    const bindValue = (input, observable) => {
       input.value = observable.value;
       observable.subscribe(
          () => input.value = observable.value);
       input.onkeyup = () => observable.value = input.value;
    }
    
    
    難しいようではないですか?入力要素を持つと仮定しますid 属性セットfirst このように配線できます.
    const first = new Observable("Jeremy");
    const firstInp = document.getElementById("first");
    bindValue(firstInp, first);
    
    
    これを他の値で繰り返すことができる.

    Tip: This is, of course, a simple example. You may have to convert values if you are expecting numeric inputs and write different handlers for elements like radio lists, but the general concept remains the same.


    「3 D」に戻ると、コードバックとDataBind宣言を最小限にすることができます.それを調べましょう.

    宣言型データ管理


    目標は、そのIDによって要素をロードしなければならず、代わりに単にオブザーバブルに直接バインドするのを避けることです.私はタスクの記述的属性を選びdata-bind . コンテキストのプロパティを指す値を持つ属性を宣言します.
    <label for="firstName">
       <div>First Name:</div>
       <input type="text" data-bind="first" id="firstName" />
    </label>
    
    針金を通すには既存を再利用できるdataBind 実装.まず、バインドするコンテキストを設定します.次に、コンテキストを設定し、バインドを適用します.
    const bindings = {};
    const app = () => {
       bindings.first = new Observable("Jeremy");
       bindings.last = new Observable("");
       bindings.full = new Computed(() =>
          `${bindings.first.value} ${bindings.last.value}`.trim(),
          [bindings.first, bindings.last]);
       applyBindings();
    };
    setTimeout(app, 0);
    
    
    The setTimeout 最初のレンダリングサイクル時間を完了します.さて、宣言を解析してバインドするコードを実装します.
    const applyBindings = () => {
       document.querySelectorAll("[data-bind]").forEach(elem => {
          const obs = bindings[elem.getAttribute("data-bind")];
          bindValue(elem, obs);
       });
    };
    
    
    このコードは、data-bind 属性を使用してコンテキスト上のオブザーバブルを参照するインデックスとして使用し、dataBind 操作.
    それです.もう終わりました.Click here 完全なコード例を開きます.

    側面ノート:評価文脈


    Databindingは常に観測可能な名前を指すように単純ではありません.多くの場合、式を評価したい場合があります.式が他の式を閉じたり、安全でない操作を行わないように、コンテキストを制約することができればよいでしょう.それも可能です.式を考えるa+b . 「文脈で」それを制限するいくつかの方法があります最初の、そして最も安全なのはeval 特定のコンテキストで.サンプルコード:
    const strToEval = "this.x = this.a + this.b";
    const context1 = { a: 1, b: 2 };
    const context2 = { a: 3, b: 5 };
    const showContext = ctx =>
       console.log(`x=${ctx.x}, a=${ctx.a}, b=${ctx.b}`);
    const evalInContext = (str, ctx) =>
       (function (js) { return eval(js); }).call(ctx, str);
    showContext(context1);
    // x=undefined, a=1, b=2
    showContext(context2);
    // x=undefined, a=3, b=5
    evalInContext(strToEval, context1);
    evalInContext(strToEval, context2);
    showContext(context1);
    // x=3, a=1, b=2
    showContext(context2);
    // x=8, a=3, b=5
    
    
    これにより文脈は変異するがいくつかの欠陥がある.使用規約this 厄介であり、多くの潜在的なセキュリティの悪用があります.ジャストアドwindow.location.href= ステートメントとポイントを得る.より安全な方法は、値を返す評価を許容するだけで、動的関数でそれらをラップすることです.次のコードは、ナビゲーションの副作用のないトリックを行います.
    const strToEval = "a + b; window.location.href='https://blog.jeremylikness.com/';";
    const context1 = { a: 1, b: 2 };
    const context2 = { a: 3, b: 5 };
    const evalInContext = (str, ctx) => 
       (new Function(`with(this) { return ${str} }`)).call(ctx);
    console.log(evalInContext(strToEval, context1));
    // 3
    console.log(evalInContext(strToEval, context2));
    // 8
    
    
    この小さなトリックでは、安全に特定のコンテキストで式を評価することができます.

    結論


    私はフレームワークに反対ではありません.私はいくつかの信じられないほど大規模なエンタープライズWebアプリケーションを構築しました.しかし、すべての問題を解決することができます“ゴールデンツール”としてフレームワークを見て、最新のネイティブの進歩とペースを維持することが重要です.フレームワークに頼ることは、セットアップ、構成、およびメンテナンス、リスク・セキュリティ脆弱性を通して頭上にあなた自身を露出させることを意味します、そして、多くの場合、大きなペイロードを展開します.あなたは、そのフレームワークのニュアンスに精通している才能を雇うか、それの上で彼らを訓練して、アップデートとペースを保つ必要があります.ネイティブコードを理解するだけで、ビルドプロセスを保存することができますし、“コードは、多くのコードなしで近代的なブラウザで”だけで動作するシナリオを有効にします.
    いつものように、私はあなたのフィードバック、思考、コメント、および質問を歓迎します.
    宜しく