Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack tree shaking #9

Open
mbaxszy7 opened this issue Jun 19, 2020 · 0 comments
Open

webpack tree shaking #9

mbaxszy7 opened this issue Jun 19, 2020 · 0 comments
Labels

Comments

@mbaxszy7
Copy link
Owner

之前对tree shaking的认识仅仅停留在无用代码剔除上。今天来深究总结一下。

什么是tree shaking

Tree shaking is a term commonly used in the JavaScript context for dead-code elimination

Tree shaking是一个用在js中删除无用代码的术语。

在webpack 2版本中webpack内置支持了ES2015 modules,并且也支持了无用模块的导出检测。webpack 4版本在此功能上进行了扩展,并通过在package.json 中添加“ sideEffects” 属性向编译器提供提示,以标示项目中的哪些文件是“纯”的,从而可以安全的移除。

背后依据的原理

所以tree shaking背后依据的原理肯定和es module息息相关— 依赖的是ES6模块的静态分析能力,体现在:

  • Export和import 只能出现在文件代码的顶层
  • 使用 import 导入的变量是readonly的,类似const

demo实验

下面的例子都是用最新版的webpack

一个tree shaking可行的demo

// test.js:
export const one = function (value) {
  console.log("this is one");
  return value
};
export const two = function (value) {
  console.log("this is two");
  return value;
};

// index.js
import { one, two } from "./test";
console.log(two("test"));

在生产环境下打包(下同),发现函数one没有被打包进去:

WeChat340d8eb3ba34b562aec8e66085b46ee7

一个tree shaking不可行的demo

// test-one.js
window.testOne = () => {
  console.log("this is window test one")
}

export const anotherTest = () => {
  console.log("another test")
}

// test.js
import { anotherTest } from "./test-one.js";

export const one = function (value) {
  console.log("this is one");
  return value;
};
export const two = function (value) {
  console.log("this is two");
  return value;
};

// index.js
import { one, two } from "./test";
console.log(two("test"))

打包后:

WeChat97ab32591ccdc1d089b2cdbf04833e95

可以看到test-one.js 中的anotherTest虽然已经exported,因为没有被使用,所以被tree shaking了。但是,test-one.js中的window.testOne代码却被打包了。如果我想要的效果是:test-one.js export 的内容没有用到就不需要打包该怎么办呢?下面来介绍一下package.json中可以添加的一个属性sideEffects。

sideEffects

sideEffects 顾名思义是side effects,也就是副作用(函数式编程中的一个名词)。在webpack4中,通过在package.json 中添加"sideEffects": false , 可以向webpack指明整个项目是没用副作用的,可以安全的 tree-shaking 。sideEffects 也可以接受一个数组,数组的每一项是文件路径,用于保留这些文件的副作用:

"sideEffects":["./src/global.config.js"]

用sideEffects解决第二个demo的问题

在package.json中配置"sideEffects": false 后,我们再来打包看一下结果:
WeChat67144c387c48827c058d21959fddffdb
我们可以看到window.testOne确实没有被打包了

sideEffects 的注意点

有时候在项目中我们确实想“引入一些有副作用的文件”,比如我们想在window对象上定义一些js函数,供native端调用 (js bridge):

 // global.js
window.NativeBridge = {
  share: () => {
    console.log("this is native share")
  }
}

此时我们在index.js中调用global.js

import { one, two } from "./test";
import "./global.js"

console.log(two("test"));

打包后发现global.js根本没有被打包进文件。导致这种错误的原因是:我们是通过import "./global.js" 引入文件的,webpack 会把所有import "xxx" 看做是引入了文件,但是没有使用的。 如果此时在package.json中配置"sideEffects": false ”, 那么就global.js会被 tree-shaking 。

解决中类似import "xxx" 的方法是在`"sideEffects”中表明有副作用的文件:

"sideEffects":["./src/global.js"]

Tree-shaking 目前的局限

直接上代码:

  // test.js
import { isDate } from "lodash-es"

export const one = function (value) {
  console.log("this is one");
  return isDate(value);
};

export const two = function (value) {
  console.log("this is two");
  return value;
};

// index.js
import { one, two } from "./test";
import "./global.js"

console.log(two("test"));

打包后:
WeChat24790fee69486bc61652b58b3fc0d7d3
可以看到虽然函数one没有没使用,但是lodash-es的部分代码还是被打包了。

解决方法:

  1. 把one函数修改为纯函数,用到的lodash的isDate方法作为依赖注入:
export const one = function (value, isDate ) {
  console.log("this is one");
  return isDate(value);
};
  1. 使用第三方webpack插件: webpack-deep-scope-analysis-plugin

关于babel

为了确保babel不会将代码编译为commonjs,配置 Babel preset @babel/preset-env 的modules属性为false

总结

想要在webpack 中更好的使用tree shaking,那么

  1. 需要使用ES6 模块
  2. 在使用babel的时候,不能编译转化为非es6模块
  3. 合理的加sideEffects。其实sideEffects就是来通知webpack可以安全的进行tree-shaking的,如果有些包真的是有副作用, 那么也可以在sideEffects中配置。
  4. 在写代码时尽量考虑到副作用的产生,合理避免。

Webpack4: Tree-shaking 深度解析
Tree Shaking

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant