What

按需加载是什么:单页面多路由的应用场景,按页面拆分成多个 js 模块,访问哪个路由就加载哪块代码,不加载其他不相干的代码。

Why

为了让用户拥有更好地体验,现在单页面应用(SPA)越来越多了。但随着业务规模越来越大(比如无线服务市场),页面也越来越多,原来通过 webpack 打包成「一个」js 的方式已经过于「重」了。 其实加载某个路由页面时并不需要其他页面冗余的代码,既 影响体积白费了流量 ,又 导致性能越来越差 。这时,做好按需加载就很有必要了。

How

1. 修改 webpack 配置

修改webpack.config.js,在 output 字段加上chunkFilename

output: {
    path: 'build',
    publicPath: !DEV ? gitVersion : '/build/',
    filename: '[name].js',
    chunkFilename: '[name].[chunkhash:5].chunk.js'
},
var execSync = require('child_process').execSync;
var gitBranch = execSync(`git symbolic-ref --short HEAD`)
    .toString()
    .trim();
var gitVersion = gitBranch.split('/')[1] || '';

2. 改造老的 routes

原来的路由(routes.js)可能是这样写的:

const Routers = (
    <Route
        path="/"
        component={Layout}
        onEnter={onRouteEnter}
        onChange={onRouteChange}
    >
        <IndexRedirect to="home" />
        <Route path="home" component={Index} spm={'a1z8w.8005165'} />
        <Route path="my" component={My} spm={'a1z8w.8005215'} />
        <Route path="search" component={Search} spm={'a1z8w.8005193'} />
        <Route
            path="serviceDetail"
            component={ServiceDetail}
            spm={'a1z8w.8005173'}
        />
    </Route>
);
export default Routes;

做如下改造:

const rootRoute = {
    path: '/',
    onEnter: onRouteEnter,
    onChange: onRouteChange,
    getComponents(location, callback) {
        require.ensure(
            [],
            function(require) {
                callback(null, require('./layout/layout').default);
            },
            'app'
        );
    },
    indexRoute: {
        onEnter: (nextState, replace) => replace('/home'),
    },
    getChildRoutes(location, callback) {
        require.ensure(
            [],
            function(require) {
                callback(null, [
                    require('./r/index/routes').default,
                    require('./r/my/routes').default,
                    require('./r/search/routes').default,
                    require('./r/serviceDetail/routes').default,
                ]);
            },
            'app'
        );
    },
};
export default rootRoute;
require.ensure(dependencies, callback, chunkName);

3. 为各个页面目录下创建子路由

以 index 路由为例,新建子路由/r/index/routes.js,文件内容:

const routes = {
    path: 'home',
    spm: 'a1z8w.8005165',
    getComponents(location, callback) {
        require.ensure(
            [],
            function(require) {
                callback(null, require('./index').default);
            },
            'index'
        );
    },
};
export default routes;

4. 修改 webpack 入口文件

app.js里增加以下代码:

// 配置运行时按需加载的静态资源url,本地/日常/线上
let url = window.location.origin;
let publicPath = __webpack_require__.p;
__webpack_public_path__ =
    url.includes('localhost') || url.includes('127.0.0.1')
        ? publicPath
        : url.includes('daily.taobao.net') || url.includes('waptest.taobao.com')
        ? `//g-assets.daily.taobao.net/sj/h5.qnservice/${publicPath}/`
        : `//g.alicdn.com/sj/h5.qnservice/${publicPath}/`;

Result - 优化结果

webpack 打包结果对比

浏览器加载情况对比

参考文章