Vue のレスポンシブ実装の原理について説明する記事

青灯夜游
リリース: 2022-09-15 19:36:43
転載
1654 人が閲覧しました

Vue のレスポンシブ実装の原理について説明する記事

#vue は、多くの便利な機能が組み込まれた使いやすいフレームワークで、最も特徴的な機能は下部に隠されたレスポンシブ システムです。コンポーネントの状態はすべて、応答性の高い JavaScript オブジェクトです。それらを変更するとビューが更新され、状態管理がより簡単かつ直感的になります。では、Vue 応答システムはどのように実装されているのでしょうか?この記事も Vue のソースコードを読んだ上での理解と模倣実装をベースにしていますので、著者の考えに従って Vue を浅いところから深いところまで探っていきましょう! [関連する推奨事項: vuejs ビデオ チュートリアル ]

この記事の Vue ソース コード バージョン: 2.6.14. 理解を容易にするために、コードは簡略化されています。

Vue がデータ応答性を実装する方法

通常の JavaScript オブジェクトをデータ オプションとして Vue インスタンスに渡すと、Vue はオブジェクトのすべてのプロパティを走査します。そして、Object.defineProperty を使用してこれらすべてのプロパティをゲッター/セッターに変換し、ゲッター/セッターを実行します。

Vue の応答システムを一文で要約すると、次のようになります。 Observer モード Object.defineProperty が getter/setter をインターセプト

MDN ObjdefineProperty

#オブザーバー パターン

Object.defineProperty とは何ですか?

Object.defineProperty() メソッドは、オブジェクトの新しいプロパティを直接定義するか、オブジェクトの既存のプロパティを変更します。このオブジェクトを返します。

簡単に言えば、このように定義されたプロパティは、

obj.xxx の実行時に get をトリガーし、obj.xxx = xxx の実行時に set をトリガーします。応答性の鍵となります。

Object.defineProperty は、シム化できない (ポリフィルを介して実装できない) ES5 の機能です。そのため、Vue は IE8 以前のブラウザをサポートしません。

レスポンシブ システムの基本的な実装

次に、

Object.defineProperty に基づいて簡単なシステムを実装しましょう。 「前菜」としてのレスポンシブ更新システム

let data = {};
// 使用一个中间变量保存 value
let value = "hello";
// 用一个集合保存数据的响应更新函数
let fnSet = new Set();
// 在 data 上定义 text 属性
Object.defineProperty(data, "text", {
  enumerable: true,
  configurable: true,
  set(newValue) {
    value = newValue;
    // 数据变化
    fnSet.forEach((fn) => fn());
  },
  get() {
    fnSet.add(fn);
    return value;
  },
});

// 将 data.text 渲染到页面上
function fn() {
  document.body.innerText = data.text;
}
// 执行函数,触发读取 get
fn();

// 一秒后改变数据,触发 set 更新
setTimeout(() => {
  data.text = "world";
}, 1000);
ログイン後にコピー
次に、このコードをブラウザで実行すると、望ましい効果が得られます

上記のコードを通じて、あなたはレスポンシブに興味があると思います。システムがどのように機能するかをある程度理解していること。この「前菜」を理解しやすくするために、この単純なリアクティブ システムにはまだ多くの欠点があります。例: データと応答の更新関数はハード コーディングを通じて強く結合されており、1 対 1 の状況のみを実現し、モジュール式ではありません。十分です。お待​​ちください...それでは、次は 1 つずつ完成させていきましょう。

完全な応答システムを設計する

完全な応答システムを設計するには、まず前提条件となる知識、つまりオブザーバー パターンとは何かを理解する必要があります。

オブザーバー パターンとは何ですか?

これは、オブジェクト イベントの発生時にオブジェクトを「監視」する他の複数のオブジェクトに通知できるサブスクリプション メカニズムを定義できる動作設計パターンです。

注目に値するステータスを持つオブジェクトは通常、

ターゲットと呼ばれます。自身のステータスが変化したときに他のオブジェクトに通知する必要があるため、パブリッシャーとも呼ばれます。 ## #。パブリッシャーの状態の変化を追跡したい他のすべてのオブジェクトは、subscribers と呼ばれます。さらに、パブリッシャーはインターフェースを介してのみすべてのサブスクライバーと直接対話し、すべてのサブスクライバーが 同じインターフェースを持っている必要があります

Vue のレスポンシブ実装の原理について説明する記事たとえば?:

あなた (つまり、アプリケーションの購読者) は、特定の書店の週刊誌に興味があり、上司 (つまり、アプリケーション ) の発行者は電話番号を残して、新しい週刊誌が入手可能になり次第、上司に電話するよう依頼しました。この週刊誌に興味を持った他の人も、上司に電話番号を残しました。新しい週刊誌が届くと、上司は読者にそれを受け取るように一人ひとりに電話をかけます。

リーダーが誤って電話番号の代わりに QQ 番号を残した場合、古いバージョンでは電話をかけることができず、リーダーは通知を受信できません。これは上で述べたとおりであり、同じインターフェイスを持つ必要があります。

オブザーバー パターンを理解した後、応答性の高いシステムの設計を開始しました。

抽象オブザーバー (サブスクライバー) クラス Watcher

在上面的例子中,数据和响应更新函数是通过硬编码强耦合在一起的。而实际开发过程中,更新函数不一定叫fn,更有可能是一个匿名函数。所以我们需要抽像一个观察者(订阅者)类Watcher来保存并执行更新函数,同时向外提供一个update更新接口。

