Webpack —— tree-starking 解析

栏目: 编程工具 · 发布时间: 6年前

内容简介:假设我们现在使用了 ElementUI 库的两个组件,通常会使用解构赋值来引入。这样引用资源, Webpack 在打包的时候会找到
Webpack —— tree-starking 解析

tree-sharking 简介

tree-sharkingWebpack 2 后续版本的优化功能,顾名思义,就是将多余的代码给 “摇晃” 掉,在开发中我们经常使用一些第三方库,而这些第三方库只使用了这个库的一部门功能或代码,未使用的代码也要被打包进来,这样出口文件会非常大, tree-sharking 帮我们解决了这个问题,它可以将各个模块中没有使用的方法过滤掉,只对有效代码进行打包。

AST 语法树分析

假设我们现在使用了 ElementUI 库的两个组件,通常会使用解构赋值来引入。

// 优化前
import { Button, Alert } from "element-ui";
复制代码

这样引用资源, Webpack 在打包的时候会找到 element-ui 并把里面所有的代码全部打包到出口文件,我们只使用了两个组件,全部打包不是我们所希望的, tree-sharking 是通过在 Webpack 中配置 babel-plugin-import 插件来实现的,它可以将解构的代码转换成下面的形式。

// 优化后
import Button from "element-ui/lib/button";
import Alert from "element-ui/lib/Alert";
复制代码

转化后会去 node_modules 中的 element-ui 模块找到 ButtonAlert 两个组件对应的文件,并打包到出口文件中。

通过上面的转换可以看出,其实 tree-sharking 的实现原理是通过改变 AST 语法树的结构来实现的,如果不了解抽象语法树可以参考AST 抽象语法树,我们可以通过在线转换网站 esprima.org/demo/parse.… 将 JS 代码装换成 AST 语法树。

优化前的 AST 语法树:

{
    "type": "Program",
    "body": [
        {
            "type": "ImportDeclaration",
            "specifiers": [
                {
                    "type": "ImportSpecifier",
                    "local": {
                        "type": "Identifier",
                        "name": "Button"
                    },
                    "imported": {
                        "type": "Identifier",
                        "name": "Button"
                    }
                },
                {
                    "type": "ImportSpecifier",
                    "local": {
                        "type": "Identifier",
                        "name": "Alert"
                    },
                    "imported": {
                        "type": "Identifier",
                        "name": "Alert"
                    }
                }
            ],
            "source": {
                "type": "Literal",
                "value": "element-ui",
                "raw": "\"element-ui\""
            }
        }
    ],
    "sourceType": "module"
}
复制代码

优化后的 AST 语法树:

{
    "type": "Program",
    "body": [
        {
            "type": "ImportDeclaration",
            "specifiers": [
                {
                    "type": "ImportDefaultSpecifier",
                    "local": {
                        "type": "Identifier",
                        "name": "Button"
                    }
                }
            ],
            "source": {
                "type": "Literal",
                "value": "element-ui/lib/button",
                "raw": "\"element-ui/lib/button\""
            }
        },
        {
            "type": "ImportDeclaration",
            "specifiers": [
                {
                    "type": "ImportDefaultSpecifier",
                    "local": {
                        "type": "Identifier",
                        "name": "Alert"
                    }
                }
            ],
            "source": {
                "type": "Literal",
                "value": "element-ui/lib/Alert",
                "raw": "\"element-ui/lib/Alert\""
            }
        }
    ],
    "sourceType": "module"
}
复制代码

从上面的语法树对比,可以看出在优化前 body 里面只有一个对象,使用的组件信息存在 specifiers 里, source 指向了 element-ui ,而在优化后,将两个组件分别拆成了两个对象存在 body 中,每个对象的的 specifiers 只存储一个组件,并在 source 里面指向了当前组件对应的路径。

模拟 tree-starking

既然我们已经清楚要修改语法树的位置,下面就使用 AST 来模拟 tree-sharking 功能,对语法树的操作是依赖于 babel-corebabel-types 两个核心模块的,下面先安装依赖。

npm install babel-core babel-types
// 文件:babel-plugin-my-import.js
const babel = require("babel-core");
const types = require("babel-types");

let code = `import { Button, Alert } from "element-ui"`;

let importPlugin = {
    visitor: {
        ImportDeclaration(path) {
            let node = path.node;
            let source = node.source.value;
            let specifiers = node.specifiers;

            // 判断是否是默认导出,其中一个不是默认导出,则都不是默认导出
            if (!types.isImportDefaultSpecifier(specifiers[0])) {
                // 如果不是默认导出,则需要转换
                specifiers = specifiers.map(specifier => {
                    // 数组内容:当前默认导出的标识、从哪里导入
                    return types.importDeclaration(
                        [types.importDefaultSpecifier(specifier.local)],
                        types.stringLiteral(`${source}/lib/${specifier.local.name.toLowerCase()}`)
                    );
                });

                // 替换树结构
                path.replaceWithMultiple(specifiers);
            }
        }
    }
};

