浅析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 相关文章推荐
javascript操作cookie的文章(设置,删除cookies)
Apr 01 Javascript
js实现可拖动DIV的方法
Dec 17 Javascript
jquery select 设置默认选中的示例代码
Feb 07 Javascript
如何让浏览器支持jquery ajax load 前进、后退功能
Jun 12 Javascript
再谈JavaScript线程
Jul 10 Javascript
js和jquery实现监听键盘事件示例代码
Jun 24 Javascript
jQuery ajax提交Form表单实例(附demo源码)
Apr 06 Javascript
js中用cssText设置css样式的简单方法
Sep 19 Javascript
JQuery实现文字无缝滚动效果示例代码(Marquee插件)
Mar 07 Javascript
vue+axios新手实践实现登陆的示例代码
Jun 06 Javascript
jQuery实现鼠标移入移出事件切换功能示例
Sep 06 jQuery
深入理解Angularjs 脏值检测
Oct 12 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
如何利用PHP执行.SQL文件
2013/07/05 PHP
PHP获取时间排除周六、周日的两个方法
2014/06/30 PHP
php绘制圆形的方法
2015/01/24 PHP
发布一个高效的JavaScript分析、压缩工具 JavaScript Analyser
2007/11/30 Javascript
jquery命令汇总,方便使用jquery的朋友
2012/06/26 Javascript
javascript实现随时变化着的背景颜色
2015/04/02 Javascript
JQuery简单实现锚点链接的平滑滚动
2015/05/03 Javascript
JS组件Bootstrap Table使用方法详解
2016/02/02 Javascript
详解AngularJS控制器的使用
2016/03/09 Javascript
Bootstrap 布局组件(全)
2016/07/18 Javascript
js+html制作简单验证码
2017/02/16 Javascript
详解用webpack把我们的业务模块分开打包的方法
2017/07/20 Javascript
vue2.0 兄弟组件(平级)通讯的实现代码
2018/01/15 Javascript
详解使用jest对vue项目进行单元测试
2018/09/07 Javascript
微信小程序五子棋游戏的棋盘,重置,对弈实现方法【附demo源码下载】
2019/02/20 Javascript
p5.js实现动态图形临摹
2019/10/23 Javascript
JavaScript原型式继承实现方法
2019/11/06 Javascript
Javascript生成器(Generator)的介绍与使用
2021/01/31 Javascript
使用Python的Flask框架来搭建第一个Web应用程序
2016/06/04 Python
django+mysql的使用示例
2018/11/23 Python
Python中的 is 和 == 以及字符串驻留机制详解
2019/06/28 Python
django框架模板语言使用方法详解
2019/07/18 Python
Python调用C语言的实现
2019/07/26 Python
vscode+PyQt5安装详解步骤
2020/08/12 Python
Django通过设置CORS解决跨域问题
2020/11/26 Python
世界排名第一的运动鞋市场:Flight Club
2020/01/03 全球购物
金融专业个人的自我评价
2013/10/18 职场文书
大学生职业生涯规划书范文
2014/01/04 职场文书
促销活动策划方案
2014/01/12 职场文书
粗加工管理制度
2014/02/04 职场文书
中学生操行评语大全
2014/04/24 职场文书
财务部绩效考核方案
2014/05/04 职场文书
2014幼儿园教师师德师风演讲稿
2014/09/10 职场文书
教师批评与自我批评(群众路线)
2014/10/15 职场文书
2015年度党员自我评价范文
2015/03/03 职场文书
幼儿园毕业典礼家长致辞
2015/07/29 职场文书