blog

Vueのレスポンシブ原則をより深く理解する - 手書きミニVue

勉強を始めて2ヶ月以上が経ちました。結局のところ、普段は授業中に眠くなり、最初から諦めて勉強している私のような人間が、勉強を続けられ、なおかつブログを書きたい衝動に駆られる魔法とは何なのでしょうか。ま...

Oct 7, 2020 · 10 min. read
シェア

記事コンテンツ出力元:Lagoon Education - Big Front End High Salary Boot Camp

収穫

私の知らないところで、それは継続的な学習の2ヶ月以上でした。最後に、私は通常、クラスで眠くなる魔法は何ですか、学習は最初から人をあきらめることであり、常に学習に主張することができ、ブログを書きたい衝動がありますか?私は冒頭で、それはいくつかの経験を学ぶ"Lahuo教育 - ビッグフロントエンド高給与ブートキャンプ"に参加する時間のこの期間についていくつかの単語を言うことは非常に必要だと思います:

まず、コースですが、正直なところ、このコースのシラバスを見た瞬間に、これは信頼できると思いました。コースは全部で8つのモジュールから構成されており、フロントエンドエンジニアに必要なスキルはほぼ網羅されています。コースの構成は非常に合理的で、内容も浅いものから深いものへと段階的で、私のようなフロントエンドエンジニアに非常に適しています。目から鱗と言いますか、これは間違いないコースだと思い、始めることにしました。2ヶ月以上の学習で、モジュール3を学びましたが、本当に基礎知識、技術知識、作業方法からかどうか、質的に改善されていることがわかりました。

良いコースは何千とありますが、最後まで続けなければ、どれも意味がありません。私が学び続けるモチベーションは、コースだけでなく、先生や生徒たちにもあります。私が最も深く感じ、最も感動したのは、もちろんZce(王磊)先生です。王さんは、彼の鮮やかなコースウェアのデモンストレーションと丁寧なナレーションで、本当にクラス、退屈な知識のポイントを生きていると言うことができ、本当に非常に困難になりたくないです。たとえば、フロントエンドエンジニアリングコースを取る、通常はとても複雑なものに見える、王さんの授業を聞いた後、今私は、実際のプロジェクトの開発にこれらのツールをplop、gulp、webpackことができ、さらには、チームの開発効率を高めるために、独自の足場を手書きのヨーマンを使用しています。興味深い講義に加えて、最も重要なことはまだ責任感、教師のための責任感、説教、すべてのライブ授業は、王氏は、終了する前に、夜の12時まで話すことです理解するために与えていない、停止しないでください。また、彼の見に行くことができ、そこから我々は多くのことを学ぶことができます。もちろん、ミスター王に加えて、また、深夜の "ナイフ "は、クラスの先生を学ぶために促されている - 先生雪、顔の家庭教師 - 先生熊熊、"美しい "家庭教師 - 先生Xiaobeiだけでなく、毎日一緒に水のグループ、ディスカッション、ディベートの学生と、本当に、学ぶために一緒に優秀な人々!優秀な方々から学ぶ喜びを学びました。

ボディ

Vueの手書きでは、データ駆動の概念、パブリッシュ/サブスクライブのパターン、オブザーバのパターンという3つの必須知識を最初にカバーする必要があります。

データ駆動型

コアコンセプト

  • データレスポンシブ
    • 通常のJavaScriptオブジェクトをデータモデルとして使用することで、データが変更されると自動的にビューが更新されるため、面倒なドム操作を回避でき、開発効率が向上します。
  • 双方向バインディング
    • データが変わればビューも変わり、ビューが変わればデータも変わります。
    • 具体的な表現方法として、v-model を使って双方向バインディングを作成することができます。
  • データドリブン
    • 開発プロセスでは、データをどのようにビューにレンダリングするかではなく、データそのものに焦点を当てる必要があります。

コア・プリンシプル

  • Vue2.x
    • Object.definePropertyをベースに、データを乗っ取り、アプリケーション起動時にデータプロパティのゲッターとセッターを設定し、データが変更されると自動的にビューを更新します。
const data = { msg: 'Hello' } const vm = {} Object.defineProperty(vm, 'msg', { //データをリスニングする get(key) { console.log('data get') return data.msg }, //データ割り当てのリスニング set(val) { //値が同じかどうかを判断し、同じ場合は何もしない。 if (data.msg === val) return //同じでなければ値を代入し、ドムを変更する data.msg = val; document.getElementById('app').textContent = val; } }) //データの変更をトリガーし、domを更新する function textInput(e) { vm.msg = e.value; }
  • Vue3.x
    • レスポンシブなデータ処理を可能にするProxyプロキシオブジェクトの使用
const data = { msg: "", }; const dataProxy = new Proxy(data, { get(target, key) { return target[key]; }, set(target, key, value) { target[key] = value; if (key == "msg") { document.getElementById("app").textContent = value; } else { console.log(key + ":changed"); } }, }); function textInput(target) { dataProxy.msg = target.value; }

パブリッシュ/サブスクライブ、オブザーバーパターン

発行/購読パターン

  • コンセプト
    • 通常のJavaScriptオブジェクトを「シグナルセンター」とし、登録されたすべてのイベントを記録します。
    • on関数でイベントを登録し、対応するイベント配列に格納します。
    • emit関数でイベントをトリガーします。
  • 実装
class EventEmiter { constructor() { this.subs = Object.create(null); } // イベントを登録する $on(eventType, eventHandler) { this.subs[eventType] = this.subs[eventType] || []; this.subs[eventType].push(eventHandler); } // イベントをトリガーする $emit(eventType, datas) { this.subs[eventType] && this.subs[eventType].forEach(handler => { handler(datas); }) } } // const em = new EventEmiter(); //イベントを登録する em.$on('input', value => { document.getElementById('app').textContent = value; }) //イベントをトリガーする function onTextInput(target) { em.$emit('input', target.value) }

Observerパターン

  • 概念
    • オブザーバー - watcher
      • update():イベントが発生したときに行う特定の処理
    • ターゲット - Dep
      • subs 配列: すべてのオブザーバを保存します
      • addSub()オブザーバーの追加
      • notify(): イベントが発生したら、すべてのオブザーバの update メソッドをコールします。
  • の実装は
//Depクラス、オブザーバーの追加、オブザーバーの通知 class Dep { constructor() { this.subs = [] } add(sub) { if (sub && sub.update && typeof sub.update == 'function') { this.subs.push(sub) } } notify(datas) { this.subs.forEach(sub => { sub.update(datas) }) } } //オブザーバー、コールバックパラメーターを受け取り、時間がトリガーされたときにコールバック関数を呼び出す class Watcher { constructor(callback) { this.callback = callback; } update(datas) { typeof this.callback == 'function' && this.callback(datas); } } //パブリッシャーをインスタンス化する const dep = new Dep(); //オブザーバーのインスタンス化とコールバックの登録 const watcher = new Watcher(onInput); //パブリッシャーにオブザーバーを追加する dep.add(watcher); //イベントはパブリッシャーによってすべてのオブザーバーに通知される。 function onTextInput(target) { dep.notify(target.value) } //コールバック関数 function onInput(datas) { document.getElementById('app').textContent = datas; }

2つの違い

  • 例えば、イベントがトリガーされると、Dep はオブザーバのメソッドを呼び出すので、オブザーバパターンのサブスクライバとパブリッシャの間には依存関係があります。
  • パブリッシュ/サブスクライブモデルは、統一されたディスパッチセンターによって呼び出されるため、パブリッシャーとサブスクライバーはお互いの存在を知る必要がありません。

Vueレスポンシブ・プリンシプルのシミュレーション

- プロセスの分析

  • Vueの基本構造は
const vueInstance = new Vue({ el: "#app", router, render: h => h(App) }).$mount("#app");
  • Vueインスタンスを表示し、そのプロパティを観察します。

  • 全体構造

  • ビュー
  • オブザーバ
    • データオブジェクトのすべてのプロパティをリッスンすることができ、変更があれば最新の値を取得し、Depを通じて通知を送信できます。

a.

  • 初期化パラメータの受け取りを担当
  • Observer を呼び出して、データ内のすべての属性の変更をリッスンします。
  • コンパイラを呼び出して実行/補間式を解析

b. Vue

  • 属性
    • options - コンストラクタに渡される引数。
    • el - マウントされる要素。
    • data - データ。
  • $el            - マウントされた要素
  • 気付く
/** * 属性 * - $elマウントされたdomオブジェクト * - $data: * - $options: メソッドに渡される属性は * * - _proxyData データをゲッター/セッター形式に変換する */ class Vue { constructor(options) { this.$options = options; this.$data = options.data || Object.create(null); this.$el = typeof options.el === "string" ? document.querySelector(options.el) : options.el; this._proxyData(this.$data); // データの変更を監視し、ビューをレンダリングする new Observer(this.$data); } // データをvueにプロキシし、レスポンシブにする。データをレスポンシブにするには、this を渡す。.xxxへのアクセスとコピー _proxyData(data) { Reflect.ownKeys(data).forEach((key) => { Reflect.defineProperty(this, key, { enumerable: true, configurable: true, get() { return data[key]; }, set(newValue) { if (newValue == data[key]) { return; } data[key] = newValue; }, }); }); } }

c. Observer

  • _proxyData()    - データのゲッター/セッターへの変換
  • クラス図
    • メソッド
      • defineReactive(data, key, value) - データをゲッター/セッターに変換します。
import Dep from './Dep.js' class Observer { constructor(data) { this.walk(data); } walk(data) { // dataがNULLの場合、あるいはdataがオブジェクトでない場合は if (!data || typeof data !== "object") { return; } Reflect.ownKeys(data).forEach((key) => { this.defineReactive(data, key, data[key]); }); } defineReactive(data, key, value) { const that = this; // 各データにオブザーバーを追加する const dep = new Dep(); // プロパティ値がオブジェクトであるかどうかを再帰的に検出し、オブジェクトであれば、そのオブジェクトをレスポンシブに変換する。 this.walk(value); Reflect.defineProperty(data, key, { enumerable: true, configurable: true, get() { // Wathcerをインスタンス化すると、対応する値がフェッチされてキャッシュされ、getがトリガーされる。 // ウォッチャー・インスタンスを取得し、depに追加する Dep.target && dep.addSub(Dep.target); return value; }, set(newValue) { if (newValue == value) { return; } // これは、値のスコープを拡張するクロージャを作成する value = newValue; // プロパティに新しい値が与えられると、そのプロパティがオブジェクトであるかどうかがチェックされ、オブジェクトであれば、そのプロパティはレスポンシブに変換される。 that.walk(newValue); // データが変更され、通知が送信され、ウォッチャーのpudateメソッドがトリガーされる dep.notify(); }, }); } } export default Observer

d. Compiler

  • 機能
    • defineReactive(data, key, value)    - データのゲッター/セッターへの変換
    • ページの最初のレンダリングを担当
    • データ変更後のビューの再レンダリング
  • 属性
    • el -app 要素
    • 属性
      • el -app
      • vm -vue
  • メソッド
    • vm -vue
    • compileElement(node) - 要素をコンパイルします。
    • compile(el) -コンパイル・エントリ
    • compileElement(node) -要素のコンパイル
    • compileText(node) テキストのコンパイル
    • isDirective(attrName) -
/** . 属性 - el-app要素 - vm -vueの例 - メソッド - コンパイル - コンパイル・エントリー - compileElement - 要素をコンパイルする - コンパイルテキスト - isDirective - isTextNode - isElementNode */ import Watcher from "./Watcher.js"; class Compiler { constructor(vm) { this.vm = vm; this.el = vm.$el; this.compile(this.el); } compile(el) { if (!el) return; const nodes = el.childNodes; // Array.from(nodes).forEach((node) => { if (this.isTextNode(node)) { this.compileText(node); } else if (this.isElementNode(node)) { this.compileElement(node); } if (node.childNodes && node.childNodes.length) { this.compile(node); } }); } update(node, value, attrName, key) { const updateFn = this[`${attrName}Updater`]; updateFn && updateFn.call(this, node, value, key); } textUpdater(node, value, key) { node.textContent = value; } modelUpdater(node, value, key) { node.value = value; node.addEventListener("input", (e) => { this.vm[key] = node.value; }); } compileElement(node) { Array.from(node.attributes).forEach((attr) => { if (this.isDirective(attr.name)) { const attrName = attr.name.substr(2); const key = attr.value; const value = this.vm[key]; this.update(node, value, attrName, key); // データが更新された後、weather()を介してビューが更新される。 new Watcher(this.vm, key, (newValue) => { this.update(node, newValue, attrName, key); }); } }); } compileText(node) { /** * . 改行なしの任意の1文字を表す * + 前の文字と同じものが複数マッチすることを示す * を使用する。非貪欲モードを表し、ルックアップをできるだけ早く終了させる。 * */ const reg = /\{\{(.+?)\}\}/; var param = node.textContent; if (reg.test(param)) { // $1最初の const key = RegExp.$1.trim(); node.textContent = param.replace(reg, this.vm[key]); // テンプレートをコンパイルする際に、ウォッチャーのインスタンスを作成し、Depに内部的にマウントする。 new Watcher(this.vm, key, (newValue) => { // コールバック関数でビューを更新する node.textContent = newValue; }); } } isDirective(attrName) { return attrName && attrName.startsWith("v-"); } isTextNode(node) { return node && node.nodeType === 3; } isElementNode(node) { return node && node.nodeType === 1; } } export default Compiler;

Dep

  • isTextNode(node) -
  • オブザーバーの収集
  • メソッド
    • target:Watcher
    • notify() - オブザーバの更新をトリガします。
/** * オブザーバー・クラス */ export default class Dep { constructor() { this.subs = []; } // オブザーバーを追加する addSub(sub) { if (sub && sub.update && typeof sub.update === "function") { this.subs.push(sub); } } // 通知を送信する notify() { this.subs.forEach((sub) => { sub.update(); }); } }

Watcher

  • addSub(sub)    -オブザーバーの追加
  • 属性
    • vm -vue インスタンス
    • key - 監視するキー.
    • vm    -vue
  • key    -オブザベーションのキー
/** * 属性 * vm -vue * key -観測された要素のキー * cb -に変更が加えられたときに呼び出されるコールバックを登録する。 */ import Dep from "./Dep.js"; export default class Watcher { constructor(vm, key, cb) { this.vm = vm; this.key = key; this.cb = cb; // oldValueキャッシュ、Depへのウォッチャー・インスタンスのマウント Dep.target = this; // 古い値をキャッシュする this.oldValue = vm[key]; // get値の後、Depのインスタンスをクリアする Dep.target = null; } update() { // update呼び出し時に新しい値を取得する const newValue = this.vm[this.key]; // 比較し、同じなら更新しない if (this.oldValue === newValue) { return; } this.cb(newValue); } }
Read next

css BEMの命名

Webプロジェクト、BEM Portalでは、cssの命名を標準化する必要があります。BEM仕様を分析し、学ぶためにElement-UIのソースコードを読むことによって、BEM仕様のセマンティクスの使用は、より明確な読みやすく、理解しやすい、Bはブロックブロックを意味し、EはElemenを意味します。

Oct 6, 2020 · 1 min read