ホームページ ウェブフロントエンド jsチュートリアル JavaScript データ バインディングは、単純な MVVM library_javascript スキルを実装します。

JavaScript データ バインディングは、単純な MVVM library_javascript スキルを実装します。

May 16, 2016 pm 03:06 PM

推奨読書:

非常に単純な JS 双方向データ バインディングの実装

MVVM は Web フロントエンドの非常に人気のある開発モデルです。MVVM を使用すると、コードは DOM 操作ではなくビジネス ロジックの処理に重点を置くことができます。現在、有名な MVVM フレームワークとしては、vue、avalon、react などが挙げられます。それぞれのフレームワークには独自のメリットがありますが、実装の考え方はほぼ同じで、データ バインディング + ビューの更新です。好奇心といじくり回しの意欲から、この方向に沿って、合計 2,000 行を超えるコードを含む最も単純な MVVM ライブラリ (mvvm.js) も作成しました。命令の名前と使用法は vue に似ています。ここで実装原則と私のコード構成のアイデアについて話しましょう。

アイデアの整理

MVVM は概念的にはビューとデータ ロジックを完全に分離するパターンであり、ViewModel はパターン全体の中心です。 ViewModel を実装するには、データ モデル (Model) をビュー (View) に関連付ける必要があります。実装のアイデア全体は次の 5 つのポイントに簡単に要約できます。

要素の各ノードの命令をスキャンして抽出するコンパイラーを実装します。

要素の命令を解析するパーサーを実装し、ノードの解析などのリフレッシュ関数 (ビューのリフレッシュを特に担当するモジュールが途中で必要になる場合があります) を通じて dom への命令の意図を更新します。 p v-show=" isShow">

まずモデル内の isShow の値を取得し、次に isShow に従って node.style.display を変更して要素の表示と非表示を制御します。

パーサーの各命令のリフレッシュ関数を対応するモデルのフィールドに接続するウォッチャーを実装します。

オブジェクトのすべてのフィールドの値の変更を監視するオブザーバーを実装すると、変更が発生すると、最新の値を取得して通知コールバックをトリガーできます。

オブザーバーを使用してウォッチャーでモデルのモニタリングを確立します。モデルの値が変更されると、ウォッチャーは新しい値を取得した後、ステップ 2 で関連付けられたリフレッシュ関数を呼び出してデータを認識します。変更時にビューを更新する目的。

効果例

最初に、他の MVVM フレームワークのインスタンス化に似た最後の使用例を簡単に見てみましょう。

<div id="mobile-list">
<h1 v-text="title"></h1>
<ul>
<li v-for="item in brands">
<b v-text="item.name"></b>
<span v-show="showRank">Rank: {{item.rank}}</span>
</li>
</ul>
</div>
var element = document.querySelector('#mobile-list');
var vm = new MVVM(element, {
'title' : 'Mobile List',
'showRank': true,
'brands' : [
{'name': 'Apple', 'rank': 1},
{'name': 'Galaxy', 'rank': 2},
{'name': 'OPPO', 'rank': 3}
]
});
vm.set('title', 'Top 3 Mobile Rank List'); // => <h1>Top 3 Mobile Rank List</h1>
ログイン後にコピー
モジュール分割

MVVM を 5 つのモジュールに分割して実装しました: コンパイル モジュール Compiler、解析モジュール Parser、ビュー更新モジュール Updater、データ サブスクリプション モジュール Watcher、およびデータ リスニング モジュール Observer。このプロセスは次のように簡単に説明できます。コンパイラーは命令をコンパイルした後、パーサーに命令情報を渡して解析します。パーサーは初期値を更新し、オブザーバーはデータの変更を監視します。その後、それらを Watcher にフィードバックし、Updater は、ビューを更新するための対応する更新関数を見つけます。

上記のプロセスを図に示します:

以下では、これら 5 つのモジュールの実装の基本原則を紹介します (コードの重要な部分のみが掲載されています。完全な実装を読むには私の Github にアクセスしてください)

1. コンパイラモジュール Compiler

コンパイラの主な役割は、要素の各ノードの命令をスキャンして抽出することです。コンパイルと解析のプロセスはノード ツリー全体を複数回実行するため、コンパイル効率を向上させるために、要素は最初に MVVM コンストラクター内のドキュメント フラグメントの形式でコピー フラグメントに変換されます。コンパイル オブジェクトはこのドキュメント フラグメントであり、すべてのノードがコンパイルされた後、ドキュメントのフラグメントが元の実際のノードに追加されて戻されます。

vm.complieElement は、要素のすべてのノードのスキャンと命令抽出を実装します。

