浅析webpack 如何优雅的使用tree-shaking(摇树优化)


Posted in Javascript onAugust 16, 2017

1.什么是tree-shaking

webpack 2 的到来带来的最棒的新特性之一就是tree-shaking 。tree-shaking源自于rollup.js,先如今,webpack 2也有类似的做法。

webpack 里的tree-shaking的到来不得不归功于es6规范的模块。为什么这么说,如今的前端模块规范很多,比较出流行的比如commonJS , AMD , es6 ,我简单的说一下commonJS和es6模块的区别。

commonJS 模块

commonJS的模块规范在Node中发扬光大,总的来说,它的特性有这几个:

1.动态加载模块

commonJS和es6的最大区别大概就在于此了吧,commonJS模块的动态加载能够很轻松的实现懒加载,优化用户体验。

2.加载整个模块

commonJS模块中,导出的是整个模块。

3.每个模块皆为对象

commonJS模块都被视作一个对象。

4.值拷贝

commonJS的模块输出和 函数的值传递相似,都是值的拷贝

es6 模块

1.静态解析

即在解析阶段就确定输出的模块,所以es6模块的import一般写在被引入文件的开头。

2.模块不是对象

在es6里,每个模块并不会当做一个对象看待

3.加载的不是整个模块

在es6模块中经常会看见一个模块中有好几个export 导出

4.模块的引用

es6模块中,导出的并不是模块的值拷贝,而是这个模块的引用

在结合es6模块和commonJS模块的区别之后,我们知道es6的特点是静态解析,而commonJS模块的特点是动态解析的,因此,借于es6模块的静态解析,tree-shaking的实现才能成为可能。
在webpack中,tree-shaking指的就是按需加载,即没有被引用的模块不会被打包进来,减少我们的包大小,缩小应用的加载时间,呈现给用户更佳的体验。

2.怎么使用tree-shaking

说了这么多那到底如何使用tree-shaking呢?
webpack默认es6规范编写的模块都能使用tree-shaking。这是什么意思呢?下面来看个例子。

首先奉上我的demo目录如下:

├─dist
    └─index.html
├─node_modules
    └─...
├─src
    ├─scripts
    ├─assets
├─webpack.config.js
└─package.json

dist用来存放打包好的代码

src相反的用来存放源文件

src里的scripts目录用来存放js脚本文件,assets用来存放静态资源文件

以下几条命令过后开始我们的tree-shaking之旅

npm install --save-dev webpack webpack-dev-server
webpack.config.js
const webpack = require('webpack')
const path = require('path')
module.exports = {
  entry:'./src/scripts/main.js',
  output:{
    path:path.resolve(__dirname,'dist/'),
    filename:'main.bundle.js'
  },
  plugins:[
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer:{
    port:4200,
    contentBase:path.resolve(__dirname,'dist/'),
    historyApiFallback:true,
    hot:true
  }
}

接下来是main.js,直接引入了sayHello

import { sayHello } from './greeter.ts';

sayHello();

相应的main.js的依赖greeter.js

export function sayHello(){
  alert('hello')
}
export function sayWorld(){
  alert('world')
}

在dist目录下有个index.html 用来引入打包后的bundle

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <script type="text/javascript" src="./main.bundle.js"></script>
</body>
</html>

以上就是整个demo的代码,接下来的事情我们直接webpack打包试试看

去掉打包后冗长的代码只看chunk传参的部分:

[
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__person__ = __webpack_require__(1);
Object(__WEBPACK_IMPORTED_MODULE_0__person__["a" /* sayHello */])();
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return sayHello; });
/* unused harmony export sayWorld */
    function sayHello(){
        alert('hello');
    }
    function sayWorld(){
        alert('world');
    }
/***/ })
/******/ ]

我们关注这一行

/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return sayHello; });

实际上只return了一个sayHello。

因此我们现在只需要压缩一下整个Js代码,就能把没引用的sayWorld剔除。

键入以下命令进行压缩

webpack --optimize-minimize

由于压缩后的代码只有一行了,我们移步尾部:

function(e,n,r){"use strict";function t(){alert("hello")}r.d(n,"a",function(){return t})}]);