let result = babel.transform(code, {
    plugins: [importPlugin]
});

console.log(result.code);

// import Button from "element-ui/lib/button";
// import Alert from "element-ui/lib/alert";
复制代码

通过上面的代码可以发现我们使用 babel-corebabel-types 两个模块的核心方法对语法书进行了遍历、修改和替换,更详细的 API 可以查看 github.com/babel/babel…

结合 Webpack 使用插件

前面只是验证了 tree-sharking 中 JS 语法的转换过程,接下来将上面的代码转换成插件配合 Webpack 使用,来彻底感受 tree-sharking 的工作过程。

// 文件:~node_modules/babel-plugin-my-import.js
const babel = require("babel-core");
const types = require("babel-types");

let importPlugin = {
    visitor: {
        ImportDeclaration(path) {
            let node = path.node;
            let source = node.source.value;
            let specifiers = node.specifiers;

            // 判断是否是默认导出,其中一个不是默认导出,则都不是默认导出
            if (!types.isImportDefaultSpecifier(specifiers[0])) {
                // 如果不是默认导出,则需要转换
                specifiers = specifiers.map(specifier => {
                    // 数组内容:当前默认导出的标识、从哪里导入
                    return types.importDeclaration(
                        [types.importDefaultSpecifier(specifier.local)],
                        types.stringLiteral(`${source}/lib/${specifier.local.name.toLowerCase()}`)
                    );
                });

                // 替换树解构
                path.replaceWithMultiple(specifiers);
            }
        }
    }
};

module.exports = importPlugin;
复制代码

上面删掉了多余的测试代码,将模块中的 importPlugin 插件导出,并把 babel-plugin-my-import.js 移入了 node_modules 当中。

接下来安装需要的依赖:

npm install webpack webpack-cli babel-loader babel-presets-env
npm install vue element-ui --save

安装完依赖,写一个要编译的文件,使用 Webpack 进行打包,查看使用插件前和使用插件后出口文件的大小。

// 文件:import.js
import Vue from "vue";
import { Button, Alert } from "element-ui";
复制代码

下面来写一个简单的 Webpack 配置文件。

// 文件:webpcak.config.js
module.exports = {
    mode: "development",
    entry: "import.js",
    output: {
        filename: "bundle.js",
        path: __dirname
    },
    module: {
        rules: [{
            test: /\.js$/,
            use: {
                loader: "babel-loader",
                options: {
                    presets: [
                        "env",
                    ],
                    plugins: [
                        // 插件:不使用插件打包注释掉该行即可
                        ["my-import", { libararyName: "element-ui" }]
                    ]
                }
            },
            exclude: /node_modules/
        }]
    }
};
复制代码

为了防止 babel 相关的依赖升级 7.0 后出现一些问题导致 Webpack 无法启动,再此贴出 package.json 文件,按照对应版本下载依赖保证上面 Webpack 配置生效。

文件:package.json

{
  "name": "ast-lesson",
  "version": "1.0.0",
  "description": "tree-starking",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.5",
    "babel-preset-env": "^1.7.0",
    "babel-types": "^6.26.0",
    "escodegen": "^1.10.0",
    "esprima": "^4.0.0",
    "estraverse": "^4.2.0",
    "webpack": "^4.16.0",
    "webpack-cli": "^3.0.8"
  },
  "devDependencies": {
    "vue": "^2.5.17",
    "element-ui": "^2.4.6"
  }
}
复制代码

对比使用插件前后的出口文件

接下来分别在使用插件和不使用插件时执行打包命令,查看出口文件 bondle.js 的大小。

npx webpack

使用 babel-plugin-my-import 前:

Webpack —— tree-starking 解析

使用 babel-plugin-my-import 后:

Webpack —— tree-starking 解析

通过对比,可以看到使用 tree-sharking 即我们自己实现的 babel-plugin-my-import 插件后,打包的出口文件大大减小,其原因是将引入第三方库没有使用的代码全都过滤掉了,只打包了有效代码。


以上所述就是小编给大家介绍的《Webpack —— tree-starking 解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

可计算性和计算复杂性

可计算性和计算复杂性

朱一清 / 国防工业出版社 / 2006-4 / 18.0

本书深入浅出地介绍了研究可计算性的四个主要模型以及四个模型彼此之间的关系:介绍了计算复杂性的基本概念和重要的研究方法与一些研究成果。内容涉及递归函数、图灵机、λ演算、马尔可夫算法、计算复杂度的分类、NP完全理论、非一致复杂性等。分述于十章,书中附有习题。 本书可作为广大有志于突破计算复杂性研究僵局——“P=NP?”的科技工作者,计算机科学和元计算机科学工作者,数学和元数学工作者以及大......一起来看看 《可计算性和计算复杂性》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具