webentwicklung-frage-antwort-db.com.de

Konfiguration eines generischen jQuery-Plugins mit Browserify-shim?

Ich verwende browserify-shim und möchte ein generisches jQuery-Plugin verwenden. Ich habe die Browserify-shim-Dokumente mehrmals durchgesehen und ich kann einfach nicht verstehen, was los ist und/oder weiß, wo Plugins eingefügt, an das jQuery-Objekt angehängt werden sollen :

"browser": {
  "jquery": "./src/js/vendor/jquery.js",
  "caret": "./src/js/vendor/jquery.caret.js"
},

"browserify-shim": {
  "caret": {
     "depends": ["jquery:$"]
  }
}

Gemäß dem in der Dokumentation zu browserify-shim angegebenen Beispiel möchte ich keine Exporte angeben, da sich dieses Plugin (und die meisten, wenn nicht alle jQuery-Plugins) an das jQuery-Objekt anhängen. Wenn ich nicht etwas falsch mache, verstehe ich nicht, warum es nicht funktioniert (ich bekomme eine Fehlermeldung, dass die Funktion undefiniert ist), wenn ich es benutze. Siehe unten:

$('#contenteditable').caret(5);  // Uncaught TypeError: undefined is not a function

Meine Frage ist also, wie man ein generisches jQuery-Plugin (das sich an das jQuery-Objekt anfügt) mit browserify und browserify-shim konfiguriert.

40
Glen Selle

Nachdem ich dies noch einmal durchgesehen und einige weitere Dinge ausprobiert hatte, wandte ich mich endlich dem zu, was browserify-shim macht und wie man es benutzt. Für mich gab es ein Schlüsselprinzip, das ich verstehen musste, bevor ich endlich verstand, wie man browserify-shim benutzt. Grundsätzlich gibt es zwei Möglichkeiten, browserify-shim für zwei verschiedene Anwendungsfälle zu verwenden: Belichten und Shimmen.

Hintergrund

Angenommen, Sie möchten einfach ein Skript-Tag in Ihr Markup einfügen (aus Test- oder Leistungsgründen wie Caching, CDN und dergleichen). Durch das Einfügen eines Skript-Tags in das Markup wird das Skript vom Browser aufgerufen, ausgeführt und höchstwahrscheinlich eine Eigenschaft an das Fensterobjekt angehängt (in JS auch als global bezeichnet). Natürlich können Sie darauf zugreifen, indem Sie entweder myGlobal oder window.myGlobal Ausführen. Bei beiden Syntaxarten gibt es jedoch ein Problem. Es folgt nicht der CommonJS-Spezifikation, dh wenn ein Modul die CommonJS-Syntax (require()) unterstützt, können Sie diese nicht nutzen.

Die Lösung

Mit Browserify-shim können Sie ein globales Objekt angeben, das mithilfe der CommonJS require() -Syntax "verfügbar gemacht" werden soll. Denken Sie daran, Sie könnten var whatever = global; Oder var whatever = window.global; Ausführen, aber Sie könnten NICHT var whatever = require('global') ausführen und erwarten, dass es Ihnen die richtige Bibliothek/das richtige Modul gibt. Seien Sie nicht verwirrt über den Namen der Variablen. Es könnte alles beliebige sein. Sie machen im Wesentlichen eine globale Variable zu einer lokalen Variablen. Es klingt dumm, aber es ist der traurige Zustand von JS im Browser. Auch hier besteht die Hoffnung, dass eine Lib, die die CommonJS-Syntax unterstützt, sich niemals über ein globales Fensterobjekt anhängt. Das heißt, Sie MÜSSEN die require() -Syntax verwenden und einer lokalen Variablen zuweisen und sie dann überall dort verwenden, wo Sie sie benötigen.

Anmerkung: Ich fand die Benennung von Variablen in den browserify-shim-Dokumenten/Beispielen etwas verwirrend. Denken Sie daran, der Schlüssel ist, dass Sie eine lib als ob einfügen möchten, die sich wie ein CommonJS-Modul verhält. Am Ende müssen Sie browserify mitteilen, dass Sie, wenn Sie myGlobal require('myGlobal') benötigen, nur die globale Eigenschaft für das Fensterobjekt window.myGlobal Erhalten möchten.

In der Tat ist es ziemlich einfach, wenn Sie neugierig sind, was die Funktion require tatsächlich bewirkt. Folgendes passiert unter der Haube:

var whatever = require('mygGlobal');

wird...

var whatever = window.mygGlobal;

Belichten

