做为当前风头正盛的打包工具,webpack风靡前端界。确实做为引领了一个时代的打包工具,不少方面都带来了颠覆性的改进,让咱们更加的感觉到自动化的快感。不过最为你们诟病的一点就是用起来太难了。
要想愉快的使用,要使用n多的配置项,究其缘由在于文档的不够详细、自己默认集成的不足。
也不能说这是缺点吧,更多的主动权放给用户就意味着配置工做量的增长,这里就不过多探讨了。
当历尽千辛万苦,你的项目跑起来以后,可能会发现有一些不太美好的问题的出现,编译慢、打包文件大等。那么,咱们还要花些时间来看看怎么优化相关配置了。 下面一块儿看下code splittingjavascript
对于前端资源来讲,文件体积过大是很影响性能的一项。特别是对于移动端的设备而言简直是灾难。
此外对于某些只要特定环境下才须要的代码,一开始就加载进来显然也不那么合理,这就引出了按需加载的概念了。前端
为了解决这些状况,代码拆分就应运而生了。代码拆分故名思意就是将大的文件按不一样粒度拆分,以知足解决生成文件体积过大、按需加载等需求。
具体到webpack而言有下面几种方式来达到咱们的目的。java
webpack经过下面三种方式来达到以上目的react
假设咱们有这么个项目,有下面几个文件 webpack
代码很简单(示例而已,直接用commonjs的语法来写了):git
//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;
复制代码
配置很简单:github
var webpack = require('webpack');module.exports = {
entry: './codesplitting/c1/a.js',
output: {
path: __dirname,
filename: '/dist/index.js'
}
//*****
}
复制代码
直接打包:能够看到文件大小有2047行,体积也变大了 web
目前只引入了react,而且业务代码几乎没有的状况下。你们能够猜到实际项目中的状况了。来让咱们进行第一优化数组
若是业务中的项目不是单页面应用,这一步能够忽略了,直接是多入口打包。这里是为了演示效果,强行分一个模块出来打包,假设咱们的文件也很大,须要将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的引用。入口文件之间不能相互引用的。否则,问题就大了,到底以谁为主呢,这样就陷入了循环引用的问题。 此时的生成文件以下:
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被抽离到单独的包中。
这样还有一个问题,对于某些代码可能只有在特定条件下才执行,或者可能就不执行。
我不但愿在首屏就去加载它,也就是咱们常说的按需加载是要怎么作呢。一块儿看下去。
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查看