Carregar e consumir módulos JS herdados (por exemplo, IIFEs) via importação do módulo ES6

9

Eu tenho funções IIFE para parte do código da biblioteca em um aplicativo herdado que precisa funcionar para o IE10 + (sem carregamento do módulo ES6, etc.).

No entanto, estou começando a desenvolver um aplicativo React que usará o ES6 e o ​​TypeScript e quero reutilizar o código que já tenho sem duplicar os arquivos. Após algumas pesquisas, descobri que desejaria usar um padrão UMD para permitir que esses arquivos de biblioteca funcionassem como <script src=*>importações e para permitir que o aplicativo React os importasse através do carregamento do módulo ES6.

Eu vim com a seguinte conversão:

var Utils = (function(){
  var self = {
    MyFunction: function(){
      console.log("MyFunction");
    }
  };
  return self;
})();

para

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (factory((global.Utils = {})));
}(this, (function (exports) { 
  exports.MyFunction = function(){
      console.log("MyFunction");
    };
})));

Isso permitirá carregar via Import Utils from './Utils.js'comando e também pode ser inserido usando uma tag de script<script src='Utils.js'></script>

No entanto, alguns dos meus IIFE usam outros IIFE como uma dependência (mal sei, mas é uma realidade).

var Utils = Utils; // Used to indicate that there is dependency on Utils
var RandomHelper = (function(){
  var self = {
    DoThing: function(){
      Utils.MyFunction();
    }
  };
  return self;
})();

Se corretamente transformar RandomHelpere Utilsem arquivos que podem ser importados, a Reagir aplicativo não é compatível com esta técnica. Fazendo simplesmente

Import Utils from './Utils.js'
Import RandomHelper from './RandomHelper.js'

não funciona porque acredito que o Utils não tem escopo de janela. Ele será carregado sem problemas, mas RandomHelper.DoThing()mostrará que o Utils não está definido.

No aplicativo herdado

<script src='Utils.js'></script>
<script src='RandomHelper.js'></script>

funciona perfeitamente.

Como o RandomHelper pode usar Utils em um aplicativo React, mantendo-o compatível com o IE e ES5, mas ainda trabalhando em reação. Talvez, de alguma forma, definir uma janela / variável global?

PS: Entendo que o objetivo do carregamento do módulo ES6 é lidar com dependências e meus IIFEs existentes não são ideais. Pretendo, eventualmente, mudar de classe es6 e melhor controle de dependência, mas, por enquanto, quero usar o que está disponível para ficar sem reescrever

ParoX
fonte
4
O React usa o jsx e nenhum navegador entende o jsx; portanto, você precisa do babel de qualquer maneira, não há como não usar instruções de importação em um projeto do reag porque você precisa usar o babel. O React também está se afastando do OO, portanto, dizer que você deseja usar as classes ES6 com o Reac não faz muito sentido. Ele ainda suporta classes, mas está se movendo em direção a componentes funcionais.
HMR
Sim, eu tenho o babel / webpack e uso a estrutura CRA.
ParoX 20/10/19
Em node.js I também pode usar global.Utils = (func ... e var Utils = global.Utils;., Em seguida,
Tom
Poderia esfregar algum amor de componente da web nele com alguns stenciljs que eu imagino dependendo do que você precisa oferecer suporte.
22619 Chris W.
11
Eu acho que você realmente deve mudar para a sintaxe de importação do ES6 para tudo o que deseja usar no seu novo aplicativo e transpilar de volta para o formato IIFE (ou simplesmente UMD) para o aplicativo herdado. Você não precisa reescrever o arquivo completo, mas corrija as declarações de dependência.
Bergi 23/10/19

Respostas:

2

Vamos resolver isso primeiro, os recursos do módulo, se não forem explicitamente exportados, têm um escopo particular para o módulo de definição . Você não pode contornar esse fato. Mas existem opções de solução alternativa que você pode considerar.

1. Supondo que uma modificação mínima do código legado seja aceitável

Uma solução alternativa com alterações mínimas no seu código legado seria simplesmente adicionar Utilse RandomHelperao windowobjeto. Por exemplo, mude var Utils = (...)();para window.Utils = (...)();. Conseqüentemente, o objeto será acessível a partir do objeto global pelos códigos herdados (carregados via import) e pela base de código mais recente.

2. Supondo que absolutamente nenhuma modificação no código legado possa ser tolerada

Um novo módulo ES6 deve ser criado como proxy para carregar os scripts herdados:

// ./legacy-main.js

const utilsScript = await fetch( './Utils.js' )
const randomHelperScript = await fetch( './RandomHelper.js' )

const utilsScriptText = await utilsScript.text()
const randomHelperScriptText = await randomHelperScript.text()

// Support access to `Utils` via `import` 
export const Utils = Function( `${utilsScriptText}; return Utils;` )()
// Additionally support access via global object 
Object.defineProperty(window, 'Utils', { value: Utils })

// Support access to `RandomHelper` via `import`
// Note that `Utils` which is a dependency for `RandomHelper` ought to be explicitly injected
// into the scope of execution of `RandomHelper`.
export const RandomHelper = Function( 'Utils', `${randomHelperScriptText}; return RandomHelper;` )( Utils )
// Additionally support access via global object 
Object.defineProperty(window, 'RandomHelper', { value: RandomHelper })

Por fim, você pode importar Utilse RandomHelperde legacy-main.jsquando necessário:

import { Utils, RandomHelper } from './legacy-main.js'

Utils.MyFunction()
RandomHelper.DoThing()
Igwe Kalu
fonte
0

Uma abordagem que você poderia considerar é alguma forma de injeção de dependência : faça com que o aplicativo React receba o RandomHelper, ou algumas de suas propriedades, do mundo exterior. Depois, você pode removê-lo quando estiver pronto para cortar o cordão.

var Utils = (function(){
  var self = {
    MyFunction: function(name){
      return `Hello, ${name}!`;
    }
  };
  return self;
})();

var RandomHelper = (function(){
  var self = {
    DoThing: function(name){
      return Utils.MyFunction(name);
    }
  };
  return self;
})();

const ComponentOne = ({hello}) => {
  return <h1>{hello('ComponentOne')}</h1>;
}

const ComponentTwo = ({hello}) => {
  return <h2>{hello('ComponentTwo')}</h2>
}

const App = ({ExternalFunctions}) => {
  return (
    <header>
      <ComponentOne hello={ExternalFunctions.hello} />
      <ComponentTwo hello={ExternalFunctions.hello} />
    </header>
  )
}

ReactDOM.render(
  <App ExternalFunctions={{hello: RandomHelper.DoThing}} />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Marcus Vinícius Monteiro
fonte