Lassen Sie uns vor diesem Hintergrund sehen, wie wir ein Modul/lib in unserer browserify-shim-Konfiguration verfügbar machen. Im Allgemeinen erklären Sie browserify-shim zwei Sachen. Der Name, unter dem auf das Objekt zugegriffen werden soll, wenn Sie require() aufrufen, und der globale Name, den es auf dem Fensterobjekt erwarten sollte. Hier kommt also die Syntax global:* Ins Spiel. Schauen wir uns ein Beispiel an. Ich möchte jquery als Skript-Tag in index.html ablegen, um eine bessere Leistung zu erzielen. Hier ist, was ich in meiner Konfiguration tun müsste (dies wäre in package.json oder einer externen Konfigurations-JS-Datei):

"browserify-shim": {
  "jquery": "global:$"
}

Also hier ist was das bedeutet. Ich habe jQuery woanders eingefügt (denken Sie daran, browserify-shim hat keine Ahnung, wo wir unser Tag platzieren, muss es aber nicht wissen), aber ich möchte nur die Eigenschaft $ Auf der Fensterobjekt, wenn ich das Modul mit dem String-Parameter "jquery" benötige. Zur weiteren Veranschaulichung. Ich hätte das auch tun können:

"browserify-shim": {
  "thingy": "global:$"
}

In diesem Fall müsste ich "thingy" als Parameter an die Funktion require übergeben, um eine Instanz des jQuery-Objekts zurückzugewinnen (das nur jQuery von window.$ Erhält):

var $ = require('thingy');

Und ja, der Variablenname kann alles sein. Es ist nichts Besonderes daran, dass $ Dieselbe Eigenschaft ist wie die globale Eigenschaft $, Die die eigentliche jQuery-Bibliothek verwendet. Es ist jedoch sinnvoll, denselben Namen zu verwenden, um Verwechslungen zu vermeiden. Dies führt dazu, dass auf die Eigenschaft $ Des Fensterobjekts verwiesen wird, wie durch den Wert global:$ Im Objekt browserify-shim In package.json ausgewählt.

Shimming

Ok, das deckt so ziemlich das Belichten ab. Das andere Hauptmerkmal von browserify-shim ist das Shimmen. Also, was ist das? Shimming macht im Wesentlichen dasselbe wie das Exponieren, außer dass Sie die lib oder das Modul in HTML-Markup mit so etwas wie einem Skript-Tag einbinden. Sie teilen browserify-shim mit, wo die JS-Datei lokal abgerufen werden soll. Die Syntax global:* Muss nicht verwendet werden. Lassen Sie uns also auf unser jQuery-Beispiel zurückkommen, aber dieses Mal nehmen wir an, wir laden jQuery nicht von einem CDN, sondern bündeln es einfach mit allen JS-Dateien. So würde die Konfiguration also aussehen:

"browser": {
  "jquery": "./src/js/vendor/jquery.js", // Path to the local JS file relative to package.json or an external shim JS file
},
"browserify-shim": {
  "jquery": "$"
},

Diese Konfiguration weist browserify-shim an, jQuery aus dem angegebenen lokalen Pfad zu laden und dann die $ -Eigenschaft aus dem Fensterobjekt abzurufen und diese zurückzugeben, wenn Sie jQuery mit einem Zeichenfolgenparameter für die Funktion "jquery" benötigen. Zur Veranschaulichung können Sie dies auch in etwas anderes umbenennen.

"browser": {
  "thingy": "./src/js/vendor/jquery.js", // Path to the local JS file relative to package.json or an external shim JS file
},
"browserify-shim": {
  "thingy": "$"
},

Welches könnte mit erforderlich sein:

var whatever = require('thingy');

Ich empfehle, in den browserify-shim-Dokumenten nach weiteren Informationen zur Syntax mit der Eigenschaft exports und der Eigenschaft depends zu suchen, mit der Sie browserify-shim mitteilen können, ob es sich um eine lib handelt hängt von einer anderen lib/module ab. Was ich hier erklärt habe, gilt für beide. Ich hoffe, dies hilft anderen, zu verstehen, was browserify-shim tatsächlich tut und wie man es benutzt.

Anonymes Shimming

Anonymous Shimming ist eine Alternative zu browserify-shim, mit der Sie Bibliotheken wie jQuery mit der browserify-Option --standalone In UMD-Module umwandeln können.

$ browserify ./src/js/vendor/jquery.js -s thingy > ../dist/jquery-UMD.js

Wenn Sie dies in ein Skript-Tag einfügen, fügt dieses Modul jQuery als thingy zum Fensterobjekt hinzu. Natürlich könnte es auch $ Sein oder was auch immer Sie mögen.

