简体   繁体   中英

to add .default or not?: dynamic imports in react router 3 for webpack code-splitting

Recently I'm working on upgrading my project from webpack 3 (v. 3.3.0) to webpack 4 (v. 4.44.2). Compiling and building worked perfectly, but it turned out to render nothing on the screen.

After I compared the parameters passed to RouterContext between two projects of webpack 3 and of webpack 4, I narrowed down the root cause to dynamic imported components.

The following script is how I use react-router 3 (v. 3.0.5) getComponent to import component, which is workable before I upgrade to webpack 4:

const loadRoute = callback => module => callback(null, module);

const forceToReload = (err) => {
  Logger.error(err);
  window.location.reload();
};

export default (loadRoute, forceToReload) => (
  <Route name="Subscribe" path={appUtils.getRoutePath('/sample')}
         getComponent={(location, callback) => import('./index.jsx').then(loadRoute(callback)).catch(forceToReload)}/>
);

Fortunately, after days of trials & errors, I finally made my project render correctly. In webpack 4 version, I have to append .default to module in loadRoute as follows:

const loadRoute = callback => module => callback(null, module.default);

The Question is

Even though the problem is solved, I still wonder if someone can provide an explanation about when to add .default .
Does webpack 4 compile differently on dynamic import? Any ideas?

After days of research, I finally found the answer to my question, hoping I can somehow help those with the same question in the future.

Webpack official website ( Code-Splitting: Dynamic Import ) has a description as follows:

The reason we need default is that since webpack 4, when importing a CommonJS module, the import will no longer resolve to the value of module.exports, it will instead create an artificial namespace object for the CommonJS module.

Out of curiosity, I did some experiment logging out things I dynamic imports:

// exportObject.js
export default {a: 1, b: 2};
export const c = 3;

// exportString.js
export default "TEST_STRING";

// exportComponent.js
import React, {Component} from "react";
export default class exportComponent extends Component {
  constructor(props) {
    super(props);
    this.a = 1;
    this.b = 2;
  }
}

// exportFunction.js
export default () => ({a: 1, b: 2});
// index.js
componentDidMount() {
 import('./exportObject').then(module => {
    console.group('Object');
    console.warn('module', module);
    console.warn('module.a:', module.a);
    console.warn('module.b:', module.b);
    console.warn('module.default', module.default);
    console.warn('module.c', module.c);
    console.groupEnd();
  });

  import('./exportString').then(module => {
    console.group('String');
    console.warn('module', module);
    console.warn('module.default', module.default);
    console.groupEnd();
  });

  import('./exportComponent').then(module => {
    console.group('Component');
    console.warn('module', module);
    console.warn('module.default', module.default);
    console.groupEnd();
  });

  import('./exportFunction').then(module => {
    console.group('Function');
    try {
      console.warn('module()', module());
    } catch (e) {
      console.warn('module()', e);
    }
    try {
      console.warn('module.default()', module.default());
    } catch (e) {
      console.warn('module.default()', e);
    }
    console.groupEnd();
  });
}

This is the result from Webpack 3 Project:

在此处输入图像描述

While from Webpack 4 Project:

在此处输入图像描述

As you can see, non-objects have different results. This finding matches the description of this article (which is provided by webpack official)

It's not that problematic when exporting an object. But you'll get into trouble when using module.exports with non-objects.

I use export default in my code rather than module.exports , then why I still got the result? Actually, Babel@6 will do the transform as below: ( reference )

Babel@6 transforms the following file

export default 'foo'

into

'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = 'foo';

Now, things are crystal clear.

The packages I used was:

node: 11.15.0

babel-core: "^6.23.1",
babel-loader: "7.1.4",
webpack: "4.44.2",
webpack-cli: "^4.2.0",

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM