深入浅出的webpack构建工具---tree shaking打包性能优化(十二)
阅读目录
2. 在webpack中如何使用 tree-shaking 呢?
3. 使用webpack-deep-scope-plugin 优化
1. 什么是tree-shaking?
在webpack中,tree-shaking的作用是可以剔除js中用不上的代码,但是它依赖的是静态的ES6的模块语法。
也就是说没有被引用到的模块它是不会被打包进来的,可以减少我们的包的大小,减少文件的加载时间,提高用户体验。
webpack2版本中就开始引入了 tree shaking的概念,它可以在打包时可以忽略哪些没有被使用到的代码。
注意:要让 Tree Shaking 正常工作的前提是:提交给webpack的javascript代码必须采用了 ES6的模块化语法,因为ES6模块化语法是静态的(在导入,导出语句中的路径必须是静态的字符串)。
2. 在webpack中如何使用 tree-shaking 呢?
在配置代码前,我们来看看我们项目中的目录结构如下:
### 目录结构如下:
demo1 # 工程名
| |--- dist # 打包后生成的目录文件
| |--- node_modules # 所有的依赖包
| |--- js # 存放所有js文件
| | |-- demo1.js
| | |-- main.js # js入口文件
| |--- common # js公用的文件
| | |-- util.js # 公用的util.js文件
| |--- webpack.config.js # webpack配置文件
| |--- index.html # html文件
| |--- styles # 存放所有的css样式文件
| | |-- main.styl # main.styl文件
| | |-- index.styl
| |--- .gitignore
| |--- README.md
| |--- package.json
| |--- .babelrc # babel转码文件
webpack.config.js 代码如下:
const path = require('path');
// 引入 mini-css-extract-plugin 插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 清除dist目录下的文件
const ClearWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
// 引入打包html文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入HappyPack插件
const HappyPack = require('happypack');
module.exports = {
// 入口文件
entry: {
main: './js/main.js'
},
output: {
filename: '[name].[contenthash].js',
// 将输出的文件都放在dist目录下
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 使用正则去匹配
test: /\.styl$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('postcss-cssnext')(),
require('cssnano')(),
require('postcss-pxtorem')({
rootValue: 16,
unitPrecision: 5,
propWhiteList: []
}),
require('postcss-sprites')()
]
}
},
{
loader: 'stylus-loader',
options: {}
}
]
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'happypack/loader?id=css-pack'
]
},
{
test: /\.(png|jpg)$/,
use: ['happypack/loader?id=image']
},
{
test: /\.js$/,
// 将对.js文件的处理转交给id为babel的HappyPack的实列
use: ['happypack/loader?id=babel'],
// loader: 'babel-loader',
exclude: path.resolve(__dirname, 'node_modules') // 排除文件
}
]
},
resolve: {
extensions: ['*', '.js', '.json']
},
devtool: 'cheap-module-eval-source-map',
devServer: {
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
inline: true,
overlay: true,
stats: 'errors-only'
},
mode: 'development',
plugins: [
new HtmlWebpackPlugin({
template: './index.html' // 模版文件
}),
new ClearWebpackPlugin(['dist']),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css'
}),
/**** 使用HappyPack实例化 *****/
new HappyPack({
// 用唯一的标识符id来代表当前的HappyPack 处理一类特定的文件
id: 'babel',
// 如何处理.js文件,用法和Loader配置是一样的
loaders: ['babel-loader']
}),
new HappyPack({
id: 'image',
loaders: [{
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: '[name].[ext]'
}
}]
}),
// 处理styl文件
new HappyPack({
id: 'css-pack',
loaders: ['css-loader']
})
]
};
.babelrc 配置如下:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"modules": false // 关闭Babel的模块转换功能,保留ES6模块化语法
}
],
"stage-2"
]
}
common/util.js 代码如下:
export function a() {
alert('aaaa');
}
export function b() {
alert('bbbbb');
}
export function c() {
alert('cccc');
}
js/main.js 代码如下:
import { a } from '../common/util';
a();
执行 webpack后,打包文件如下:

然后继续查看 dist/main.xxx.js代码如下:
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return b; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "c", function() { return c; }); function a() {
alert('aaaa');
} function b() {
alert('bbbbb');
} function c() {
alert('cccc');
} /***/ }),
如上代码,还是会包含 b,c 两个函数代码进来,那是因为 webpack 想要使用tree-shaking功能的话,我们需要压缩代码,就能把没有引用的代码剔除掉,因此我们需要在webpack中加上压缩js代码如下:
// 引入 ParallelUglifyPlugin 插件
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); module.exports = {
plugins: [
// 使用 ParallelUglifyPlugin 并行压缩输出JS代码
new ParallelUglifyPlugin({
// 传递给 UglifyJS的参数如下:
uglifyJS: {
output: {
/*
是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,
可以设置为false
*/
beautify: false,
/*
是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
*/
comments: false
},
compress: {
/*
是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用
不大的警告
*/
warnings: false, /*
是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
*/
drop_console: true, /*
是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不
转换,为了达到更好的压缩效果,可以设置为false
*/
collapse_vars: true, /*
是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx' 转换成
var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false
*/
reduce_vars: true
}
}
})
]
}
再运行下打包命令后。我们继续查看代码,如下所示:

可以看到还是会把无用的 b函数 和 c函数代码打包进去。这是什么情况?那是因为我们在webpack中配置了 mode: 'development',我们现在把它改成 mode: 'production',后,就可以看到只用 a函数了,我们可以到dist目录下的main.js代码内部搜索下 alert, 就可以看到了,只有一个alert('a')了。说明b函数和c函数被剔除掉了。
tree-shaking 目前的缺陷:
tree-shaking 能够利用ES6的静态引入规范,减少包的体积,避免不必要的代码引入,但是webpack只能做一点简单的事情。
比如 我现在在main.js代码改成如下:
import { func2 } from '../common/util';
var a = func2(222);
alert(a);
common/util.js 代码如下:
import lodash from 'lodash-es'
var func1 = function(v) {
alert('111');
return lodash.isArray(v);
}
var func2 = function(v) {
return v;
};
export {
func1,
func2
}
如上代码,在main.js中引入了 func2, 但是并没有引入func1, 但是func1引入了lodash-es。webpack在检查的时候发现func1中确实用到了lodash-es,因此不会把lodash去掉,但是func1函数会去掉的。但是我们在js中也并没有使用到lodash。因此在这种情况下,webpack中的 tree-shaking 解决不了这种情况,因此 webpack-deep-scope-plugin 插件就可以解决这种问题了,如下没有使用 webpack-deep-scope-plugin 插件打包后的文件大小。如下:
如上main.js 打包压缩后的js代码大小有81.1kb。打开dist/main.js代码搜索 lodash后,可以搜索到,因此lodash插件被打包进去main.js中了,但是实际上我们项目并没有使用到lodash,因此lodash的库我们按常理来讲并不需要打包进去的。
3. 使用webpack-deep-scope-plugin 优化
1. 首先需要安装 webpack-deep-scope-plugin, 安装命令如下:
npm i -D webpack-deep-scope-plugin
在webpack.config.js 代码引入如下:
// 引入 webpack-deep-scope-plugin 优化
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default; module.exports = {
plugins: [
new WebpackDeepScopeAnalysisPlugin()
]
}
然后我们继续打包如下所示:

打包后发现如上,只有969字节,1kb都不到,再打开dist/main.js 查看代码,搜索下 lodash, 发现搜索不到。
注意点:
1. 要使用 tree-shaking,必须保证引用的插件的模块是ES6模块规范编写的,这也是我为什么引用了的是 lodash-es,而不是 'lodash', 如果引用的是lodash的话,是不能去掉的。
2. 在 .babelrc 中,babel设置 module: false, 避免babel将模块转换为成 CommonJS规范。引入模块包也必须符合ES6规范的。如下 babelrc代码:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"modules": false // 关闭Babel的模块转换功能,保留ES6模块化语法
}
],
"stage-2"
]
}
且需要在 package.json 中定义 sideEffect: false, 这也是为了避免出现 import xxx 导致模块内部的一些函数执行后影响全局环境, 却被去除掉的情况.
3. webpack-deep-scope-plugin 插件依赖 node8.0+ 和 webpack 4.14.0 +
深入浅出的webpack构建工具---tree shaking打包性能优化(十二)的更多相关文章
- 深入浅出的webpack构建工具--webpack4+vue+router项目架构(十四)
阅读目录 一:vue-router是什么? 二:vue-router的实现原理 三:vue-router使用及代码配置 四:理解vue设置路由导航的两种方法. 五:理解动态路由和命名视图 六:理解嵌套 ...
- 深入浅出的webpack构建工具--webpack4+vue搭建环境 (十三)
深入浅出的webpack构建工具--webpack4+vue搭建环境 (十三) 从上面一系列的webpack配置的学习,我们现在来使用webpack来搭建vue的开发环境.首先我们来设想下我们的项目的 ...
- 深入浅出的webpack构建工具---AutoDllPlugin插件(八)
深入浅出的webpack构建工具---AutoDllPlugin插件(八) DllPlugin插件能够快速打包,能把第三方依赖的文件能提前进行预编译打包到一个文件里面去.提高了构建速度.因为很多第三方 ...
- 深入浅出的webpack构建工具---DevServer配置项(二)
深入浅出的webpack构建工具---DevServer配置项(二) 阅读目录 DevServer配置项 1. contentBase 2. port 3. host 4. headers 5. hi ...
- 深入浅出的webpack构建工具---webpack基本配置(一)
深入浅出的webpack构建工具---webpack基本配置(一) 阅读目录 一:webpack入门构建: 1. 安装webpack到全局 2. 安装webpack到本项目. 3. 如何使用webpa ...
- 深入浅出的webpack构建工具---PostCss(五)
一:PostCss是什么? PostCss是一个样式处理工具,它通过自定义的插件和工具生态体系来重新定义css.它鼓励开发者使用规范的css原生语法编写代码,然后配置编译器转换需要兼容的浏览器版本, ...
- 深入浅出的webpack构建工具---HappyPack优化构建(九)
阅读目录 一:什么是HappyPack? 作用是什么? 二:如何在配置中使用HappyPack? 回到顶部 一:什么是HappyPack? 作用是什么? Webpack是允许在NodeJS中的,它是单 ...
- 深入浅出的webpack构建工具---DllPlugin DllReferencePlugin提高构建速度(七)
阅读目录 一:什么是DllPlugin 和 DllReferencePlugin?作用是什么? 二:在项目中如何使用 DllPlugin 和 DllReferencePlugin? 三:DllPlug ...
- 深入浅出的webpack构建工具---devTool中SourceMap模式详解(四)
阅读目录 一:什么是SourceMap? 二:理解webpack中的SourceMap的eval,inline,sourceMap,cheap,module 三:开发环境和线上环境如何选择source ...
随机推荐
- mongodb 权限设置--用户名、密码、端口
转自:http://www.cnblogs.com/valor-xh/p/6369432.html 一.关于权限的默认配置 在默认情况下,mongod是监听在0.0.0.0之上的,任何客户端都可以直接 ...
- Java String的简单介绍
一.String类的构造方法(先粗略介绍三种 分别是s1,s2,s3) 二.String的常用判断方法 三.String类的常用获取方法 三.Sting的常用转换方法 四.String其他功能 五 ...
- layui switch 开关监听 弹出确定状态转换
不废话,直接上图: 原始状态: 点击确定: 点击取消或者X 代码: <!doctype html> <html lang="en"> <head& ...
- Django框架理解和使用常见问题
1.什么是中间件? 中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出. 中间件一般做认证或批量请求处理,django中的中间 ...
- 【读书笔记】iOS-iCloud介绍
iCloud是一种面向消费者市场的云存储服务,苹果公司已经做了大量的工作让用户能够平滑过渡到iCloud,不过对开发者而言这意味着新的负担. 怎样使用iCloud? 你可以使用2种方式在你的应用中使用 ...
- js 键盘码
键盘各按键对应的数字 keycode 9 = Tab keycode 12 = Clear keycode 13 = Enter keycode 16 = Shift keycode 17 = Con ...
- 纯小白入手 vue3.0 CLI - 3.2 - 路由的初级使用
vue3.0 CLI 真小白一步一步入手全教程系列:https://www.cnblogs.com/ndos/category/1295752.html 尽量把纷繁的知识,肢解重组成为可以堆砌的知识. ...
- Salesforce Live Agent 简介
Live Agent Salesforce 内置了即时聊天功能--Live Agent,可以用来作为客服工具. 本文简单介绍了使用 Live Agent 的步骤,主要包含: 启用 Live Agent ...
- 【BI学习笔记】适合集成到项目里的BI:Wyn Enterprise
近日,我收到葡萄城的邮件,讲葡萄城开始进军BI领域,发布了一套BI和报表工具,叫Wyn Enterprise. 之前了解过PowerBI等面向终端用户的BI工具,我发现BI工具与Wyn Enterpr ...
- 《ASP.NET MVC企业实战》(二) MVC开发前奏
在上一篇“<ASP.NET MVC企业级实战>(一)MVC开发前奏”中记录了作者介绍的一些比较实用的VS使用方法以及C#2.0中添加的新特性.本篇继续大概了解之后版本的一些新特性. ...