Załaduj i zużyj starsze moduły JS (np. IIFE) poprzez import modułów ES6

9

Mam funkcje IIFE dla niektórych kodów bibliotecznych w starszej aplikacji, która musi działać dla IE10 + (brak ładowania modułu ES6 itp.).

Zaczynam jednak opracowywać aplikację React, która będzie korzystała z ES6 i TypeScript, i chcę ponownie użyć kodu, który już mam, bez kopiowania plików. Po krótkich badaniach odkryłem, że chciałbym użyć wzorca UMD, aby umożliwić tym plikom biblioteki działanie zarówno jako <script src=*>importowania, jak i umożliwić aplikacji React importowanie ich poprzez ładowanie modułu ES6.

Wymyśliłem następującą konwersję:

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

do

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

Umożliwi to ładowanie za pomocą Import Utils from './Utils.js'polecenia, a także umożliwi wstawianie go za pomocą znacznika skryptowego<script src='Utils.js'></script>

Jednak niektóre z moich IIFE używają innych IIFE jako zależności (źle wiem, ale rzeczywistość).

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

Jeśli prawidłowo włączyć RandomHelperi Utilsdo plików, które mogą być importowane The React aplikacja nie jest kompatybilna z tą techniką. Robienie po prostu

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

nie działa, ponieważ uważam, że Utils nie ma zasięgu okna. Załaduje się bez problemu, ale RandomHelper.DoThing()rzuci, że Utils nie jest zdefiniowane.

W starszej aplikacji

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

działa bezbłędnie.

Jak mogę sprawić, by RandomHelper mógł używać Utils w aplikacji React, utrzymując kompatybilność z IE i ES5, ale nadal działając. Być może w jakiś sposób ustawiasz zmienną window / global?

PS: Rozumiem, że celem ładowania modułu ES6 jest radzenie sobie z zależnościami, a moje istniejące IIFE nie są idealne. Planuję w końcu zmienić klasy es6 i lepszą kontrolę zależności, ale na razie chcę korzystać z tego, co jest dostępne, bez przepisywania

ParoX
źródło
4
React używa jsx i żadna przeglądarka nie obsługuje jsx, więc i tak potrzebujesz babel, nie ma sensu nie używać instrukcji importu w projekcie reagowania, ponieważ i tak musisz używać babel. React również odchodzi od OO, więc powiedzenie, że chcesz używać klas ES6 z reakcją, nie ma większego sensu. Nadal obsługuje klasy, ale zmierza w kierunku komponentów funkcjonalnych.
HMR
Tak, mam babel / webpack i używam frameworka CRA.
ParoX,
W node.js mogę również użyć global.Utils = (func ... i var Utils = global.Utils; następnie.
Tom
Mogę pocierać to miłością z komponentu internetowego za pomocą szablonów, które wyobrażam sobie w zależności od tego, co potrzebujesz wspierać.
Chris W.
1
Myślę, że naprawdę powinieneś przejść do składni importu ES6 dla wszystkiego, czego chcesz używać w nowej aplikacji, i przetransponować ją z powrotem do formatu IIFE (lub po prostu UMD) dla starszej aplikacji. Nie musisz przepisywać całego pliku, ale napraw deklaracje zależności.
Bergi

Odpowiedzi:

2

Najpierw usuńmy to, funkcje modułu, jeśli nie zostaną wyraźnie wyeksportowane, są prywatnie objęte zakresem modułu definiującego . Nie da się obejść tego faktu. Istnieją jednak opcje obejścia, które możesz rozważyć.

1. Przyjęcie minimalnej modyfikacji starszego kodu jest dopuszczalne

Dzieło wokół z minimalnymi zmianami w kodzie starszych byłoby po prostu dodać Utilsi RandomHelperdo windowobiektu. Na przykład zmień var Utils = (...)();na window.Utils = (...)();. W związku z tym obiekt będzie dostępny z obiektu globalnego zarówno przez starsze kody (ładowane przez import), jak i nowszą bazę kodów.

2. Zakładając, że absolutnie żadna modyfikacja starszego kodu nie jest tolerowana

Nowy moduł ES6 powinien zostać utworzony jako serwer proxy do ładowania starszych skryptów:

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

Wreszcie, można importować Utilsi RandomHelperod legacy-main.jskiedy wymagane:

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

Utils.MyFunction()
RandomHelper.DoThing()
Igwe Kalu
źródło
0

Jednym z podejść, które można rozważyć, jest jakaś forma wstrzykiwania zależności : poproś aplikację React, aby otrzymywała RandomHelper lub niektóre jego właściwości ze świata zewnętrznego. Następnie możesz go usunąć, gdy będziesz gotowy do przecięcia przewodu.

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
źródło