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

发布时间 - 2026-01-11 02:49:02    点击率:

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

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

(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  # tree-shaking的优化  # webpack使用tree  # Tree Shaking实现方法指南  # Tree-Shaking 机制快速掌握  # tree shaking功能及使用原理详细解析  # tree shaking对打包体积优化及作用  # webpack的tree shaking的实现方法  # Vue中的Tree-Shaking介绍及原理  # 加载  # 的是  # 我们可以  # 可以看到  # 两份  # 都是  # 是一个  # 这是  # 来了  # 也有  # 就在  # 有个  # 特点是  # 的说  # 说了  # 都能  # 这么多  # 我对  # 中有  # 要在 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Gemini怎么用新功能实时问答_Gemini实时问答使用【步骤】  Laravel如何生成PDF或Excel文件_Laravel文档导出工具与使用教程  怎么用AI帮你设计一套个性化的手机App图标?  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  简历没回改:利用AI润色让你的文字更专业  微信小程序 配置文件详细介绍  Laravel如何处理文件下载请求?(Response示例)  如何用JavaScript实现文本编辑器_光标和选区怎么处理  Linux后台任务运行方法_nohup与&使用技巧【技巧】  北京网站制作公司哪家好一点,北京租房网站有哪些?  如何用狗爹虚拟主机快速搭建网站?  如何使用 Go 正则表达式精准提取括号内首个纯字母标识符(忽略数字与嵌套)  javascript和jQuery中的AJAX技术详解【包含AJAX各种跨域技术】  Laravel如何处理表单验证?(Requests代码示例)  Laravel如何创建自定义Artisan命令?(代码示例)  Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】  网站制作壁纸教程视频,电脑壁纸网站?  长沙做网站要多少钱,长沙国安网络怎么样?  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  怎样使用JSON进行数据交换_它有什么限制  香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化  长沙企业网站制作哪家好,长沙水业集团官方网站?  魔方云NAT建站如何实现端口转发?  如何在阿里云域名上完成建站全流程?  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?  如何注册花生壳免费域名并搭建个人网站?  JavaScript中如何操作剪贴板_ClipboardAPI怎么用  Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】  Laravel如何记录自定义日志?(Log频道配置)  ,南京靠谱的征婚网站?  关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)  Laravel如何实现API速率限制?(Rate Limiting教程)  详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)  Laravel如何生成和使用数据填充?(Seeder和Factory示例)  Laravel用户密码怎么加密_Laravel Hash门面使用教程  微信小程序 canvas开发实例及注意事项  微信小程序 HTTPS报错整理常见问题及解决方案  动图在线制作网站有哪些,滑动动图图集怎么做?  非常酷的网站设计制作软件,酷培ai教育官方网站?  Python正则表达式进阶教程_复杂匹配与分组替换解析  详解MySQL数据库的安装与密码配置  b2c电商网站制作流程,b2c水平综合的电商平台?  北京网站制作的公司有哪些,北京白云观官方网站?  js代码实现下拉菜单【推荐】  如何在云主机上快速搭建多站点网站?  JavaScript中的标签模板是什么_它如何扩展字符串功能  黑客入侵网站服务器的常见手法有哪些?  移动端手机网站制作软件,掌上时代,移动端网站的谷歌SEO该如何做?  电商网站制作价格怎么算,网上拍卖流程以及规则?