自定义webpack loader简化react-router按需加载写法

使用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 Loader

1
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
32
function 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
10
preLoaders: [
...
{
test: /routes\.js$/,
loader: path.join(__dirname, './routes-loader'), // 这里要使用绝对路径
include: projectRoot,
exclude: /node_modules/
}
...
],