vm.compileAllNodes メソッドは、this.$unCompileNodes 内の各ノードをコンパイルします (命令情報をパーサーに渡します)。ノードをコンパイルした後、ノードをキャッシュ キューから削除し、length == の場合は this.$unCompileNodes を確認します。 = 0 の場合、すべてのコンパイルが完了し、ドキュメントのフラグメントを実際のノードに追加できることを意味します。
vm.complieElement = function(fragment, root) {
var node, childNodes = fragment.childNodes;
// 扫描子节点
for (var i = 0; i < childNodes.length; i++) {
node = childNodes[i];
if (this.hasDirective(node)) {
this.$unCompileNodes.push(node);
}
// 递归扫描子节点的子节点
if (node.childNodes.length) {
this.complieElement(node, false);
}
}
// 扫描完成,编译所有含有指令的节点
if (root) {
this.compileAllNodes();
}
}
ログイン後にコピー

2. 命令解析モジュール Parser

コンパイラ Compiler が各ノードの命令を抽出すると、パーサーで解析することができます。各命令には異なる解析方法があります。すべての命令の解析方法は 2 つのことだけを実行する必要があります。1 つはデータ値をビュー (初期状態) に更新すること、もう 1 つは更新関数を変更監視にサブスクライブすることです。モデル。ここでは、v-text の解析を例として、命令の一般的な解析方法を説明します。

3. 数据订阅模块 Watcher

上个例子,Watcher 提供了一个 watch 方法来对数据变化进行订阅,一个参数是模型字段 model 另一个是回调函数,回调函数是要通过 Observer 来触发的,参数传入新值 last 和 旧值 old , Watcher 拿到新值后就可以找到 model 对应的回调(刷新函数)进行更新视图了。model 和 刷新函数是一对多的关系,即一个 model 可以有任意多个处理它的回调函数(刷新函数),比如: v-text="title" 和 v-html="title" 两个指令共用一个数据模型字段。

添加数据订阅 watcher.watch 实现方式为:

watcher.watch = function(field, callback, context) {
var callbacks = this.$watchCallbacks;
if (!Object.hasOwnProperty.call(this.$model, field)) {
console.warn('The field: ' + field + ' does not exist in model!');
return;
}
// 建立缓存回调函数的数组
if (!callbacks[field]) {
callbacks[field] = [];
}
// 缓存回调函数
callbacks[field].push([callback, context]);
}
ログイン後にコピー

当数据模型的 field 字段发生改变时,Watcher 就会触发缓存数组中订阅了 field 的所有回调。

4. 数据监听模块 Observer

Observer 是整个 mvvm 实现的核心基础,看过有一篇文章说 O.o (Object.observe) 将会引爆数据绑定革命,给前端带来巨大影响力,不过很可惜,ES7 草案已经将 O.o 给废弃了!目前也没有浏览器支持!所幸的是还有 Object.defineProperty 通过拦截对象属性的存取描述符(get 和 set) 可以模拟一个简单的 Observer :

// 拦截 object 的 prop 属性的 get 和 set 方法
Object.defineProperty(object, prop, {
get: function() {
return this.getValue(object, prop);
},
set: function(newValue) {
var oldValue = this.getValue(object, prop);
if (newValue !== oldValue) {
this.setValue(object, newValue, prop);
// 触发变化回调
this.triggerChange(prop, newValue, oldValue);
}
}
});
ログイン後にコピー

然后还有个问题就是数组操作 ( push, shift 等) 该如何监测?所有的 MVVM 框架都是通过重写该数组的原型来实现的:

observer.rewriteArrayMethods = function(array) {
var self = this;
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
var methods = 'push|pop|shift|unshift|splice|sort|reverse'.split('|');
methods.forEach(function(method) {
Object.defineProperty(arrayMethods, method, function() {
var i = arguments.length;
var original = arrayProto[method];
var args = new Array(i);
while (i--) {
args[i] = arguments[i];
}
var result = original.apply(this, args);
// 触发回调
self.triggerChange(this, method);
return result;
});
});
array.__proto__ = arrayMethods;
}
ログイン後にコピー

这个实现方式是从 vue 中参考来的,觉得用的很妙,不过数组的 length 属性是不能够被监听到的,所以在 MVVM 中应避免操作 array.length

5. 视图刷新模块 Updater

Updater 在五个模块中是最简单的,只需要负责每个指令对应的刷新函数即可。其他四个模块经过一系列的折腾,把最后的成果交给到 Updater 进行视图或者事件的更新,比如 v-text 的刷新函数为:

updater.updateNodeTextContent = function(node, text) {
node.textContent = text;
}
ログイン後にコピー

v-bind:style 的刷新函数:

updater.updateNodeStyle = function(node, propperty, value) {
node.style[propperty] = value;
}
ログイン後にコピー

双向数据绑定的实现

表单元素的双向数据绑定是 MVVM 的一个最大特点之一:

其实这个神奇的功能实现原理也很简单,要做的只有两件事:一是数据变化的时候更新表单值,二是反过来表单值变化的时候更新数据,这样数据的值就和表单的值绑在了一起。

