使用react-router时,需要在开始异步获取组件/获取完成/组件是否需要渲染等时刻加入hock(使用发布订阅发送消息),按需加载写法本来就比较繁琐,加上这些hock,更加繁琐了:1
2
3
4
5
6
7
8
9
10
11{
    path: '/dev/components',
    getComponent: (nextState, cb) => {
        startFetchingComponent();
        require.ensure([], (require) => {
            if (!shouldComponentMount(nextState)) return;
            endFetchingComponent();
            cb(null, connectComponent(require('./sys-component/SysComponent')));
        });
    },
},
其中除了path: '/dev/components',和 './sys-component/SysComponent' 其他信息,各个route写法都一样,就算复制粘贴,相同代码也占了绝大部分。
注:其中startFetchingComponent shouldComponentMount endFetchingComponent 方法会使用发布订阅发送消息,connectComponent用来链接redux,这些hock是贴近我的具体业务加的,具体情况,随机应变。
想要的结果
简化写法如下就能实现如上功能:1
2
3
4{
    path: '/dev/components',
    asyncComponent: './sys-component/SysComponent',
},
自定义routes-loader实现
webpack 自定义loader请参考:如何开发一个 Webpack Loader1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32function getComponentString(componentPath) {
    return "getComponent: (nextState, cb) => {"
        + "startFetchingComponent();"
        + "require.ensure([], (require) => {"
        + "if (!shouldComponentMount(nextState)) return;"
        + "endFetchingComponent();"
        + "cb(null, connectComponent(require('" + componentPath + "')));"
        + "});"
        + "},";
}
module.exports = function (source, other) {
    this.cacheable();
    var routesStrTemp = source;
    var patt = /asyncComponent:[ ]*['"]([^'"]+)['"][,]/gm;
    var isRoutes = false;
    var block = null;
    while ((block = patt.exec(source)) !== null) {
        isRoutes = block[0] && block[1];
        if (isRoutes) {
            routesStrTemp = routesStrTemp.replace(block[0], getComponentString(block[1]));
        }
    }
    if (isRoutes) {
        routesStrTemp = "import connectComponent from 'src/utils/connectComponent.js';\n"
            + "import {startFetchingComponent, endFetchingComponent, shouldComponentMount} from 'src/utils/route-utils';"
            + routesStrTemp;
        this.callback(null, routesStrTemp, other);
    } else {
        this.callback(null, source, other);
    }
};
webpack 配置
由于这是对原routes.js文件的修改,故routes-loader加在preLoaders中,在loaders执行之前,做好这些转换工作。1
2
3
4
5
6
7
8
9
10preLoaders: [
    ...
    {
        test: /routes\.js$/,
        loader: path.join(__dirname, './routes-loader'), // 这里要使用绝对路径
        include: projectRoot,
        exclude: /node_modules/
    }
    ...
],