Webpack ProvidePlugin vs externos?

84

Estoy explorando la idea de usar Webpack con Backbone.js .

Seguí la guía de inicio rápido y tengo una idea general de cómo funciona Webpack, pero no tengo claro cómo cargar la biblioteca de dependencias como jquery / backbone / underscore.

¿Deberían cargarse externamente <script>o es algo que Webpack puede manejar como el shim de RequireJS?

De acuerdo con el documento webpack: shimming modules , ProvidePluginy externalsparece estar relacionado con esto (también lo está el bundle!cargador en alguna parte) pero no puedo averiguar cuándo usar cuál.

Gracias

Enrique
fuente

Respuestas:

153

Ambos son posibles: puede incluir bibliotecas con <script>(es decir, usar una biblioteca de un CDN) o incluirlas en el paquete generado.

Si lo carga a través de una <script>etiqueta, puede usar la externalsopción para permitir escribir require(...)en sus módulos.

Ejemplo con biblioteca de CDN:

<script src="https://code.jquery.com/jquery-git2.min.js"></script>

// the artifial module "jquery" exports the global var "jQuery"
externals: { jquery: "jQuery" }

// inside any module
var $ = require("jquery");

Ejemplo con biblioteca incluida en el paquete:

copy `jquery-git2.min.js` to your local filesystem

// make "jquery" resolve to your local copy of the library
// i. e. through the resolve.alias option
resolve: { alias: { jquery: "/path/to/jquery-git2.min.js" } }

// inside any module
var $ = require("jquery");

El ProvidePluginpuede asignar a los módulos de variables (gratis). Así se podría definir: "Cada vez que utilice la variable (gratis) xyzdentro de un módulo (webpack) debe fijar xyza require("abc")".

Ejemplo sin ProvidePlugin:

// You need to require underscore before you can use it
var _ = require("underscore");
_.size(...);

Ejemplo con ProvidePlugin:

plugins: [
  new webpack.ProvidePlugin({
    "_": "underscore"
  }) 
]

// If you use "_", underscore is automatically required
_.size(...)

Resumen:

  • Biblioteca de CDN: use <script>etiqueta y externalsopción
  • Biblioteca del sistema de archivos: incluye la biblioteca en el paquete. (Tal vez modifique las resolveopciones para encontrar la biblioteca)
  • externals: Hacer que las vars globales estén disponibles como módulo
  • ProvidePlugin: Hacer que los módulos estén disponibles como variables libres dentro de los módulos
Tobias K.
fuente
Debe agregarse newantes de webpack.ProvidePlugin webpack.github.io/docs/list-of-plugins.html
MK Yung
¿Por qué no usar simplemente el cargador de scripts? Esto es mucho más fácil, como lo explicó
@dtothefp
Si mi archivo webpack.config está en una carpeta llamada javascript y dentro de ella tengo una carpeta llamada vendedor con mi archivo jquery. debe el camino no ser. resolver: {alias: {jquery: "proveedor / jquery-1.10.2.js"}}. Todavía no me funciona con el alias
yo-yo
3
Simplemente pase una ruta absoluta a la opción de alias. Si pasa una ruta relativa, es relativa a la ubicación del requerimiento / importación en el paquete web 1. En el paquete web 2 es relativo al archivo webpack.config.js resp. la opción de contexto.
Tobias K.
@TobiasK. Una ruta absoluta no coopera con las exportaciones predeterminadas. Obtengo un objeto en {__esModule: true, default: MY_DEFAULT_EXPORT}lugar de MY_DEFAULT_EXPORTen los archivos.
mgol
25

Algo interesante a tener en cuenta es que si usa el ProvidePluginen combinación con la externalspropiedad, le permitirá haber jQuerypasado al cierre del módulo webpack sin tener que hacerlo explícitamente require. Esto puede ser útil para refactorizar código heredado con una gran cantidad de referencias de archivos diferentes $.

//webpack.config.js
module.exports = {
  entry: './index.js',
  output: { 
    filename: '[name].js' 
  },
  externals: {
    jquery: 'jQuery'
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
    })
  ]
};

ahora en index.js

console.log(typeof $ === 'function');

tendrá una salida compilada con algo como a continuación pasado al webpackBootstrapcierre:

/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    /* WEBPACK VAR INJECTION */(function($) {
        console.log(typeof $ === 'function');

    /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))

/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {

    module.exports = jQuery;

/***/ }
/******/ ])

Por lo tanto, puede ver que $hace referencia a la ventana global / jQueryde la CDN, pero se pasa al cierre. No estoy seguro de si se trata de una funcionalidad prevista o un truco afortunado, pero parece funcionar bien para mi caso de uso.

dtothefp
fuente
no necesitaba ninguno de los complementos si no lo iba a require/importutilizar. $simplemente funcionaría porque alcanzará el alcance global sin importar qué. El ProviderPluginrequiere analizar el AST así que es un plugin caro y se sumará a su tiempo de construcción notablemente. Entonces es básicamente un desperdicio.
faceyspacey.com
@dtohefp esta respuesta es un regalo del cielo. ¿Puede explicar por qué ProvidePlugindevolvió un objeto como, a myModule.defaultmenos que agregue el módulo a los externos? Nunca supe que habría una relación directa.
Slbox
11

Sé que esta es una publicación antigua, pero pensé que sería útil mencionar que el cargador de scripts del paquete web también puede ser útil en este caso. De los documentos del paquete web:

"script: ejecuta un archivo JavaScript una vez en el contexto global (como en la etiqueta de script), los requisitos no se analizan".

http://webpack.github.io/docs/list-of-loaders.html

https://github.com/webpack/script-loader

He encontrado esto particularmente útil al migrar procesos de compilación más antiguos que combinan archivos de proveedores JS y archivos de aplicaciones. Una advertencia es que el cargador de scripts parece funcionar solo a través de la sobrecarga require()y no funciona hasta donde puedo decir al estar especificado en un archivo webpack.config. Aunque, muchos argumentan que la sobrecarga requirees una mala práctica, puede ser bastante útil para combinar el proveedor y el script de la aplicación en un paquete y, al mismo tiempo, exponer JS Globals que no es necesario incluir en paquetes web adicionales. Por ejemplo:

require('script!jquery-cookie/jquery.cookie');
require('script!history.js/scripts/bundled-uncompressed/html4+html5/jquery.history');
require('script!momentjs');

require('./scripts/main.js');

Esto haría que $ .cookie, History y moment estén disponibles globalmente dentro y fuera de este paquete, y agruparían estas librerías de proveedores con el script main.js y todos sus requirearchivos d.

Además, es útil con esta técnica:

resolve: {
  extensions: ["", ".js"],
  modulesDirectories: ['node_modules', 'bower_components']
},
plugins: [
  new webpack.ResolverPlugin(
    new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
   )
]

que está usando Bower, verá el mainarchivo en cada requirepaquete de bibliotecas d.json. En el ejemplo anterior, History.js no tiene un mainarchivo especificado, por lo que la ruta al archivo es necesaria.

dtothefp
fuente