[rollup]tree-shaking实践

1,310 阅读2分钟

什么是tree-shaking

tree-shaking, 直译摇树, 是利用es module的静态分析, 可删除掉无用代码, 从而减少代码体积.tree-shakingrollupwebpack里都有实现

参考: rollup官网文档

如何使用tree-shaking

sdk开发者对外提供三个api: fnA, fnB, fnC, 其中fnAfnB都调用了fnC

/**
 * @description 这是函数A
 */

export async function fnA(str: string): Promise<string> {
  return `AAAAAA` + fnC(str)
}

/**
 * @description 这是函数B
 */
export function fnB(str: string): Promise<string> {
  return Promise.resolve(`BBBBBB` + fnC(str))
}

/**
 * @description 这是函数C
 */
export function fnC(str: string): string {
  return `CCCCCC` + str
}

例子一

sdk使用者只引用了fnA

import * as sdk from './sdk'

var useApis = {
  fnA: sdk.fnA
}

useApis.fnA('test')

查看构建后的代码

/**
 * @description 这是函数A
 */
async function fnA(str) {
    return `AAAAAA` + fnC(str);
}
/**
 * @description 这是函数C
 */
function fnC(str) {
    return `CCCCCC` + str;
}

var useApis = {
    fnA: fnA
};
useApis.fnA('test');

可以看到函数B相关的源码被tree-shaking掉了, 摇树成功

此使用sdk的方式等同于

import { fnA } from './sdk'

var useApis = {
  fnA,
}

useApis.fnA('test')

如下是构建后的代码, 也是成功的删除了fnB相关的源码

/**
 * @description 这是函数A
 */
async function fnA(str) {
    return `AAAAAA` + fnC(str);
}
/**
 * @description 这是函数C
 */
function fnC(str) {
    return `CCCCCC` + str;
}

var useApis = {
    fnA,
};
useApis.fnA('test');

例子二

sdk使用者引用了全部的api, 只调用了fnA

import * as sdk from './sdk'

var useApis = {
  ...sdk
}

useApis.fnA('test')

查看构建后的代码

/**
 * @description 这是函数A
 */
async function fnA(str) {
    return `AAAAAA` + fnC(str);
}
/**
 * @description 这是函数B
 */
function fnB(str) {
    return Promise.resolve(`BBBBBB` + fnC(str));
}
/**
 * @description 这是函数C
 */
function fnC(str) {
    return `CCCCCC` + str;
}

var sdk = /*#__PURE__*/Object.freeze({
  __proto__: null,
  fnA: fnA,
  fnB: fnB,
  fnC: fnC
});

var useApis = {
    ...sdk
};
useApis.fnA('test');

中包含了所有函数的源码, 因为是静态分析, 不能判断哪个函数是否被调用, 摇树失效

例子三

sdk开发者对外提供一个默认对象, 包含全部的api

/**
 * @description 这是函数A
 */

async function fnA(str: string): Promise<string> {
  return `AAAAAA` + fnC(str)
}

/**
 * @description 这是函数B
 */
function fnB(str: string): Promise<string> {
  return Promise.resolve(`BBBBBB` + fnC(str))
}

/**
 * @description 这是函数C
 */
function fnC(str: string): string {
  return `CCCCCC` + str
}

export default {
  fnA,
  fnB,
  fnC,
}

sdk使用者引用了全部的api, 只调用了fnA

import sdk from './sdk'
const { fnA } = sdk

var useApis = {
  fnA
}

useApis.fnA('test')

查看构建后的代码

/**
 * @description 这是函数A
 */
async function fnA(str) {
    return `AAAAAA` + fnC(str);
}
/**
 * @description 这是函数B
 */
function fnB(str) {
    return Promise.resolve(`BBBBBB` + fnC(str));
}
/**
 * @description 这是函数C
 */
function fnC(str) {
    return `CCCCCC` + str;
}
var sdk = {
    fnA,
    fnB,
    fnC,
};

const { fnA: fnA$1 } = sdk;
var useApis = {
    fnA: fnA$1
};
useApis.fnA('test');

显然这也不是我们想要的效果

总结: 为了利用好tree-shaking, sdk的开发者和使用者都需要注意模块的导入导出方式

注意,本文为更好的展现构建后的代码, 将ts配置文件的编译目标设置成了esnext, 如有需要可设置成es5或者es3