Wenn es jedoch in Ihrem browserify-App-Bundle requireed ist, var $ = require("./dist/jquery-UMD.js");, steht jQuery in der App zur Verfügung, ohne dass es dem Fensterobjekt hinzugefügt wird.

Diese Methode erfordert kein browserify-shim und nutzt die CommonJS-Kenntnisse von jQuery, um nach einem module -Objekt zu suchen und ein noGlobal -Flag an die Factory zu übergeben, das angibt, sich nicht an das Fensterobjekt anzuhängen .

97
Glen Selle

Für alle, die ein konkretes Beispiel suchen:

Im Folgenden finden Sie ein Beispiel für package.json- und app.js-Dateien für ein jQuery-Plugin, das sich an das jQuery/$-Objekt anfügt, z. B. $('div').expose(). Ich möchte nicht, dass jQuery eine globale Variable (window.jQuery) ist, wenn ich es benötige. Deshalb wird jQuery auf 'exports': null gesetzt. Da das Plugin jedoch ein globales jQuery-Objekt erwartet, an das es sich selbst anhängen kann, müssen Sie es in der Abhängigkeit hinter dem Dateinamen angeben: ./jquery-2.1.3.js:jQuery. Außerdem müssen Sie die Variable jQuery tatsächlich exportieren, wenn Sie das Plugin verwenden, selbst wenn Sie dies nicht möchten, da das Plugin sonst nicht funktioniert (zumindest bei diesem bestimmten Plugin).

package.json

{
  "name": "test",
  "version": "0.1.0",
  "description": "test",
  "browserify-shim": {
    "./jquery-2.1.3.js": { "exports": null },
    "./jquery.expose.js": { "exports": "jQuery", "depends": [ "./jquery-2.1.3.js:jQuery" ] }
  },
  "browserify": {
    "transform": [
      "browserify-shim"
    ]
  }
}

app.js

// copy and delete any previously defined jQuery objects
if (window.jQuery) {
  window.original_jQuery = window.jQuery;
  delete window.jQuery;

  if (typeof window.$.fn.jquery === 'string') {
    window.original_$ = window.$;
    delete window.$;
  }
}

// exposes the jQuery global
require('./jquery.expose.js');
// copy it to another variable of my choosing and delete the global one
var my_jQuery = jQuery;
delete window.jQuery;

// re-setting the original jQuery object (if any)
if (window.original_jQuery) { window.jQuery = window.original_jQuery; delete window.original_jQuery; }
if (window.original_$) { window.$ = window.original_$; delete window.original_$; }

my_jQuery(document).ready(function() {
  my_jQuery('button').click(function(){
    my_jQuery(this).expose();
  });
});

Im obigen Beispiel wollte ich nicht, dass mein Code Globals setzt, aber ich musste dies vorübergehend tun, damit das Plugin funktioniert. Wenn Sie nur jQuery benötigen, können Sie dies einfach tun und benötigen keine Problemumgehung: var my_jQuery = require('./jquery-2.1.3.js'). Wenn es Ihnen gut geht, dass Ihre jQuery als global verfügbar gemacht wird, können Sie das obige package.json-Beispiel wie folgt ändern:

  "browserify-shim": {
    "./jquery-2.1.3.js": { "exports": "$" },
    "./jquery.expose.js": { "exports": null, "depends": [ "./jquery-2.1.3.js" ] }

Ich hoffe, das hilft einigen Leuten, die nach konkreten Beispielen suchten (wie ich es war, als ich diese Frage fand).

12
gottlike

Der Vollständigkeit halber handelt es sich um eine Methode, die das CommonJS-Bewusstsein von jQuery ausnutzt, um zu vermeiden, dass das window-Objekt verschmutzt werden muss, ohne dass ein Shim erforderlich ist.

Eigenschaften

  1. jQuery im Bundle enthalten
  2. plugin im Bundle enthalten
  3. keine Verschmutzung des window-Objekts

Konfig

Fügen Sie in ./package.json einen browser-Knoten hinzu, um Aliase für die Ressourcenpositionen zu erstellen. Dies ist nur der Bequemlichkeit halber, es ist nicht notwendig, irgendetwas auszublenden, da keine Kommunikation zwischen dem Modul und dem globalen Raum (Skript-Tags) besteht .

{
  "main": "app.cb.js",
  "scripts": {
    "build": "browserify ./app.cb.js > ./app.cb.bundle.js"
  },
  "browser": {
    "jquery": "./node_modules/jquery/dist/jquery.js",
    "expose": "./js/jquery.expose.js",
    "app": "./app.cb.js"
  },
  "author": "cool.blue",
  "license": "MIT",
  "dependencies": {
    "jquery": "^3.1.0"
  },
  "devDependencies": {
    "browserify": "^13.0.1",
    "browserify-shim": "^3.8.12"
  }
}

Methode

  • Da jQuery heutzutage CommonJS-fähig ist, erkennt es das von browserify bereitgestellte module-Objekt und gibt eine Instanz zurück, ohne es dem window-Objekt hinzuzufügen.
  • In der App require jquery und fügen Sie sie dem module.exports-Objekt hinzu (zusammen mit allen anderen Kontexten, die gemeinsam genutzt werden müssen).
  • Fügen Sie am Anfang des Plugins eine einzelne Zeile hinzu, damit die App auf die erstellte jQuery-Instanz zugreifen kann.
  • Kopieren Sie in der App die jQuery-Instanz nach $ und verwenden Sie jQuery mit dem Plugin.
  • Durchsuchen Sie die App mit Standardoptionen und legen Sie das resultierende Bundle in einem Skript-Tag in Ihrem HTML-Code ab.

Code

app.cb.js

var $ = module.exports.jQuery = require("jquery");
require('expose');

$(document).ready(function() {

    $('body').append(
        $('<button name="button" >Click me</button>')
            .css({"position": "relative",
                  "top": "100px", "left": "100px"})
            .click(function() {
                $(this).expose();
            })
    );
});

am oberen Rand des Plugins

var jQuery = require("app").jQuery;

im HTML

<script type="text/javascript" src="app.cb.bundle.js"></script>

Hintergrund

Das von jQuery verwendete Muster ruft seine Factory mit einem noGlobal-Flag auf, wenn eine CommonJS-Umgebung erkannt wird. Es fügt dem window-Objekt keine Instanz hinzu und gibt wie immer eine Instanz zurück.

Der CommonJS-Kontext wird standardmäßig von browserify erstellt. Nachfolgend finden Sie einen vereinfachten Auszug aus dem Bundle mit der jQuery-Modulstruktur. Ich habe den Code entfernt, der sich auf die isomorphe Behandlung des window-Objekts bezieht, um der Übersichtlichkeit halber zu helfen. 

3: [function(require, module, exports) {

    ( function( global, factory ) {

        "use strict";

        if ( typeof module === "object" && typeof module.exports === "object" ) {
            module.exports = factory( global, true );
        } else {
            factory( global );
        }

    // Pass this if window is not defined yet
    } )( window, function( window, noGlobal ) {

    // ...

    if ( !noGlobal ) {
        window.jQuery = window.$ = jQuery;
    }

    return jQuery;
    }) );
}, {}]

Die beste Methode, die ich gefunden habe, ist, Dinge im Knotenmodulsystem zum Laufen zu bringen, und dann funktioniert es jedes Mal nach dem Browserisieren.
Verwenden Sie einfach jsdom, um das window-Objekt so einzustellen, dass der Code isomorph ist. Konzentrieren Sie sich dann darauf, dass der Knoten im Knoten funktioniert. Shim den Datenverkehr zwischen dem Modul und dem globalen Speicherplatz und browsen Sie ihn schließlich ab, und es funktioniert nur im Browser.

1
Cool Blue

Ich habe WordPress verwendet. Daher war ich gezwungen, die jQuery des WordPress-Kerns zu verwenden, die in window object verfügbar ist.

Es wurde ein slick() nicht definierter Fehler generiert, als ich versuchte, slick() plugin von npm zu verwenden. Das Hinzufügen von browserify-shim hat nicht viel geholfen.

Ich grub ein bisschen und fand heraus, dass require('jquery') nicht immer konsistent war.

In meiner Theme-Javascript-Datei wurde die Jquery des WordPress-Kerns aufgerufen.

Im slick-Jquery-Plugin wurde jedoch die neueste Jquery aus Knotenmodulen aufgerufen.

Schließlich konnte ich es lösen. Teilen Sie also die Konfiguration package.json und gulpfile.

package.json:

"browserify": { "transform": [ "browserify-shim" ] }, "browserify-shim": { "jquery": "global:jQuery" },

gulpfile.babel.js:

browserify({entries: 'main.js', extensions: ['js'], debug: true}) .transform(babelify.configure({ presets: ["es2015"] })) .transform('browserify-shim', {global: true})

Das Umwandeln von 'browserify-shim' war ein wichtiger Teil, den ich zuvor vermisst hatte. Ohne es wäre browserify-shim nicht konsistent.

0
Jashwant