// Watcher 观察者可能有 n 个,我们为了区分它们,保证唯一性,增加一个 uid
let watcherId = 0;
// 当前活跃的 Watcher
let activeWatcher = null;

class Watcher {
  constructor(cb) {
    this.uid = watcherId++;
    // 更新函数
    this.cb = cb;
    // 保存 watcher 订阅的所有数据
    this.deps = [];
    // 初始化时执行更新函数
    this.get();
  }
  // 求值函数
  get() {
    // 调用更新函数时,将 activeWatcher 指向当前 watcher
    activeWatcher = this;
    this.cb();
    // 调用完重置
    activeWatcher = null;
  }
  // 数据更新时,调用该函数重新求值
  update() {
    this.get();
  }
}
ログイン後にコピー

抽象被观察者(发布者)类Dep

我们再想一想,实际开发过程中,data 中肯定不止一个数据,而且每个数据,都有不同的订阅者,所以说我们还需要抽象一个被观察者(发布者)Dep类来保存数据对应的观察者(Watcher),以及数据变化时通知观察者更新。

class Dep {
  constructor() {
    // 保存所有该依赖项的订阅者
    this.subs = [];
  }
  addSubs() {
    // 将 activeWatcher 作为订阅者,放到 subs 中
    // 防止重复订阅
    if(this.subs.indexOf(activeWatcher) === -1){
      this.subs.push(activeWatcher);
    }
  }
  notify() {
    // 先保存旧的依赖,便于下面遍历通知更新
    const deps = this.subs.slice()
    // 每次更新前,清除上一次收集的依赖,下次执行时,重新收集
    this.subs.length = 0;
    deps.forEach((watcher) => {
      watcher.update();
    });
  }
}
ログイン後にコピー

抽象 Observer

现在,WatcherDep只是两个独立的模块,我们怎么把它们关联起来呢?

答案就是Object.defineProperty,在数据被读取,触发get方法,Dep 将当前触发 get 的 Watcher 当做订阅者放到 subs中,Watcher 就与 Dep建立关系;在数据被修改,触发set方法,Dep就遍历 subs 中的订阅者,通知Watcher更新。

下面我们就来完善将数据转换为getter/setter的处理。

上面基础的响应式系统实现中,我们只定义了一个响应式数据,当 data 中有其他property时我们就处理不了了。所以,我们需要抽象一个 Observer类来完成对 data数据的遍历,并调用defineReactive转换为 getter/setter,最终完成响应式绑定。

为了简化,我们只处理data中单层数据。

class Observer {
  constructor(value) {
    this.value = value;
    this.walk(value);
  }
  // 遍历 keys,转换为 getter/setter
  walk(obj) {
    const keys = Object.keys(obj);
    for (let i = 0; i <p>这里我们通过参数 value 的闭包,来保存最新的数据,避免新增其他变量</p><pre class="brush:php;toolbar:false">function defineReactive(target, key, value) {
  // 每一个数据都是一个被观察者
  const dep = new Dep();
  Object.defineProperty(target, key, {
    enumerable: true,
    configurable: true,
    // 执行 data.xxx 时 get 触发,进行依赖收集,watcher 订阅 dep
    get() {
      if (activeWatcher) {
        // 订阅
        dep.addSubs(activeWatcher);
      }
      return value;
    },
    // 执行 data.xxx = xxx 时 set 触发,遍历订阅了该 dep 的 watchers,
    // 调用 watcher.updata 更新
    set(newValue) {
      // 如果前后值相等,没必要跟新
      if (value === newVal) {
        return;
      }
      value = newValue;
      // 派发更新
      dep.notify();
    },
  });
}
ログイン後にコピー

至此,响应式系统就大功告成了!!

测试

我们通过下面代码测试一下:

let data = {
  name: "张三",
  age: 18,
  address: "成都",
};
// 模拟 render
const render1 = () => {
  console.warn("-------------watcher1--------------");
  console.log("The name value is", data.name);
  console.log("The age value is", data.age);
  console.log("The address value is", data.address);
};
const render2 = () => {
  console.warn("-------------watcher2--------------");
  console.log("The name value is", data.name);
  console.log("The age value is", data.age);
};
// 先将 data 转换成响应式
new Observer(data);
// 实例观察者
new Watcher(render1);
new Watcher(render2);
ログイン後にコピー

在浏览器中运行这段代码,和我们期望的一样,两个render都执行了,并且在控制台上打印了结果。

Vue のレスポンシブ実装の原理について説明する記事

我们尝试修改 data.name = '李四 23333333',测试两个 render 都会重新执行:

Vue のレスポンシブ実装の原理について説明する記事

我们只修改 data.address = '北京',测试一下是否只有render 1回调都会重新执行:

Vue のレスポンシブ実装の原理について説明する記事

都完美通过测试!!?

总结

Vue のレスポンシブ実装の原理について説明する記事

Vue响应式原理的核心就是ObserverDepWatcher,三者共同构成 MVVM 中的 VM

Observer中进行数据响应式处理以及最终的WatcherDep关系绑定,在数据被读的时候,触发get方法,将 Watcher收集到 Dep中作为依赖;在数据被修改的时候,触发set方法,Dep就遍历 subs 中的订阅者,通知Watcher更新。

本篇文章属于入门篇,并非源码实现,在源码的基础上简化了很多内容,能够便于理解ObserverDepWatcher三者的作用和关系。

本文的源码,以及作者学习 Vue 源码完整的逐行注释源码地址:github.com/yue1123/vue…

(学习视频分享:web前端开发编程基础视频

以上がVue のレスポンシブ実装の原理について説明する記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート