Configurar un plugin genérico de jQuery con Browserify-shim?


Estoy usando browserify-shim y quiero usar un plugin genérico de jQuery. He revisado los documentos Browserify-shim varias veces y parece que no puedo entender lo que está pasando y/o cómo sabe dónde poner complementos, adjuntar al objeto jQuery, etc. Este es mi paquete.el archivo json se ve así:

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

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

De acuerdo con el ejemplo dado en la documentación browserify-shim, no quiero especificar una exportación porque este plugin (y la mayoría si no todos los plugins de jQuery) adjuntar en el objeto jQuery. A menos que esté haciendo algo mal anteriormente, no entiendo por qué no funciona (recibo un error que me dice que la función no está definida) cuando la uso. Véase a continuación:

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

Así que mi pregunta es, ¿cómo se configura un plugin genérico de jQuery (que se adhiere al objeto jQuery) con browserify y browserify-shim?

Author: Glen Selle, 2014-07-19

4 answers

Después de volver a visitar esto y probar algunas cosas más, finalmente envolví mi cabeza alrededor de lo que browserify-shim está haciendo y cómo usarlo. Para mí, había un principio clave que tenía que entender antes de que finalmente entendiera cómo usar browserify-shim. Básicamente, hay dos formas de usar browserify-shim para dos casos de uso diferentes: exponer y shimming.

Antecedentes

Supongamos que desea simplemente colocar una etiqueta de script en su marcado (por razones de prueba o rendimiento, como almacenamiento en caché, CDN y similares). Al incluir una etiqueta de script en el marcado, el navegador golpeará el script, lo ejecutará y lo más probable es que adjunte una propiedad al objeto window (también conocido como global en JS). Por supuesto, esto se puede acceder haciendo myGlobal o window.myGlobal. Pero hay un problema con cualquiera de las sintaxis. No sigue la especificación de CommonJS, lo que significa que si un módulo comienza a soportar la sintaxis de CommonJS (require()), no podrá aprovecharla.

El Solución

Browserify-shim le permite especificar un global que le gustaría "expuesto" a través de la sintaxis CommonJS require(). Recuerda, puedes hacer var whatever = global; o var whatever = window.global; pero NO puedes hacer var whatever = require('global') y esperar que te dé el lib/módulo correcto. No se confunda sobre el nombre de la variable. Podría ser cualquier cosa arbitraria. Esencialmente estás haciendo una variable global una variable local. Suena estúpido, pero es el triste estado de JS en el navegador. Una vez más, la esperanza es que una vez que una lib apoya CommonJS sintaxis nunca se adjuntará a través de un objeto global en la ventana. Lo que significa que DEBE usar la sintaxis require() y asignarla a una variable local y luego usarla donde la necesite.

Nota: Encontré el nombre de variables ligeramente confuso en los documentos/ejemplos de browserify-shim. Recuerde, la clave es que desea incluir un lib como si fuera un módulo CommonJS que se comporta correctamente. Así que lo que terminas haciendo es decirle a browserify que cuando requieres myGlobal require('myGlobal') en realidad solo quiero que se le dé la propiedad global en el objeto window que es window.myGlobal.

De hecho, si tienes curiosidad en cuanto a lo que la función require realmente hace, es bastante simple. Esto es lo que sucede bajo el capó:

var whatever = require('mygGlobal');

Se convierte...

var whatever = window.mygGlobal;

Exponiendo

Así que con ese fondo, veamos cómo exponemos un módulo/lib en nuestra configuración browserify-shim. Básicamente, le dices a browserify-shim dos cosas. El nombre con el que quieres que sea accesible cuando llames require() y el global que debería esperar encontrar en el objeto window. Así que aquí es donde entra en juego la sintaxis global:*. Veamos un ejemplo. Quiero colocar jquery como una etiqueta de script en index.html para obtener un mejor rendimiento. Esto es lo que tendría que hacer en mi configuración (esto sería en el paquete.json o un archivo de configuración JS externo):

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

