Importing a lazy-loaded module with routes in a module with a route breaks routing

Actually, when creating a shared module, there is no need to care about modules which uses a little part of the shared module, because Angular's tree-shaking will keep only the used code from an imported module, and remove the rest.

I've prepared a minimal project to demonstrate it: https://github.com/youkouleley/Angular-treeshaking-demo

This project has two lazy modules: AModule and BModule. Both of these modules import SharedModule. SharedModule exports three components:

  • AComponent which is used in AModule only
  • BComponent which is used in BModule only
  • BothComponent which is used in AModule and BModule

Here is what you'll get when ng building this project in production mode and opening the 4-es2015.<hash>.js file:

(window.webpackJsonp = window.webpackJsonp || []).push([[4], {
      KImX: function (n, l, u) {
        "use strict";
        u.r(l);
        var t = u("8Y7J");
        class a {
          constructor() {}
          ngOnInit() {}
        }
        var r = u("phyl");
        class o {}
        var c = u("pMnS"),
        s = t.gb({
            encapsulation: 0,
            styles: [[""]],
            data: {}
          });
        function b(n) {
          return t.sb(0, [(n()(), t.ib(0, 0, null, null, 1, "p", [], null, null, null, null, null)), (n()(), t.rb(-1, null, ["a works!"]))], null, null)
        }
        function i(n) {
          return t.sb(0, [(n()(), t.ib(0, 0, null, null, 1, "app-a", [], null, null, null, b, s)), t.hb(1, 114688, null, 0, a, [], null, null)], function (n, l) {
            n(l, 1, 0)
          }, null)
        }
        var p = t.eb("app-a", a, i, {}, {}, []),
        e = u("gJxL"),
        f = u("SVse"),
        h = u("iInd"),
        d = u("PCNd");
        u.d(l, "AModuleNgFactory", function () {
          return v
        });
        var v = t.fb(o, [], function (n) {
            return t.ob([t.pb(512, t.j, t.T, [[8, [c.a, p, e.a]], [3, t.j], t.u]), t.pb(4608, f.i, f.h, [t.r, [2, f.o]]), t.pb(1073742336, f.b, f.b, []), t.pb(1073742336, h.l, h.l, [[2, h.q], [2, h.k]]), t.pb(1073742336, d.a, d.a, []), t.pb(1073742336, o, o, []), t.pb(1024, h.i, function () {
                  return [[{
                        path: "a",
                        component: a
                      }, {
                        path: "both",
                        component: r.a
                      }
                    ]]
                }, [])])
          })
      },
      PCNd: function (n, l, u) {
        "use strict";
        u.d(l, "a", function () {
          return t
        });
        class t {}
      },
      gJxL: function (n, l, u) {
        "use strict";
        var t = u("8Y7J"),
        a = u("phyl");
        u.d(l, "a", function () {
          return s
        });
        var r = t.gb({
            encapsulation: 0,
            styles: [[""]],
            data: {}
          });
        function o(n) {
          return t.sb(0, [(n()(), t.ib(0, 0, null, null, 1, "p", [], null, null, null, null, null)), (n()(), t.rb(-1, null, ["both works!"]))], null, null)
        }
        function c(n) {
          return t.sb(0, [(n()(), t.ib(0, 0, null, null, 1, "app-both", [], null, null, null, o, r)), t.hb(1, 114688, null, 0, a.a, [], null, null)], function (n, l) {
            n(l, 1, 0)
          }, null)
        }
        var s = t.eb("app-both", a.a, c, {}, {}, [])
      },
      phyl: function (n, l, u) {
        "use strict";
        u.d(l, "a", function () {
          return t
        });
        class t {
          constructor() {}
          ngOnInit() {}
        }
      }
    }
  ]);

Note that the BComponent from the SharedModule is missing from the AModule chunk. Sames goes for BModule chunk that excludes AComponent.

Also, note that this behavior is obtained when setting commonChunk to false in the build options. This option allows you to choose between:

  • false: Bundle the needed parts of the SharedModule directly into the lazy modules that imported it. Pro: Even loading time between lazy modules. Con: Some code from SharedModule is duplicated between lazy modules chunks, higher app size overall
  • true (default): Have a common chunk that contains the parts of the SharedModule which are used at least by two lazy modules (the rest is bundled into the lazy modules themselves). Pro: No duplicated code, lower app size overall. Con: The first lazy module is slower to load (it loads the common chunk even if the current route doesn't need it)

As a conclusion, Angular build provides optimizations for the SharedModule with commonChunk set either to true or false (depending on your context) you don't really have to worry about your SharedModule size. Thus, you don't have to try strange patterns like you did, with hybrid modules fulfilling the feature module role, and the shared module role at the same time.