react-router4/typescript/webpack4 下的组件按需加载实现和失效问题

2,255 阅读2分钟

在 react-router4/typescript/webpack4 技术栈下,从头实现组件按需加载

react-router

在 webpack 文档中这么说的:

import('path/to/module') -> Promise 动态地加载模块。调用 import() 之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中。

所以在react-router中,我们常用react-loadable实现基于import的路由按需加载配置,like this:

import Loadable from 'react-loadable';
...
<Route path="/" exact component={Loadable({
    component={() => import('@/page/home')}
})} />} />

但其实 loadable 的原理很简单,ts下实现一个:


interface AsyncComponentModuleType {
  default: typeof React.Component
}

export default (getter: Function) => {
  return class extends Component<any, {
    component: null | typeof React.Component
  }> {
    constructor(props: any) {
      super(props);
      this.state = {
        component: null
      };
    }
    componentDidMount() {
      getter().then((res: AsyncComponentModuleType) => {
        this.setState({
          component: res.default
        });
      });
    }
    render() {
      const C = this.state.component;
      return C ? <C {...this.props} /> : null;
    }
  };
};

关于Component的类型

如果异步回来的component类型指定不对,会报错:

JSX element type C does not have any construct or call signatures

也就是 C 的类型不能放在<>中,我们常见的放在<>中的是JSX.IntrinsicElements,参见react的index.d.ts。但这是个JSX标签的集合,用在这里虽然能绕过类型检查,但不合理。 因此我们参考:What does the error “JSX element type '…' does not have any construct or call signatures” mean?typeof React.Component

webpack4 配置

loader

如果代码里还有js(不全是ts),在webpack4中要对babelloader进行如下配置,支持js的import打包

presets: ['@babel/preset-react'],
plugins: ['@babel/plugin-syntax-dynamic-import']

splitChunks

在webpack4中splitChunks默认是开启的,不用手动改,当然要深入了解下 splitChunks,可以看这篇:《一步一步的了解webpack4的splitChunk插件》 。 当然如果已经动了splitChunks,需要注意下这个配置

  • chunks:默认是 async,配成 async 或者 all

打包文件名

output.chunkFilename的配置会影响打包产出:

output: {
    chunkFilename: '[name].chunk.js',
}

另外webpack还支持通过注释指定输出文件名:

<Route path="/" exact component={getAsyncComponent(() => import(/* webpackChunkName: 'home' */'@/pages/Home/home'))} />} />

最终打包出的东西长这样:

Asset       Size  Chunks                    Chunk Names
home.chunk.js  622 bytes   4  [emitted]         p-home
home.chunk.css  24.7 KiB   5  [emitted]         p-home

typescript 配置

到这里,前面说的都是废话,因为一搜就能搜到一大把,写这个主要是记录关于ts的配置

其实在配置完上面部分后,我的按需加载是不能用的,实际上虽然任何功能都没受影响,但并没有给我拆包。这块开始没想到是 ts 的问题,查了好多地方。 直到我找到这篇博客:《Code-Splitting a TypeScript Application with import() and webpack》

"module": "esnext",
"moduleResolution": "node",

意思是我们不能给import弄死,要按包含import模块化方案的语法打包,这样就能声明语法并留着import,给webpack做分片打包入口用。