How to fix this ES6 module circular dependency?

后端 未结 5 771
谎友^
谎友^ 2020-12-02 10:52

EDIT: for more background, also see the discussion on ES Discuss.


I have three modules A, B, and C. A and <

5条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-02 11:08

    I would recommend to use inversion of control. Make your C constructor pure by adding an A and a B parameter like this:

    // --- Module A
    
    import C from './C';
    
    export default class A extends C {
        // ...
    }
    
    // --- Module B
    
    import C from './C'
    
    export default class B extends C {
        // ...
    }
    
    // --- Module C
    
    export default class C {
        constructor(A, B) {
            // this may run later, after all three modules are evaluated, or
            // possibly never.
            console.log(A)
            console.log(B)
        }
    }
    
    // --- Entrypoint
    
    import A from './A';
    import B from './B';
    import C from './C';
    const c = new C(A, B);
    console.log('Entrypoint', C, c);
    document.getElementById('out').textContent = 'Entrypoint ' + C + ' ' + c;
    

    https://www.webpackbin.com/bins/-KlDeP9Rb60MehsCMa8u

    Update, in response to this comment: How to fix this ES6 module circular dependency?

    Alternatively, if you do not want the library consumer to know about various implementations, you can either export another function/class that hides those details:

    // Module ConcreteCImplementation
    import A from './A';
    import B from './B';
    import C from './C';
    export default function () { return new C(A, B); }
    

    or use this pattern:

    // --- Module A
    
    import C, { registerA } from "./C";
    
    export default class A extends C {
      // ...
    }
    
    registerA(A);
    
    // --- Module B
    
    import C, { registerB } from "./C";
    
    export default class B extends C {
      // ...
    }
    
    registerB(B);
    
    // --- Module C
    
    let A, B;
    
    const inheritors = [];
    
    export const registerInheritor = inheritor => inheritors.push(inheritor);
    
    export const registerA = inheritor => {
      registerInheritor(inheritor);
      A = inheritor;
    };
    
    export const registerB = inheritor => {
      registerInheritor(inheritor);
      B = inheritor;
    };
    
    export default class C {
      constructor() {
        // this may run later, after all three modules are evaluated, or
        // possibly never.
        console.log(A);
        console.log(B);
        console.log(inheritors);
      }
    }
    
    // --- Entrypoint
    
    import A from "./A";
    import B from "./B";
    import C from "./C";
    const c = new C();
    console.log("Entrypoint", C, c);
    document.getElementById("out").textContent = "Entrypoint " + C + " " + c;
    

    Update, in response to this comment: How to fix this ES6 module circular dependency?

    To allow the end-user to import any subset of the classes, just make a lib.js file exporting the public facing api:

    import A from "./A";
    import B from "./B";
    import C from "./C";
    export { A, B, C };
    

    or:

    import A from "./A";
    import B from "./B";
    import C from "./ConcreteCImplementation";
    export { A, B, C };
    

    Then you can:

    // --- Entrypoint
    
    import { C } from "./lib";
    const c = new C();
    const output = ["Entrypoint", C, c];
    console.log.apply(console, output);
    document.getElementById("out").textContent = output.join();
    

提交回复
热议问题