浅析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 相关文章推荐
JS中令人发指的valueOf方法介绍
Feb 22 Javascript
Jquery动态更改一张位图的src与Attr的使用
Jul 31 Javascript
PHP abstract与interface之间的区别
Nov 11 Javascript
js实现创建删除html元素小结
Sep 30 Javascript
es6的数字处理的方法(5个)
Mar 16 Javascript
jquery实现异步加载图片(懒加载图片一种方式)
Apr 24 jQuery
JS中的三个循环小结
Jun 20 Javascript
vue 添加vux的代码讲解
Nov 30 Javascript
详解javascript常用工具类的封装
Jan 30 Javascript
更优雅的微信小程序骨架屏实现详解
Aug 07 Javascript
vue中实现拖动调整左右两侧div的宽度的示例代码
Jul 22 Javascript
js实现购物车商品数量加减
Sep 21 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学习笔记 用户注册模块用户类以及验证码类
2011/09/20 PHP
CodeIgniter图像处理类的深入解析
2013/06/17 PHP
微信公众平台开发实现2048游戏的方法
2015/04/15 PHP
php实现文章置顶功能的方法
2016/10/20 PHP
php检测mysql表是否存在的方法小结
2017/07/20 PHP
PHP有序表查找之插值查找算法示例
2018/02/10 PHP
php的对象传值与引用传值代码实例讲解
2021/02/26 PHP
var与Javascript变量隐式声明
2009/09/17 Javascript
新手常遇到的一些jquery问题整理
2010/08/16 Javascript
js限制文本框输入长度两种限制方式(长度、字节数)
2012/12/19 Javascript
验证码在IE中不刷新而谷歌等浏览器正常的解决方案
2014/03/18 Javascript
js检测网络是否具体连接功能的代码
2014/05/23 Javascript
jQuery选择器之基本选择器与层次选择器
2015/03/03 Javascript
基于jQuery实现拖拽图标到回收站并删除功能
2015/11/25 Javascript
jQuery使用经验小技巧(推荐)
2016/05/31 Javascript
js实现String.Fomat的实例代码
2016/09/02 Javascript
微信小程序 自定义Toast实例代码
2017/06/12 Javascript
详谈js模块化规范
2017/07/07 Javascript
使用canvas进行图像编辑的实例
2017/08/29 Javascript
node.js中express模块创建服务器和http模块客户端发请求
2019/03/06 Javascript
Vue  webpack 项目自动打包压缩成zip文件的方法
2019/07/24 Javascript
[06:09]辉夜杯主赛事开幕式
2015/12/25 DOTA
[01:06]欢迎来到上海,TI9
2018/08/26 DOTA
python删除文件示例分享
2014/01/28 Python
python占位符输入方式实例
2019/05/27 Python
python获取地震信息 微信实时推送
2019/06/18 Python
python3读取图片并灰度化图片的四种方法(OpenCV、PIL.Image、TensorFlow方法)总结
2019/07/04 Python
python实现小世界网络生成
2019/11/21 Python
美国老牌主机服务商:iPage
2016/07/22 全球购物
Expedia印度:您的一站式在线旅游网站
2017/08/24 全球购物
意大利领先的线上奢侈品销售电商:Eleonora Bonucci
2017/10/17 全球购物
工程学毕业生自荐信
2014/06/14 职场文书
爱祖国爱家乡演讲稿
2014/09/02 职场文书
列车乘务员工作不细心检讨书
2014/10/07 职场文书
Java异常处理try catch的基本用法
2021/12/06 Java/Android
Python使用Web框架Flask开发项目
2022/06/01 Python