[Edit] 7 mois plus tard
Citant le projet github
jQuery n'est pas bon, et les plugins jQuery est pas la façon de faire du code modulaire.
Au sérieux "plugins jQuery" ne sont pas un bruit de l'architecture de la stratégie. L'écriture de code avec une dépendance matérielle sur jQuery est aussi absurde.
[Original]
Depuis que j'ai donné critique sur ce modèle, je vais proposer une alternative.
Pour rendre plus facile de vivre cela dépend de l' jQuery
1.6+ et ES5 (utiliser l' ES5 Cale).
J'ai passer un peu de temps de re-concevoir le plugin modèle que vous avez donné et lancé mon propre.
Liens:
Comparaison:
J'ai refait le modèle de sorte qu'elle est divisée en standard (85%) et de l'échafaudage code (15%). L'intention est que vous n'avez qu'à éditer les échafaudages de code et vous pouvez garder le congé de code réutilisable intacte. Pour réaliser cela, j'ai utilisé
-
l'héritage
var self = Object.create(Base)
Plutôt d'édition de l' Internal
classe que vous avez directement, vous devriez être en train de modifier une sous-classe. Tous les modèles / par défaut la fonctionnalité doit être dans une classe de base (appelés Base
dans mon code).
-
convention -
self[PLUGIN_NAME] = main;
, Par convention, le plugin défini sur jQuery qui fera appel à la méthode de définir sur self[PLUGIN_NAME]
par défaut. Ceci est considéré comme l' main
plugin méthode et a une autre méthode externe pour plus de clarté.
-
monkey patching
$.fn.bind = function _bind ...
Utilisation de monkey patching signifie que l'événement namespacing est fait automatiquement pour vous sous le capot. Cette fonctionnalité est gratuite et ne pas se faire au détriment de la lisibilité (appelant getEventNS
tout le temps).
OO Techniques
Il est préférable de s'en tenir à la bonne JavaScript OO plutôt classique OO émulation. Pour ce faire, vous devez utiliser Object.create
. (ES5 suffit d'utiliser la cale de mise à niveau de vieux navigateurs).
var Base = (function _Base() {
var self = Object.create({});
/* ... */
return self;
})();
var Wrap = (function _Wrap() {
var self = Object.create(Base);
/* ... */
return self;
})();
var w = Object.create(Wrap);
C'est différent de la norme new
et .prototype
basé OO les gens sont habitués. Cette approche est privilégiée parce qu'elle renforce l'idée qu'il y a seulement des Objets en JavaScript et c'est un prototype OO approche.
[getEventNs
]
Comme mentionné cette méthode a été reconstruit à l'écart par des raisons impérieuses .bind
et .unbind
pour injecter automatiquement les espaces de noms. Ces méthodes sont écrasés sur le privé, la version de jQuery $.sub()
. L'écrasé méthodes se comportent de la même manière que votre namespacing. Il espaces de noms événements uniquement basé sur le plugin et l'exemple d'un plugin wrapper autour d'une HTMLElement (à l'Aide d' .ns
.
[getData
]
Cette méthode a été remplacée par un .data
méthode qui a la même API que jQuery.fn.data
. Le fait que c'est la même API rend plus facile à utiliser, c'est en fait un wrapper mince autour d' jQuery.fn.data
avec namespacing. Cela vous permet de définir une paire clé/valeur de données qui est immédiatement stockée pour ce plugin. Plusieurs plugins peuvent utiliser cette méthode en parallèle, sans aucun conflit.
[publicMethods
]
Le publicMethods objet a été remplacé par n'importe quelle méthode étant définie en Wrap
automatiquement public. Vous pouvez appeler n'importe quelle méthode sur un objet Enveloppé directement, mais vous n'avez pas réellement avoir accès à l'objet enveloppé.
[$.fn[PLUGIN_NAME]
]
Cela a été refait donc, il expose plus uniformes et de l'API. Cette api est
$(selector).PLUGIN_NAME("methodName", {/* object hash */}); // OR
$(selector).PLUGIN_NAME({/* object hash */}); // methodName defaults to PLUGIN_NAME
les éléments du sélecteur sont automatiquement enveloppé dans l' Wrap
de l'objet, la méthode est appelée ou chaque élément sélectionné dans le sélecteur et la valeur de retour est toujours un $.Deferred
élément.
Cette normalise l'API et le type de retour. Vous pouvez ensuite appeler .then
sur le revenu reportés pour obtenir les données qui vous intéressent. L'utilisation de reportées ici est très puissant pour l'abstraction loin si le plugin est synchrone ou asynchrone.
_create
Une mise en cache de créer la fonction a été ajoutée. Voilà ce qui s'appelle tourner un HTMLElement
Enveloppé dans un élément et de chaque HTMLElement ne seront enveloppés d'une fois. Cette mise en cache vous donne une solide réduction de la mémoire.
$.PLUGIN_NAME
A ajouté une autre méthode publique pour le plugin (pour Un total de deux!).
$.PLUGIN_NAME(elem, "methodName", {/* options */});
$.PLUGIN_NAME([elem, elem2, ...], "methodName", {/* options */});
$.PLUGIN_NAME("methodName", {
elem: elem, /* [elem, elem2, ...] */
cb: function() { /* success callback */ }
/* further options */
});
Tous les paramètres sont facultatifs. elem
par défaut est <body>
, "methodName"
par défaut est "PLUGIN_NAME"
et {/* options */}
par défaut est {}
.
Cette API est très souple (14 surcharges de méthode!) et standard suffisant pour s'habituer à la syntnax pour chaque méthode de votre plugin pour exposer.
L'exposition du Public
L' Wrap
, create
et $
des objets sont exposés dans le monde. Ceci permettra à l'avancée du plugin utilisateurs un maximum de flexibilité avec votre plugin. Ils peuvent utiliser create
et de la modification de la subbed $
de leur développement, et ils peuvent également monkey patch Wrap
. Cela permet par exemple d'accrochage dans votre plugin méthodes. Tous les trois de ceux-ci sont marqués avec un _
en face de leur nom, de sorte qu'ils sont à l'intérieur et à l'aide de leur casse la garantuee que votre plugin fonctionne.
L'interne defaults
objet est également exposées en $.PLUGIN_NAME.global
. Cela permet aux utilisateurs de modifier vos paramètres par défaut et définir plugin global defaults
. Dans ce plugin installation de tous les hachages passé dans les méthodes que les objets sont fusionnées avec les valeurs par défaut, ce qui permet aux utilisateurs de définir des valeurs globales par défaut pour toutes vos méthodes.
Code
(function($, jQuery, window, document, undefined) {
var PLUGIN_NAME = "Identity";
// default options hash.
var defaults = {
// TODO: Add defaults
};
// -------------------------------
// -------- BOILERPLATE ----------
// -------------------------------
var toString = Object.prototype.toString,
// uid for elements
uuid = 0,
Wrap, Base, create, main;
(function _boilerplate() {
// over-ride bind so it uses a namespace by default
// namespace is PLUGIN_NAME_<uid>
$.fn.bind = function _bind(type, data, fn, nsKey) {
if (typeof type === "object") {
for (var key in type) {
nsKey = key + this.data(PLUGIN_NAME)._ns;
this.bind(nsKey, data, type[key], fn);
}
return this;
}
nsKey = type + this.data(PLUGIN_NAME)._ns;
return jQuery.fn.bind.call(this, nsKey, data, fn);
};
// override unbind so it uses a namespace by default.
// add new override. .unbind() with 0 arguments unbinds all methods
// for that element for this plugin. i.e. calls .unbind(_ns)
$.fn.unbind = function _unbind(type, fn, nsKey) {
// Handle object literals
if ( typeof type === "object" && !type.preventDefault ) {
for ( var key in type ) {
nsKey = key + this.data(PLUGIN_NAME)._ns;
this.unbind(nsKey, type[key]);
}
} else if (arguments.length === 0) {
return jQuery.fn.unbind.call(this, this.data(PLUGIN_NAME)._ns);
} else {
nsKey = type + this.data(PLUGIN_NAME)._ns;
return jQuery.fn.unbind.call(this, nsKey, fn);
}
return this;
};
// Creates a new Wrapped element. This is cached. One wrapped element
// per HTMLElement. Uses data-PLUGIN_NAME-cache as key and
// creates one if not exists.
create = (function _cache_create() {
function _factory(elem) {
return Object.create(Wrap, {
"elem": {value: elem},
"$elem": {value: $(elem)},
"uid": {value: ++uuid}
});
}
var uid = 0;
var cache = {};
return function _cache(elem) {
var key = "";
for (var k in cache) {
if (cache[k].elem == elem) {
key = k;
break;
}
}
if (key === "") {
cache[PLUGIN_NAME + "_" + ++uid] = _factory(elem);
key = PLUGIN_NAME + "_" + uid;
}
return cache[key]._init();
};
}());
// Base object which every Wrap inherits from
Base = (function _Base() {
var self = Object.create({});
// destroy method. unbinds, removes data
self.destroy = function _destroy() {
if (this._alive) {
this.$elem.unbind();
this.$elem.removeData(PLUGIN_NAME);
this._alive = false;
}
};
// initializes the namespace and stores it on the elem.
self._init = function _init() {
if (!this._alive) {
this._ns = "." + PLUGIN_NAME + "_" + this.uid;
this.data("_ns", this._ns);
this._alive = true;
}
return this;
};
// returns data thats stored on the elem under the plugin.
self.data = function _data(name, value) {
var $elem = this.$elem, data;
if (name === undefined) {
return $elem.data(PLUGIN_NAME);
} else if (typeof name === "object") {
data = $elem.data(PLUGIN_NAME) || {};
for (var k in name) {
data[k] = name[k];
}
$elem.data(PLUGIN_NAME, data);
} else if (arguments.length === 1) {
return ($elem.data(PLUGIN_NAME) || {})[name];
} else {
data = $elem.data(PLUGIN_NAME) || {};
data[name] = value;
$elem.data(PLUGIN_NAME, data);
}
};
return self;
})();
// Call methods directly. $.PLUGIN_NAME(elem, "method", option_hash)
var methods = jQuery[PLUGIN_NAME] = function _methods(elem, op, hash) {
if (typeof elem === "string") {
hash = op || {};
op = elem;
elem = hash.elem;
} else if ((elem && elem.nodeType) || Array.isArray(elem)) {
if (typeof op !== "string") {
hash = op;
op = null;
}
} else {
hash = elem || {};
elem = hash.elem;
}
hash = hash || {}
op = op || PLUGIN_NAME;
elem = elem || document.body;
if (Array.isArray(elem)) {
var defs = elem.map(function(val) {
return create(val)[op](hash);
});
} else {
var defs = [create(elem)[op](hash)];
}
return $.when.apply($, defs).then(hash.cb);
};
// expose publicly.
Object.defineProperties(methods, {
"_Wrap": {
"get": function() { return Wrap; },
"set": function(v) { Wrap = v; }
},
"_create":{
value: create
},
"_$": {
value: $
},
"global": {
"get": function() { return defaults; },
"set": function(v) { defaults = v; }
}
});
// main plugin. $(selector).PLUGIN_NAME("method", option_hash)
jQuery.fn[PLUGIN_NAME] = function _main(op, hash) {
if (typeof op === "object" || !op) {
hash = op;
op = null;
}
op = op || PLUGIN_NAME;
hash = hash || {};
// map the elements to deferreds.
var defs = this.map(function _map() {
return create(this)[op](hash);
}).toArray();
// call the cb when were done and return the deffered.
return $.when.apply($, defs).then(hash.cb);
};
}());
// -------------------------------
// --------- YOUR CODE -----------
// -------------------------------
main = function _main(options) {
this.options = options = $.extend(true, defaults, options);
var def = $.Deferred();
// Identity returns this & the $elem.
// TODO: Replace with custom logic
def.resolve([this, this.elem]);
return def;
}
Wrap = (function() {
var self = Object.create(Base);
var $destroy = self.destroy;
self.destroy = function _destroy() {
delete this.options;
// custom destruction logic
// remove elements and other events / data not stored on .$elem
$destroy.apply(this, arguments);
};
// set the main PLUGIN_NAME method to be main.
self[PLUGIN_NAME] = main;
// TODO: Add custom logic for public methods
return self;
}());
})(jQuery.sub(), jQuery, this, document);
Comme peut être vu le code de votre soi-disant à modifier est en dessous de la YOUR CODE
ligne de. L' Wrap
objet agit de même pour votre Internal
objet.
La fonction main
est la principale fonction est appelée avec $.PLUGIN_NAME()
ou $(selector).PLUGIN_NAME()
et devrait contenir votre logique principale.