可以看到sayWorld函数已经被成功剔除。

我们启动webpack-dev-server

webpack-dev-server

在浏览器中输入

http://localhost:4200

浅析webpack 如何优雅的使用tree-shaking(摇树优化)

每次都需要在命令行里输入参数,岂不是很麻烦,还有没有其他更好的办法呢?

(1)我们可以把这串命令放入package.json的scripts字段,然后通过npm start来自动执行

(2)其实?optimize-minimize的底层实现是一个插件UglifyJsPlugin,因此,我们可以直接在webpack.config.js里配置它

在webpack.config.js里配置插件

const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry:'./src/scripts/main.js',
  output:{
    filename:'main.bundle.js',
    path:path.join(__dirname,'dist')
  },
  plugins:[
    new webpack.optimize.UglifyJsPlugin(), // <----------- 压缩js
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer:{
    port:4200,
    historyApiFallback:true,
    hot:true,
    contentBase:path.join(__dirname,"dist/")
  }
}

然后我们webpack打包

即看到同样的效果

function(e,n,r){"use strict";function t(){alert("hello")}r.d(n,"a",function(){return t})}]);

在tree-shaking触发打包后,仅仅是撇开了模块的引用,但还是要结合压缩工具来进行,这才是完整的一次tree-shaking

那如果是typescript该怎么使用tree-shaking呢?

3.如何在typescript里使用tree-shaking

要在webpack里使用ts,首先我们必须安装tsc

npm install --save-dev typescript

之后我们需要解析ts文件的loader

npm install --save-dev ts-loader

然后在webpack.config.js进行配置

const webpack = require('webpack')
const path = require('path')
module.exports = {
  entry:'./src/scripts/main.ts',
  output:{
    path:path.resolve(__dirname,'dist/'),
    filename:'main.bundle.js'
  },
  module:{
    rules:[
      {
        test:/\.ts$/,
        use:['ts-loader']
      }
    ]
  },
  plugins:[
    new webpack.optimize.UglifyJsPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  devServer:{
    port:4200,
    contentBase:path.resolve(__dirname,'dist/'),
    historyApiFallback:true,
    hot:true
  }
}

献上我的两份文件main.ts , greeter.ts (这两份文件除了后缀名基本没有改动)

main.ts

import { sayHello } from './greeter.ts';

sayHello();

greeter.ts

export var sayHello = function(){
  alert('hello')
}

export var sayWorld = function(){
  alert('world')
}

之后我们需要做的是,创建一个tsconfig.json的配置文件供tsc解析,这时,坑来了。

下面是我的tsconfig.json文件

{
  "compilerOptions":{
    "target":"es5",
    "sourceMap":true
  },
  "exclude":[
    "./node_modules"
  ]
}

好像没有什么不对

接着我们webpack

看下打包压缩后的代码的最后一部分:

"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sayHello=function(){alert("hello")},n.sayWorld=function(){alert("world")}}]);

sayWorld居然还是存在!!!怎么回事,为什么没有被触发tree-shaking优化?

这是因为tsc编译后的代码为es5 ,而正因如此,tsc默认使用了commonJS的规范来加载模块,因此并没有触发tree-shaking,那我们要怎么做?

修改一下tsconfig.json,把target改为es6即可!

{
  "compilerOptions":{
    "target":"es6",
    "sourceMap":true
  },
  "exclude":[
    "./node_modules"
  ]
}

再次打包

看一下打包后的bundle

function(e,n,r){"use strict";r.d(n,"a",function(){return t});var t=function({alert("hello")}}]);

果然是触发了tree-shaking

开启webpack-dev-server

webpack-dev-server

可以看到成功打印hello

浅析webpack 如何优雅的使用tree-shaking(摇树优化)

以上就是我对webpack tree-shaking的总结,希望对大家的学习有所帮助

Javascript 相关文章推荐
jquery 得到当前页面高度和宽度的两个函数
Feb 21 Javascript
php 中序列化和json使用介绍
Jul 08 Javascript
JS操作Cookie写入和读取实例代码
Oct 20 Javascript
js中的preventDefault与stopPropagation详解
Jan 29 Javascript
JavaScript中自定义事件用法分析
Dec 23 Javascript
jQuery插件实现图片轮播特效
Jun 16 Javascript
AngularJS教程之MVC体系结构详解
Aug 16 Javascript
Vue监听数据对象变化源码
Mar 09 Javascript
Angular2入门教程之模块和组件详解
May 28 Javascript
Spring Boot/VUE中路由传递参数的实现代码
Mar 02 Javascript
vue实现个人信息查看和密码修改功能
May 06 Javascript
Vue.js实现立体计算器
Feb 22 Javascript
JavaScript实现三级联动菜单效果
Aug 16 #Javascript
ionic3 懒加载
Aug 16 #Javascript
JavaScript仿微信(电话)联系人列表滑动字母索引实例讲解(推荐)
Aug 16 #Javascript
理解 Node.js 事件驱动机制的原理
Aug 16 #Javascript
JavaScript选取(picking)和反选(rejecting)对象的属性方法
Aug 16 #Javascript
JavaScript-定时器0~9抽奖系统详解(代码)
Aug 16 #Javascript
vue实现留言板todolist功能
Aug 16 #Javascript
You might like
PHP4实际应用经验篇(2)
2006/10/09 PHP
PHP中遍历stdclass object的实现代码
2011/06/09 PHP
PHP Filter过滤器全面解析
2016/08/09 PHP
JavaScript的Cookies
2008/01/16 Javascript
Prototype PeriodicalExecuter对象 学习
2009/07/19 Javascript
JavaScript 密码强度判断代码
2009/09/05 Javascript
基于jQuery的淡入淡出可自动切换的幻灯插件
2010/08/24 Javascript
jQuery快速上手:写jQuery与直接写JS的区别详细解析
2013/08/26 Javascript
ECMAScript6函数剩余参数(Rest Parameters)
2015/06/12 Javascript
基于Jquery实现万圣节快乐特效
2015/11/01 Javascript
JS插件plupload.js实现多图上传并显示进度条
2016/11/29 Javascript
jquery实现超简单的瀑布流布局【推荐】
2017/03/08 Javascript
vuejs使用$emit和$on进行组件之间的传值的示例
2017/10/04 Javascript
js用类封装pop弹窗组件
2017/10/08 Javascript
教你用Cordova打包Vue项目的方法
2017/10/17 Javascript
Vue.js 的移动端组件库mint-ui实现无限滚动加载更多的方法
2017/12/23 Javascript
node命令行工具之实现项目工程自动初始化的标准流程
2019/08/12 Javascript
javascript导出csv文件(excel)的方法示例
2019/08/25 Javascript
webgl实现物体描边效果的方法介绍
2019/11/27 Javascript
[10:04]国际邀请赛采访专栏:DK.Farseer,mouz.Black^,采访员Josh专访
2013/08/05 DOTA
[07:38]2014DOTA2国际邀请赛 Newbee顺利挺进胜者组赛后专访
2014/07/15 DOTA
[45:18]2018DOTA2亚洲邀请赛 4.3 突围赛 Optic vs iG 第一场
2018/04/04 DOTA
[27:08]完美世界DOTA2联赛PWL S2 SZ vs Rebirth 第二场 11.21
2020/11/23 DOTA
Linux下通过python访问MySQL、Oracle、SQL Server数据库的方法
2016/04/23 Python
python 对dataframe下面的值进行大规模赋值方法
2018/06/09 Python
Python转换itertools.chain对象为数组的方法
2020/02/07 Python
Python如何使用27行代码绘制星星图
2020/07/20 Python
python如何调用php文件中的函数详解
2020/12/29 Python
香港莎莎官网Sasa.com:亚洲著名国际化妆品商城
2019/11/10 全球购物
PHP中如何创建和修改数组
2012/05/02 面试题
电子商务毕业生求职信
2013/11/10 职场文书
马智宇婚礼主持词
2014/03/22 职场文书
新年联欢会主持词
2014/03/27 职场文书
感恩的演讲稿
2014/05/06 职场文书
检查机关党的群众路线个人整改措施
2014/10/04 职场文书
Go语言使用select{}阻塞main函数介绍
2021/04/25 Golang