Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

聊一聊Code Splitting #71

Open
jyzwf opened this issue Mar 23, 2019 · 0 comments
Open

聊一聊Code Splitting #71

jyzwf opened this issue Mar 23, 2019 · 0 comments

Comments

@jyzwf
Copy link
Owner

jyzwf commented Mar 23, 2019

Code Splitting (代码分隔)可以将代码分隔到不同的bundle中,然后可以按需加载或者并行加载这个文件。从而在网页加载时获得更好的加载体验。
该文章主要以实践为主,因为之前自己在配置这方面折腾的比较少,😑😑

SplitChunksPlugin

首先简单搭建下webpack开发配置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
    .BundleAnalyzerPlugin;

module.exports = {
    entry: {
        index: './src',
        another: './src/another.jsx',
    },
    output: {
        filename: '[name].js',
        chunkFilename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                },
            },
        ],
    },
    resolve: {
        extensions: ['.js', '.jsx', '.css'],
    },
    optimization: {
        splitChunks: {},
    },
    plugins: [
        new CleanWebpackPlugin(),
        new BundleAnalyzerPlugin(),
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, 'src/index.html'),
        }),
    ],
};

开发目录如下:
image

index.jsx:

import React from 'react';

import ReactDOM from 'react-dom';

import moment from 'moment';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

anathor.jsx:

import React from 'react';
import ReactDOM from 'react-dom';
import Container from './components/container';

ReactDOM.render(<Container />, document.getElementById('root'));

App.jsx:

import React from 'react';
import Container from './components/container';

export default function App() {
    return (
        <div>
            <Container />
        </div>
    );
}

container.jsx:

import React from 'react';
import moment from 'moment';
export default function Container() {
    return <div>容器</div>;
}

Selector.jsx:

import React from 'react';
import { Select } from 'antd';

const { Option } = Select;

const mapStateToProps = rootState => {
    return { global: rootState.global };
};

function RegionSelect(props) {
    const { global } = props;

    return (
        <div className="region-select">
            <span>选择大区</span>
            <Select
                value={global.region}
                onChange={handleSelectChange}
                style={{ width: 104 }}>
                <Option value="all">全部</Option>
                <Option value="east">东区</Option>
                <Option value="south">南区</Option>
                <Option value="west">西区</Option>
                <Option value="north">北区</Option>
            </Select>
        </div>
    );
}

export default RegionSelect;

现在直接跑 npm run build(webpack --mode production),看下打包情况:

image

可以看出公共的依赖包并没有被单独拿出来,这时我们需要依靠 SplitChunksPlugin 来做这件事,而webpack 已内置了 SplitChunksPlugin,并且给设定了初始配置:

splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
    default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
        }
    }
}

上面的每个字段的意思参见Webpack4之SplitChunksPlugin。因为 chunks 被设置为了 async,所以只对动态加载的文件进行分块。这里我们先将其改成 all
image

相较于之前,momentreact相关代码已被单独成包了。
但现在如果我想吧react以及react-dom单独拿出来呢?我们可以设置一个 cacheGroup:

splitChunks: {
            chunks: 'all',
            cacheGroups: {
                react: {
                    test: (module, chunks) => /react/.test(module.context),
                    priority: 1,
                },
            },
        },

image
好,react相关代码也被单独拿出来了,,这时可能会问,为何react的代码不会被放到vendors中呢?这时因为 react这个cacheGroup的优先级(1)大于vendors的优先级(-10)。
之前我们讲到,chunks的默认值为 async,现在我们来试试该效果,在another.jsx引入如下代码,然后不设置splitChunks

import(/* webpackChunkName:"antd" */ './components/Selector');

image

看的出来,,antd 被单独打包了,而moment还是在各个入口文件中各有一份,此时在设置为all试试:

image

关于react路由动态加载

如果我们一开始就加载整个应用的代码,那会在首页即加载很多无关的代码,所以我们可以按照路由级别来按需加载相关代码。
通过上面的了解,我们可以对每个路由对应的组件使用 import() 这个方法来动态引入,然后webpack会根据此来分隔代码,从而实现按需加载。
但是 import() 方法返回的是一个 Promise,并不能直接给react-router 来使用,所以我们需要一个组件。可以想一下,我们封装一个 加载组件,在组件未加载完成时,给个loading来提示,设置一个state,在组件加载好后,setState一下,不就ok 了?
可以看看 umi/dynamic,它是基于 react-loadable来实现的,而 react-loadable 的大致思路也是上面我说的这种。

未完待续......

参考

SplitChunksPlugin
Webpack Code Splitting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant