webpack 4.x 代码分割实践

写在前面的话

代码分割是webpack最强大的功能之一,它允许将代码拆分成多个捆绑的包(bundle),然后对这些捆绑的包进行按需加载或者并行加载,使用得当可以达到首次加载速度的提升。

下面是有三种实现代码分割的通用方法,三种也可以搭配使用

1.入口文件:通过手动配置webpack多个为入口
2.避免重复:使用SplitChunks插件删除并提取出公共代码块作为单独的一个chunk
3.动态导入:调用模块内的内联函数如import()或者require.ensure()异步加载代码块
一、入口文件

最简单、最直观的代码分割方式,同时也存在的一些缺陷需要我们注意

a.js:

import _ from 'lodash';
console.log(
    _.join(['A', 'Module', 'Loaded!'], ' ')
);

index.js:

console.log('Index Module Loaded!');

webpack.config.js:

var path = require('path');
var root_path = path.resolve(__dirname);
module.exports = {
    mode: 'production',
    entry: {  //配置多个入口文件打包成多个代码块
        index: path.resolve(root_path, 'index.js'), 
        a: path.resolve(root_path, 'a.js')
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(root_path, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: path.resolve(root_path, 'node_modules'),
                include: root_path
            }
        ]
    }
}

打包结果:

Hash: 69ecfe6ff873d88b9d3a
Version: webpack 4.15.1
Time: 663ms
Built at: 2018-07-10 12:00:55
          Asset       Size  Chunks             Chunk Names
    a.bundle.js   70.5 KiB       0  [emitted]  a
index.bundle.js  982 bytes       1  [emitted]  index
[0] (webpack)/buildin/module.js 497 bytes {0} [built]
[1] (webpack)/buildin/global.js 489 bytes {0} [built]
[3] ./a.js 263 bytes {0} [built]
[4] ./index.js 51 bytes {1} [built]
    + 1 hidden module

这样使得无关系的两个js模块可以并行加载,这种方式相比较于加载一个等价大小的js模块有速度上的提升。

但是也存在以下问题
1.如果index.js也引入了lodash,那么lodash将会同时打包到两个js模块中
2.不灵活,不能根据核心应用程序的逻辑来动态分割代码

二、避免重复

SplitChunks插件(webpack 4.x以前使用CommonsChunkPlugin)允许我们将公共依赖项提取到现有的entry chunk或全新的代码块中

index.js 也引入lodash

import _ from 'lodash';
console.log(
    _.join(['Index', 'Module', 'Loaded!'], ' ')
);

webpack.config.js 使用SplitChunks插件:

optimization: {
     splitChunks: {
         chunks: 'all'
     }
 }

打包结果:

Hash: bdaac52a2513ada9bfa7
Version: webpack 4.15.1
Time: 3540ms
Built at: 2018-07-10 13:11:59
                    Asset      Size  Chunks             Chunk Names
vendors~a~index.bundle.js  69.5 KiB       0  [emitted]  vendors~a~index
              a.bundle.js  1.64 KiB       1  [emitted]  a
          index.bundle.js   1.8 KiB    2, 1  [emitted]  index
[0] ./a.js 378 bytes {1} {2} [built]
[2] (webpack)/buildin/module.js 497 bytes {0} [built]
[3] (webpack)/buildin/global.js 489 bytes {0} [built]
[4] ./index.js 296 bytes {2} [built]

lodash被单独打包成venders~a~index.bundle.js,`SplitChunks插件还可以依照自己的需求进行配置,默认配置情况如下

1.模块被重复引用或者来自node_modules中的模块
2.在压缩前最小为30kb
3.在按需加载时,请求数量小于等于5
4.在初始化加载时,请求数量小于等于3

满足以上条件的模块都会被单独拆分到一个代码块里面

社区还为代码分割提供了很多有用的plugin和loader

三、动态导入

有两种方式供我们实现动态导入

1.import()   //ES的提案,返回以一个promise,导入的模块在then中拿到
2.require.ensure()  //webpack在编译时会静态地解析代码中的require.ensure(),将里面require的模块添加到一个分开的chunk中。这个新chunk会被webpack通过jsonp来按需加载。

以上两种方式都依赖于promise,如果在旧的浏览器中使用 require.ensure 请记得 shim Promise, 相应的库有 es6-promise polyfill.

下面主要使用import()实现动态导入,因为import()目前为止还是提案阶段,需要安装插件npm install babel-plugin-syntax-dynamic-import --save-dev.babelrc配置如下

{
    "presets": [
        ...
    ],
    "plugins": ["syntax-dynamic-import"]
}

下面看核心代码
webpack.config.js:

var path = require('path');
var root_path = path.resolve(__dirname);
module.exports = {
    mode: 'production',
    entry: {
        index: path.resolve(root_path, 'index.js'),
    },
    output: {
        filename: '[name].bundle.js',
        chunkFilename: '[name].demand.js',
        path: path.resolve(root_path, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: path.resolve(root_path, 'node_modules'),
                include: root_path
            }
        ]
    }
}
chunkFilename:指定非entry chunk的文件名,比如import()引入的模块不打包进entry中,而会作为单独的chunk打包,其文件名就由该属性决定

index.js:点击按钮才异步加载lodash

let btn = document.getElementById('btn');

btn.addEventListener('click', () => {
    import(/* webpackChunkName: "lodash" */ 'lodash').then(
        _ => {
            var app = document.getElementById('app');
            app.textContent = _.join(['Index', 'Module', 'Loaded!'], ' ');
        }
    ).catch(
        err => {
            console.log('loading module error occur', err);
        }
    )
});
webpackChunkName:用于指定动态加载的组件的名称

参考资料

Webpack CodeSpliting

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容

  • 1 Webpack 1.1 概念简介 1.1.1 WebPack是什么 1、一个打包工具 2、一个模块加载工具 3...
    Kevin_Junbaozi阅读 6,645评论 0 16
  • GitChat技术杂谈 前言 本文较长,为了节省你的阅读时间,在文前列写作思路如下: 什么是 webpack,它要...
    萧玄辞阅读 12,681评论 7 110
  • 作者:小 boy (沪江前端开发工程师)本文原创,转载请注明作者及出处。原文地址:https://www.smas...
    iKcamp阅读 2,750评论 0 18
  • webpack 介绍 webpack 是什么 为什么引入新的打包工具 webpack 核心思想 webpack 安...
    yxsGert阅读 6,460评论 2 71
  • 前端将大型项目分成一个个单独的模块,一般封装好的每个模块都会实现一个目的明确的完成的功能。如何处理这些模块以及模块...
    pixels阅读 3,419评论 1 14