Así que esto es lo que eso significa. He incluido jQuery en otro lugar (recuerde, browserify-shim no tiene idea de dónde ponemos nuestra etiqueta, pero no es necesario saber), pero todo lo que quiero es que se me dé la propiedad $ en el objeto window cuando requiera el módulo con el parámetro de cadena "jquery". Para ilustrar más. También podría haber hecho esto:

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

En este caso, tendría que pasar "thingy" como parámetro a la función require para obtener una instancia del objeto jQuery de vuelta (del que solo está obteniendo jQuery window.$):

var $ = require('thingy');

Y sí, de nuevo, el nombre de la variable podría ser cualquier cosa. No hay nada especial acerca de $ siendo la misma que la propiedad global $ que usa la biblioteca jQuery. Aunque tiene sentido usar el mismo nombre para evitar confusiones. Esto termina haciendo referencia a la propiedad $ en el objeto window, seleccionada por el valor global:$ en el objeto browserify-shim en package.json.

Shimming

Ok, así que casi cubre la exposición. La otra característica principal de browserify-shim es shimming. Entonces, ¿qué es eso? Shimming hace esencialmente lo mismo que exponer excepto en lugar de incluir la lib o el módulo en el marcado HTML con algo como una etiqueta de script, le dices a browserify-shim dónde agarrar el archivo JS localmente. No es necesario usar la sintaxis global:*. Así que volvamos a nuestro ejemplo de jQuery, pero esta vez supongamos que no estamos cargando jQuery desde una CDN, sino simplemente agrupándolo con todos los archivos JS. Así que así es como se vería la configuración:

"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": "$"
},

Esta configuración le dice a browserify-shim que cargue jQuery desde la ruta local especificada y luego grab la propiedad window del objeto window y devuelve que cuando se requiere jQuery con un parámetro de cadena a la función require de "jquery". Una vez más, para fines ilustrativos, también puede cambiar el nombre de esto a cualquier otra cosa.

"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": "$"
},

Que podría ser requerido con:

var whatever = require('thingy');

Recomiendo revisar los documentos de browserify-shim para obtener más información sobre la sintaxis de mano larga usando la propiedad exports y también la propiedad depends que le permite decirle a browserify-shim si una lib depende de otro lib/módulo. Lo que he explicado aquí se aplica a ambos. Espero que esto ayude a otros que luchan por entender lo que browserify-shim está haciendo realmente y cómo usarlo.

Anónimo Shimming

Anónimo shimming es una alternativa a browserify-shim que le permite transformar libs como jQuery en módulos UMD usando browserify --standalone opción.

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

Si lo sueltas en una etiqueta de script, este módulo agregará jQuery al objeto window como thingy. Por supuesto también podría ser $ o lo que quieras.

Sin embargo, si es requireed en su paquete de aplicaciones browserifie'd, var $ = require("./dist/jquery-UMD.js");, tendrá jQuery disponible dentro de la aplicación sin agregarlo al objeto window.

Este método no requiere browserify-shim y explota la conciencia CommonJS de jQuery donde busca un objeto module y pasa una bandera noGlobal a su fábrica que le dice que no se adhiera al objeto window.

 97
Author: Glen Selle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-12 15:09:01

Para todos los que buscan un ejemplo concreto: {[17]]}

El siguiente es un ejemplo de los archivos package.json y app.js para un complemento de jQuery que se adjunta a la jQuery/$ objeto, por ejemplo: $('div').expose(). No quiero que jQuery sea una variable global (window.jQuery) cuando lo requiera, por eso jQuery se establece en 'exports': null. Sin embargo, debido a que el plugin está esperando un objeto global jQuery al que puede adjuntarse, debe especificarlo en la dependencia después del nombre de archivo: ./jquery-2.1.3.js:jQuery. Además tú necesidad de exportar realmente el jQuery global cuando se utiliza el plugin, incluso si usted no quiere, porque el plugin no funcionará de otra manera (al menos este en particular).

Paquete.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();
  });
});

En el ejemplo anterior no quería que mi código estableciera ningún global, pero tuve que hacerlo temporalmente, para hacer que el plugin funcionara. Si solo necesita jQuery, puede hacer esto y no necesita ninguna solución: var my_jQuery = require('./jquery-2.1.3.js'). Si usted está bien con su jQuery está expuesto como global, entonces puede modificar el ejemplo anterior package.json de la siguiente manera:

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

Espero que eso ayude a algunas personas, que estaban buscando ejemplos concretos (como yo, cuando encontré esta pregunta).

 12
Author: gottlike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-11 13:44:09

Solo para completar, aquí hay un método que explota la conciencia CommonJS de jQuery para evitar tener que preocuparse por contaminar el objeto window sin necesidad de calzar.

Características

  1. jQuery incluido en el paquete
  2. plugin incluido en el paquete
  3. no hay contaminación del objeto window

Config

{[17] {} En[32]}./paquete.json, agregue un nodo browser para crear alias para las ubicaciones de recursos. Esto es puramente para mayor comodidad, no hay necesidad de calzar realmente nada porque no hay comunicaciones entre el módulo y el espacio global (etiquetas de script) .
{
  "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"
  }
}

Método

  • Debido a que jQuery es consciente de CommonJS en estos días, detectará la presencia del objeto module proporcionado por browserify y devolverá una instancia, sin agregarla al objeto window.
  • En la aplicación, require jquery y agregarlo al objeto module.exports (junto con cualquier otro contexto que necesita ser compartida).
  • Agregue una sola línea al inicio del plugin para requerir que la aplicación acceda a la instancia de jQuery que creó.
  • En la aplicación, copie la instancia de jQuery en $ y use jQuery con el complemento.
  • Navegue por la aplicación, con las opciones predeterminadas, y coloque el paquete resultante en una etiqueta de script en su HTML.

Código

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();
            })
    );
});

En la parte superior del plugin

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

En el HTML

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

Antecedentes

El patrón utilizado por jQuery es llamar a su fábrica con una bandera noGlobal si detecta un entorno CommonJS. No añadirá una instancia al objeto window y devolverá una instancia como siempre.

El contexto CommonJS es creado por browserify por defecto. A continuación se muestra un extracto simplificado del paquete que muestra la estructura del módulo jQuery. He eliminado el código que trata con el manejo isomórfico del objeto window por el bien de claridad.

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;
    }) );
}, {}]

El mejor método que encontré es hacer que las cosas funcionen en el sistema de módulos de nodo y luego funcionará cada vez después de browserify-ing.
Simplemente use jsdom para calzar el objeto window de modo que el código sea isomorfo. Luego, solo concéntrate en hacer que funcione en node. Luego, recorta cualquier tráfico entre el módulo y el espacio global y finalmente browserify y solo funcionará en el navegador.

 1
Author: Cool Blue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-18 12:12:46

Estaba usando wordpress. Por lo tanto, me vi obligado a usar el jQuery del núcleo de wordpress, disponible en el objeto window.

Estaba generando slick() error no definido, cuando traté de usar slick() plugin de npm. Agregar browserify-shim no ayudó mucho.

Investigué un poco y descubrí que require('jquery') no siempre era consistente.

En mi archivo javascript de tema, estaba llamando al jquery del núcleo de wordpress.

Pero, en slick plugin jquery estaba llamando a la última jquery desde módulos de nodo.

Finalmente, pude resolverlo. Entonces, compartiendo la configuración package.json y gulpfile.

Paquete.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})

Hacer transform 'browserify-shim' fue una parte crucial, me faltaba antes. Sin ella browserify-shim no era consistente.

 0
Author: Jashwant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-22 12:00:16