数据变化更新表单值利用前面说的 Watcher 模块很容易就可以做到:

watcher.watch(model, function(last, old) {
input.value = last;
});'
ログイン後にコピー

表单变化更新数据只需要实时监听表单的值得变化事件并更新数据模型对应字段即可:

var model = this.$model;
input.addEventListenr('change', function() {
model[field] = this.value;
});‘
ログイン後にコピー

其他表单 radio, checkbox 和 select 都是一样的原理。

以上,整个流程以及每个模块的基本实现思路都讲完了,第一次在社区发文章,语言表达能力不太好,如有说的不对写的不好的地方,希望大家能够批评指正!

结语

折腾这个简单的 mvvm.js 是因为原来自己的框架项目中用的是 vue.js 但是只是用到了它的指令系统,一大堆功能只用到四分之一左右,就想着只是实现 data-binding 和 view-refresh 就够了,结果没找这样的 javascript 库,所以我自己就造了这么一个轮子。

虽说功能和稳定性远不如 vue 等流行 MVVM 框架,代码实现可能也比较粗糙,但是通过造这个轮子还是增长了很多知识的 ~ 进步在于折腾嘛!

目前我的 mvvm.js 只是实现了最本的功能,以后我会继续完善、健壮它,如有兴趣欢迎一起探讨和改进~

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? フロントエンドのサーマルペーパーレシートのために文字化けしたコード印刷に遭遇した場合はどうすればよいですか? Apr 04, 2025 pm 02:42 PM

フロントエンドのサーマルペーパーチケット印刷のためのよくある質問とソリューションフロントエンド開発におけるチケット印刷は、一般的な要件です。しかし、多くの開発者が実装しています...

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

javascriptの分解:それが何をするのか、なぜそれが重要なのか javascriptの分解:それが何をするのか、なぜそれが重要なのか Apr 09, 2025 am 12:07 AM

JavaScriptは現代のWeb開発の基礎であり、その主な機能には、イベント駆動型のプログラミング、動的コンテンツ生成、非同期プログラミングが含まれます。 1)イベント駆動型プログラミングにより、Webページはユーザー操作に応じて動的に変更できます。 2)動的コンテンツ生成により、条件に応じてページコンテンツを調整できます。 3)非同期プログラミングにより、ユーザーインターフェイスがブロックされないようにします。 JavaScriptは、Webインタラクション、シングルページアプリケーション、サーバー側の開発で広く使用されており、ユーザーエクスペリエンスとクロスプラットフォーム開発の柔軟性を大幅に改善しています。

JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? JavaScriptを使用して、同じIDを持つArray要素を1つのオブジェクトにマージする方法は? Apr 04, 2025 pm 05:09 PM

同じIDを持つ配列要素をJavaScriptの1つのオブジェクトにマージする方法は?データを処理するとき、私たちはしばしば同じIDを持つ必要性に遭遇します...

Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Console.log出力の違い結果:なぜ2つの呼び出しが異なるのですか? Apr 04, 2025 pm 05:12 PM

Console.log出力の違いの根本原因に関する詳細な議論。この記事では、Console.log関数の出力結果の違いをコードの一部で分析し、その背後にある理由を説明します。 �...

Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は?
または:
Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Shiseidoの公式Webサイトのように、視差スクロールと要素のアニメーション効果を実現する方法は? または: Shiseidoの公式Webサイトのようにスクロールするページを伴うアニメーション効果をどのように実現できますか? Apr 04, 2025 pm 05:36 PM

この記事の視差スクロールと要素のアニメーション効果の実現に関する議論では、Shiseidoの公式ウェブサイト(https://www.shisido.co.co.jp/sb/wonderland/)と同様の達成方法について説明します。

JavaScriptは学ぶのが難しいですか? JavaScriptは学ぶのが難しいですか? Apr 03, 2025 am 12:20 AM

JavaScriptを学ぶことは難しくありませんが、挑戦的です。 1)変数、データ型、関数などの基本概念を理解します。2)非同期プログラミングをマスターし、イベントループを通じて実装します。 3)DOM操作を使用し、非同期リクエストを処理することを約束します。 4)一般的な間違いを避け、デバッグテクニックを使用します。 5)パフォーマンスを最適化し、ベストプラクティスに従ってください。

フロントエンド開発でVSCodeと同様に、パネルドラッグアンドドロップ調整機能を実装する方法は? フロントエンド開発でVSCodeと同様に、パネルドラッグアンドドロップ調整機能を実装する方法は? Apr 04, 2025 pm 02:06 PM

フロントエンドのVSCodeと同様に、パネルドラッグアンドドロップ調整機能の実装を調べます。フロントエンド開発では、VSCODEと同様のVSCODEを実装する方法...

See all articles