origine
Il y a quelques jours, je regardais la mise en œuvre de certains mini-Frameworks populaires MVVM (tels que des cadres plus légers tels que Avalon.js, Vue.js, plutôt que des cadres plus lourds tels que AngularJS et Emberjs). Les cadres MVVM modernes populaires éliminent généralement la liaison bidirectionnelle des données (liaison des données à deux voies), qui est un argument de vente du cadre lui-même (Ember.js ne semble pas prendre en charge la liaison bidirectionnelle des données.), Et les méthodes d'implémentation de la liaison bidirectionnelle de chaque cadre ne sont pas très cohérentes. Par exemple, Anguarjs utilise la vérification sale en interne, tandis que l'essence de l'implémentation interne d'Avalon.js est de définir des accessoires de propriété.
Nous n'avons pas l'intention de discuter de l'implémentation spécifique de la liaison bidirectionnelle des données par chaque cadre ici. Nous ne parlerons que de plusieurs méthodes courantes d'implémentation de liaison bidirectionnelle à l'avant et de nous concentrer sur la sélection technique d'Avalon.js pour implémenter la liaison des données bidirectionnelles.
La mise en œuvre conventionnelle de la liaison des données bidirectionnelle
Tout d'abord, parlons de la liaison des données bidirectionnelles frontale. En termes simples, c'est la couche de contrôleur du cadre (la couche de contrôleur ici est un terme général, qui peut être compris comme le middleware qui contrôle le comportement de vue et connecte la couche de modèle) et la couche d'affichage de l'interface utilisateur (couche de vue) pour établir un canal de données bidirectionnel. Lorsque l'une ou l'autre de ces deux couches change, l'autre couche apportera automatiquement les modifications correspondantes immédiatement (ou semblera être immédiatement).
D'une manière générale, pour réaliser cette relation de liaison aux données bidirectionnelle (le processus de corrélation entre la couche de contrôleur et la couche d'affichage), il y a actuellement trois façons dans l'avant.
1. Dirty Check
2. Mécanisme d'observation
3. Accesseur de propriété encapsulé
Saleté
Nous disons qu'AngularJS (se réfère ici spécifiquement à la version AngularJS 1.xx, qui ne représente pas la version AngularJS 2.xx) est une implémentation technique de la liaison des données bidirectionnelle. Le principe général est qu'AngularJS maintiendra une séquence et placera tous les attributs qui doivent être surveillés dans cette séquence. Lorsque certains événements spécifiques se produisent (notez que cela n'est pas chronométré mais déclenché par certains événements spéciaux), AngularJS appellera la méthode $ digest. La logique à l'intérieur de cette méthode consiste à traverser tous les observateurs, à comparer les attributs surveillés et à comparer si la valeur d'attribut a changé avant et après l'appel de la méthode. S'il change, le gestionnaire correspondant sera appelé. Il existe de nombreux articles sur Internet qui analysent le principe de mise en œuvre de la liaison des données bidirectionnelle d'AngularJS, comme cet article, etc.
Les inconvénients de cette méthode sont évidents. Les observateurs de traversée et de formation sont très consommateurs de performances, en particulier lorsque le nombre de surveillance d'une seule page atteint un ordre de grandeur.
Mécanisme d'observation
Le blogueur avait un article réimprimé et traduit, le changement de liaison des données provoqué par object.observe (), qui fait référence à l'utilisation de la méthode object.observe dans ecmascript7 pour surveiller et observer l'objet (ou ses propriétés). Une fois qu'il change, le gestionnaire correspondant sera exécuté.
C'est le moyen le plus parfait de surveiller actuellement les changements de données d'attribut. Le support natif du langage (navigateur) n'est rien de mieux que cela. Le seul regret est que l'étendue actuelle de soutien n'est pas suffisante et doit être pleinement promue.
Accessoire de propriété d'encapsulation
Il existe un concept de méthodes magiques en PHP, comme les méthodes __get () et __set () en PHP. Il y a un concept similaire dans JavaScript, mais il n'est pas appelé une méthode magique, mais un accessoire. Regardons un exemple de code.
var data = {name: "erik", getName: function () {return this.name;}, setName: function (name) {this.name = name;}};À partir du code ci-dessus, nous pouvons avoir un aperçu du saut, comme les méthodes getName () et setName () dans les données. Nous pouvons simplement le considérer comme l'accessoire (ou un accessoire) de Data.name.
En fait, pour le code ci-dessus, s'il est plus strict, il n'est pas autorisé à accéder directement à la propriété Data.Name. Toutes les lectures et écritures sur Data.name doivent être transmises via les méthodes data.getName () et data.setName (). Imaginez donc qu'une fois qu'une propriété n'autorise pas la lecture et l'écriture directes, mais doit être lue et écrite via l'accessoire, alors bien sûr, je fais des extras en réécrivant la méthode accessoire de la propriété, telle que le suivi du changement de valeur de la propriété. Il s'agit du principe d'utilisation d'accessoires de propriété pour effectuer la liaison des données bidirectionnelle.
Bien sûr, cette méthode présente également ses inconvénients. La chose la plus importante est que chaque fois qu'une surveillance d'attribut est ajoutée, une méthode d'accessoire correspondant doit être ajoutée à cet attribut, sinon le changement de cet attribut ne sera pas capturé.
Méthode object.defineproperty
Le principe du framework MVVM national Avalon.js implémentant la liaison bidirectionnelle des données est l'accessoire de propriété. Mais bien sûr, ce ne sera pas aussi original que l'exemple de code ci-dessus. Il utilise la méthode de la propriété standard. En réponse aux conditions du marché intérieur, certains ne soutiennent pas l'objet.DefineProperty. Les navigateurs de bas niveau utilisent VBScript pour une compatibilité parfaite, contrairement à d'autres cadres MVVM qui ont progressivement abandonné le support pour les navigateurs bas de gamme.
Définissons d'abord la méthode object.defineproperty sur MDN.
La méthode object.defineproperty () définit une nouvelle propriété directement sur un objet, ou modifiez une propriété existante sur un objet et renvoie l'objet.
La signification est claire et la méthode Object.DefineProperty fournit un moyen direct de définir les propriétés des objets ou de modifier les propriétés d'objet existantes. Le prototype de la méthode est le suivant:
Object.defineproperty (obj, prop, descripteur)
dans,
obj, objet à modifier
Prop, avec nom d'attribut modifié
Descripteur, la description pertinente des attributs à modifier
Le descripteur nécessite un objet à passer, sa valeur par défaut est la suivante
/ *** @ {param} Descripteur * / {configurable: false, énumérable: false, écrivable: false, valeur: null, set: undefined, get: undefined}configurable, si la propriété est configurable. Les significations configurables incluent: si l'attribut peut être supprimé, si les propriétés écrivables, énumérables et configurables de l'attribut peuvent être modifiées.
Énumérable, si l'attribut est énumérable. La signification de l'énumération comprend: si elle peut être traversée pour ... in et si elle peut être obtenue via la méthode object.keys ().
Écrivable, si l'attribut peut être réécrit. La signification de la rérabilité comprend: si l'attribut peut être réaffecté.
valeur, la valeur par défaut de la propriété.
set, un réécriture de propriétés (pour l'instant, c'est tout). Une fois l'attribut réaffecté, cette méthode est automatiquement appelée.
Obtenez, le lecteur de la propriété (pour l'instant, c'est tout). Une fois la propriété accessible et lue, cette méthode est automatiquement appelée.
Voici un exemple de code,
var o = {}; object.defineproperty (o, 'name', {value: 'erik'}); console.log (object.getownPropertyDescriptor (o, 'name')); // Object {Value: "Erik", Writable: false, énumérable: false, configurable: false} object.defineproperty (o, 'age', {value: 26, configurable: true, withing: true}); console.log (o.age); // 26o.age = 18; console.log (o.age); // 18. Parce que la propriété d'âge est une console réécrit.log (object.keys (o)); // []. Ni le nom ni la propriété d'âge sont un objet énumérable. // l'affectation ici est en fait inefficace Console.log (O.SEX); // 'mâle'; supprimer o.sex; // faux, l'action de suppression de la propriété n'est pas non valideAprès l'exemple ci-dessus, dans des circonstances normales, l'utilisation d'object.definePropert () est relativement simple.
Cependant, il y a encore une chose qui nécessite une attention supplémentaire. Lorsque la méthode object.defineproperty () définit les propriétés, la propriété ne peut pas déclarer les attributs d'accessoire (set et get) et les attributs de rédaction ou de valeur en même temps. Cela signifie que si une certaine propriété est définie avec un attribut écrit ou Value, cette propriété ne peut pas déclarer Get and Set, et vice versa.
Parce que lorsque Object.DefineProperty () déclare une propriété, plus de deux types de contrôles d'accès ne sont pas autorisés pour la même propriété.
Exemple de code,
var o = {}, myname = 'erik'; object.defineproperty (o, 'name', {value: myname, set: function (name) {myName = name;}, get: function () {return myName;}});Le code ci-dessus semble être bien, mais il rapportera une erreur lors de sa réalisation, et l'erreur sera signalée comme suit.
TypeError: propriété non valide. Une propriété ne peut pas avoir à la fois des accessoires et être écrivative ou avoir une valeur, # <objet>
Étant donné que l'attribut de nom déclare ici l'attribut de valeur, les attributs set and get en même temps, qui fournissent deux contrôles de lecture et d'écriture sur l'attribut de nom. Si la fonctionnalité de valeur n'est pas déclarée ici, mais que la fonction rédactée est déclarée, le résultat sera le même et une erreur sera signalée.