分享:使用 webpack 的 require.context 实现路由“去中心化”管理

2017-05-05 16:29:10 +08:00
 wuchangming89

阅读须知:示例代码以 react-router V3 为例。其他路由同样适用,如:vue-router。同样其他的功能模块也可以用该思路进行相应的去中心化管理改造。
本文示例代码

一个项目中路由的变化过程

当你在开发一个大型单页面应用的时候,项目之初一般做法是所有的路由规则都维护在一个route.js的文件里。

// rootRoute.js
const rootRoute = {
    childRoutes: [
        {
            path: '/',
            component: AppLayout,
            childRoutes: [
                {
                    path: 'shop', // 购买详情页
                    component: Shop
                },
                {
                    path: 'order', // 订单页
                    component: Order
                }
                // ...
                // 少量其他路由
                // ...
            ]
        }
    ]
};

随着业务代码的增长路由很快会变成:

// rootRoute.js
const rootRoute = {
    childRoutes: [
        {
            path: '/',
            component: AppLayout,
            childRoutes: [
                {
                    path: 'shop', // 购买详情页
                    component: ShopLayout,
                    childRoutes: [
                        {
                            path: 'foodDetail',
                            component: FoodDetail
                        },
                        {
                            path: 'shoesDetail',
                            component: ShoesDetail
                        }
                        // 其他
                    ]
                },
                {
                    path: 'order', // 订单页
                    component: Order,
                    childRoutes: [
                        {
                            path: 'remark', //订单备注
                            component: Remark
                        },
                        {
                            path: 'invoice', //发票抬头
                            component: Invoice
                        },
                        {
                            path: 'payment', //付款页面
                            component: Payment
                        },
                        {
                            path: 'userValidation', //用户验证
                            component: UserValidation
                        },
                        {
                            path: 'chooseAddress', //选择地址
                            component: ChooseAddress,
                            childRoutes: [
                                {
                                    path: 'addAddress', //添加地址
                                    component: AddAddress,
                                    childRoutes: [
                                        {
                                            path: 'searchAddress', //搜索地址
                                            component: SearchAddress
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
                // ...
                // 大量新增路由
                // ...
            ]
        }
    ]
};

当路由变的越来越大,大到已经难以维护时。我们按照react-router 提供的思路,对路由按业务模块进行拆分。

// rootRoute.js
const rootRoute = {
    childRoutes: [
        {
            path: '/',
            component: AppLayout,
            childRoutes: [
                require('./modules/shop/route'), //购买详情页
                require('./modules/order/route'), // 订单页
                require('./modules/login/route'), // 登录注册页
                require('./modules/service/route'), // 服务中心
                // ...
                // 其他大量新增路由
                // ...
            ]
        }
    ]
};

按该方式拆分后,每个业务模块维护自身的路由配置。新增业务模块路由,只需要在总的 rootRoute 中引入该业务模块的路由即可(也就是加一行代码)。这个方案看来是已经接近完美了。但如果想达到连一行代码都不用加?实现彻彻底底的去中心化管理

require.context 是什么?

想要彻彻底底的实现去中心化管理我们需要使用到 require.context

webpack 官方文档的介绍require.context

简单说就是:有了 require.context,我们可以通过正则匹配引入相应的文件模块。

require.context(directory, useSubdirectories, regExp)

require.context 有三个参数:

使用 require.context 改造后的 rootRoute.js 文件

const rootRoute = {
    childRoutes: [
        {
            path: '/',
            component: AppLayout,
            childRoutes: (r => {
                return r.keys().map(key => r(key));
            })(require.context('./', true, /^\.\/modules\/((?!\/)[\s\S])+\/route\.js$/))
        }
    ]
};

优化后,新增一个业务模块,只要业务模块 route 文件遵循统一的目录结构。业务模块 route 就能被自动关联到 rootRoute 里。
查看示例代码

其他应用场景

这个思路可应用于其他想要实现"去中心化"管理的功能模块。

欢迎转载,转载请注明出处: https://github.com/wuchangming/blog/blob/master/docs/webpack/require-context-usage.md


11191 次点击
所在节点    JavaScript
6 条回复
AdamChrist
2017-05-05 17:15:57 +08:00
不错..感谢分享.
wuchangming89
2017-05-05 17:43:29 +08:00
@AdamChrist,THX
kimown
2017-05-06 08:56:58 +08:00
这个好用 感谢分享
otarim
2017-05-30 18:17:39 +08:00
不错的思路,之前使用的是 loader 的方式
zhjie
2017-11-20 10:30:42 +08:00
这路由就很灵性了
bonashen
2018-02-01 22:18:09 +08:00
确实不错,通常我是在 redux 中使用。
```js
const context = require.context('./', false, /\.js$/);
const keys = context.keys().filter(item => item !== './index.js' && !item.endsWith('.test.js'));
const regexp = /\.\/(\S+)\.js$/;

const reducers = keys.reduce((reducers, key) => {
const match = regexp.exec(key);
const result = reducers;
let reducer = context(key).default;
reducer = isFunction(reducer) ? reducer : combineReducers({ ...reducer });
result[match[1]] = reducer;
return result;
}, {});

export default reducers;

```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/359380

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX