作为当前风头正盛的打包工具,webpack风靡前端界。确实作为引领了一个时代的打包工具,很多方面都带来了颠覆性的改进,让我们更加的感受到自动化的快感。不过最为大家诟病的一点就是用起来太难了。

要想愉快的使用,要使用n多的配置项,究其原因在于文档的不够详细、本身默认集成的不足。也不能说这是缺点吧,更多的主动权放给用户就意味着配置工作量的增加,这里就不过多探讨了。当历尽千辛万苦,你的项目跑起来之后,可能会发现有一些不太美好的问题的出现,编译慢、打包文件大等。那么,我们还要花些时间来看看怎么优化相关配置了。 下面一起看下code splitting

code splitting出现的背景

对于前端资源来说,文件体积过大是很影响性能的一项。特别是对于移动端的设备而言简直是灾难。此外对于某些只要特定环境下才需要的代码,一开始就加载进来显然也不那么合理,这就引出了按需加载的概念了。

为了解决这些情况,代码拆分就应运而生了。代码拆分故名思意就是将大的文件按不同粒度拆分,以满足解决生成文件体积过大、按需加载等需求。具体到webpack而言有下面几种方式来达到我们的目的。

webpack实现代码拆分的方式

webpack通过下面三种方式来达到以上目的

  1. Entry Points: 多入口分开打包
  2. Prevent Duplication:去重,抽离公共模块和第三方库
  3. Dynamic Imports:动态加载

    这里不去扒文档上的定义了,我们从一个例子中来逐步体会他们不同的作用。

假设我们有这么个项目,有下面几个文件

代码很简单(示例而已,直接用commonjs的语法来写了):

//a.js
var react = require('react')
var tool = require('./tool')
var b = require('./b')
function load(){
b()
tool()
console.log('全部文件都从一个入口打包')
}
load()
//b.js
var react = require('react')
var tool = require('./tool')
function b(){
tool()
console.log('这是bjs文件')
}
module.exports = b;
//tool.js
var react = require('react')
function tool(){
console.log('这是tooljs文件')
}
module.exports = tool;

配置很简单:

var webpack = require('webpack');module.exports = {
entry: './codesplitting/c1/a.js',
output: {
path: __dirname,
filename: '/dist/index.js'
}
//*****
}

直接打包:可以看到文件大小有2047行,体积也变大了

目前只引入了react,并且业务代码几乎没有的情况下。大家可以猜到实际项目中的情况了。来让我们进行第一优化

Entry Points

如果业务中的项目不是单页面应用,这一步可以忽略了,直接是多入口打包。这里是为了演示效果,强行分一个模块出来打包,假设我们的文件也很大,需要将b.js单独打个包出来:

    entry: {
index:'./codesplitting/c1/a.js',
other:'./codesplitting/c1/b.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].js'
},
//***

这里a.js也需要修改,去掉对b的引用。入口文件之间不能相互引用的。不然,问题就大了,到底以谁为主呢,这样就陷入了循环引用的问题。

此时的生成文件如下:



看来文件竟然只小了那么一点了吧?第一步的优化这里就完成了,显然你会认为我在开玩笑。

当然这只是万里长征第一步,看一下dist下的文件不难发现两个文件中都把react这个第三方库和tool.js这个可复用模块打进去了,显然这样重复打包有点没必要。

是不是可以把这些复用性强的模块拿出来单独打包呢?

这样浏览器第一次请求之后就会将该文件缓存起来,从服务端请求的只有体积缩小之后的业务文件了,这样的话加载速度显然会有所提升。

如果你也是这么想的,来一起继续看下去。

Prevent Duplication

webpack去除重复引用是通过CommonsChunkPlugin插件来实现的。该插件的配置项如下:

{
//被抽离为公共文件的chunk名,例如common,可以是string或者数组
//显然如果是单个的模块,就是name多个就是names
name:string,
names:[],
//打包之后公共模块的名称模板
//例如'[name].js'
//如果省略,则和name名称一致
filename:string,
//模块被引的最小次数,也就是说至少有几个组件引用了该模块。
//如果是Infinity,则表明单纯的创建,并不做任何事情
minChunks:2
}

具体在webpack中去重对于第三方库显示声明vendor,公共模块声明common的方式来处理

entry: {
index:'./codesplitting/c1/a.js',
other:'./codesplitting/c1/b.js',
//第三方库显示声明
vendor:['react'],
//公共组件声明为common
common:['./codesplitting/c1/tool']
},
//***
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names:["common", "vendor"],
filename: "[name].js"
})
]

打包结果如下:

可以看到index和other两个业务包已经很小了,react被抽离到单独的包中。

这样还有一个问题,对于某些代码可能只有在特定条件下才执行,或者可能就不执行。

我不希望在首屏就去加载它,也就是我们常说的按需加载是要怎么做呢。一起看下去。

Dynamic Imports

webpack建议如下两种方式使用动态加载。

1)、ECMAScript中出于提案状态的import()

2)、webpack 特定的 require.ensure

我们这里就是用第二种来看下效果(毕竟偷懒没用babel...),在ajs中动态引入di.js

    //虽然始终会加载,大家能明白就行
if(true){
require.ensure([],function(require){
var di = require('./di')
})
}
//新增动态加载的js
function di(){
tool()
console.log('这是动态引入的文件')
}
module.exports = di;

运行之后可以发现多了个2.2.js,打开可以发现就是我们新建的动态引入的di.js

大家可能会问怎么确定就是动态引入的呢,虽然本示例只能看打包之后的例子(就不引入dev server了,毕竟是懒。。。)我们依然可以从代码里看到结果。

首先、查看index.js文件,可以看到下面的代码:

      var react = __webpack_require__(2)
var tool = __webpack_require__(1)
/****省略8*****/
//虽然始终会加载
if(true){
__webpack_require__.e/* nsure */(2, function(require){
var di = __webpack_require__(13)
})
}

与直接require的模块不同,require.ensure被转化为了 webpack_require.e方法,来继续看一下该方法有什么用。

   	__webpack_require__.e = function requireEnsure(chunkId, callback) {
// "0" is the signal for "already loaded"
if(installedChunks[chunkId] === 0)
return callback.call(null, __webpack_require__); // an array means "currently loading".
if(installedChunks[chunkId] !== undefined) {
installedChunks[chunkId].push(callback);
} else {
// start chunk loading
installedChunks[chunkId] = [callback];
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.charset = 'utf-8';
script.async = true; script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"common","1":"index","3":"other"}[chunkId]||chunkId) + ".js";
head.appendChild(script);
}
};

结合注释直接从源码中可以看出来,最后面的条件语句来创建script标签进而实现动态加载的。所谓动态加载本质还是要创建script标签来实现的。

结束语

至此代码分割部分的优化已经完成了,以上是个人关于代码分割的简单理解,抛砖引玉,共同学习进步。更多请移步github查看

webpack优化之code splitting的更多相关文章

  1. [转] react-router4 + webpack Code Splitting

    项目升级为react-router4后,就尝试着根据官方文档进行代码分割.https://reacttraining.com/react-router/web/guides/code-splittin ...

  2. react-router4 + webpack Code Splitting

    项目升级为react-router4后,就尝试着根据官方文档进行代码分割.https://reacttraining.com/react-router/web/guides/code-splittin ...

  3. webpack 和 code splitting

    Code Splitting指的是代码分割,那么什么是代码分割,webpack和code splitting又有什么样的联系呢? 使用npm run dev:"webpack-dev-ser ...

  4. [Webpack 2] Maintain sane file sizes with webpack code splitting

    As a Single Page Application grows in size, the size of the payload can become a real problem for pe ...

  5. webpack Code Splitting浅析

    Code Splitting是webpack的一个重要特性,他允许你将代码打包生成多个bundle.对多页应用来说,它是必须的,因为必须要配置多个入口生成多个bundle:对于单页应用来说,如果只打包 ...

  6. webpack 利用Code Splitting 分批打包、按需下载

    webpack中的解决方案——Code Splitting,简单来说就是按需加载(下载),如果是requireJS对应的AMD的方案中这本是在正常不过了.但是在webpack中All in one的思 ...

  7. webpack async load modules & dynamic code splitting

    webpack async load modules & dynamic code splitting webpack 按需/异步加载/Code Splitting webpack loade ...

  8. webpack - 优化阻塞渲染的css

    随着浏览器的日新月异,网页的性能和速度越来越好,并且对于用户体验来说也越来越重要. 现在有很多优化页面的办法,比如:静态资源的合并和压缩,code splitting,DNS预读取等等. 本文介绍的是 ...

  9. 在webpack中使用Code Splitting--代码分割来实现vue中的懒加载

    当Vue应用程序越来越大,使用Webpack的代码分割来懒加载组件,路由或者Vuex模块, 只有在需要时候才加载代码. 我们可以在Vue应用程序中在三个不同层级应用懒加载和代码分割: 组件,也称为异步 ...

随机推荐

  1. idea为tomcat设置虚拟地址

    1.设置tomcat的server.xml <Host name="localhost" appBase="webapps" unpackWARs=&qu ...

  2. Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块

    Python第二十二天   stat模块  os.chmod方法  os.stat方法  pwd  grp模块 stat模块描述了os.stat(filename)返回的文件属性列表中各值的意义,根据 ...

  3. Maven打包排除某个资源或者目录

    最近在spark streaming本地调试的时候,引入了一些资源文件,打包的时候需要给排除掉.所以就考虑使用maven的方式 详细参考官方文档:https://maven.apache.org/pl ...

  4. HY.Mail:C#简单、易用的邮件工具库

    一.开发HY.Mail的初衷 Nuget或者github上有很多成熟且优秀的邮件库可以使用, 但是目前找到的使用都不够简洁或者不适合我的使用场景 我的场景是开发应用场景(例如系统通知.运维通知),而非 ...

  5. Zabbix 单位换算

    直接举一例子,然后再举一反三: 如图: 单位B 则基数为1024(倍数) 我性能参数为KB单位,我们则把单位转换成和我们计数器 保持一致的单位即可,一致后,zabbix 后面会自己准换成自己想要的显示 ...

  6. Hadoop2.9.0安装

    参考 https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html 1.下载并解 ...

  7. SpringCloud学习笔记(6)——Eureka高可用

    参考Spring Cloud官方文档第12章12.3.12.5.12.6小节 12.3 High Availability, Zones and Regions 默认情况下,每一个Eureka服务器同 ...

  8. IOS 触摸事件分发机制详解

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者:MelonTeam 前言 很多时候大家都不关心IOS触摸事件的分发机制的实现原理,当遇到以下几种情形的时候你很可能抓破头皮都找不到解决方案 ...

  9. 豹哥嵌入式讲堂:ARM开发之文件详解(4)- relocatable文件(object, library)

    大家好,我是豹哥,猎豹的豹,犀利哥的哥.今天豹哥给大家讲的是嵌入式开发里的relocatable文件(object, library). 前三节课里,豹哥都是在给大家介绍嵌入式开发中的input文件. ...

  10. 论python3下“多态”与“继承”中坑

    1.背景: 近日切换到python3后,发现python3在多态处理上,有一些比较有意思的情况,特别记载,供大家参考... 以廖老师的python3教程中的animal 和dog的继承一节的代码做例子 ...