不要再依赖 CommonJS 了
云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!
什么是 CommonJS?
CommonJS 是 2009 年的标准,为 JavaScript 模块建立了约定。它最初打算在 Web 浏览器之外的场景中使用,主要用于服务端应用程序。
使用 CommonJS,你可以定义模块,从中导出功能,并将它们导入其他模块中。例如,下面的代码片段定义了一个模块,其导出五个函数:add,subtract,multiply,divide 和 max:
// utils.js const { maxBy } = require('lodash-es'); const fns = { add: (a, b) => a + b, subtract: (a, b) => a - b, multiply: (a, b) => a * b, divide: (a, b) => a / b, max: arr => maxBy(arr) }; Object.keys(fns).forEach(fnName => module.exports[fnName] = fns[fnName]);
稍后,另一个模块可以导入和使用这些函数:
// index.js const { add } = require(‘./utils'); console.log(add(1, 2));
使用 node 调用 index.js 将在控制台中输出数字 3。
由于 2010 年代初期浏览器中缺乏标准化的模块系统,CommonJS 也成为了 JavaScript 客户端库的流行模块格式。
CommonJS 如何影响最终的打包大小?
服务端 JavaScript 应用程序的大小并不像浏览器中那样重要,所以 CommonJS 并没有在设计时考虑到包大小的控制。与此同时,有分析表明 JavaScript 的包体积仍然是拖慢浏览器应用的主要因素之一。
JavaScript 打包器和压缩器(minifier),例如 webpack 和 terser,会执行多种优化措施以减小应用程序的大小。它们在构建时分析你的应用程序,尝试尽可能删掉那些没用到的源代码。
例如,在上面的代码片段中,你的最终打包应该只包括 add 函数,因为这是你从 utils.js 中导入到 index.js 中的唯一符号。
我们使用以下 webpack 配置来构建这个应用:
const path = require('path'); module.exports = { entry: 'index.js', output: { filename: 'out.js', path: path.resolve(__dirname, 'dist'), }, mode: 'production', };
在这里,我们指定了要使用生产模式优化并将 index.js 用作入口点。调用 webpack 之后,如果我们查看输出大小,将看到下面这样的内容:
$ cd dist && ls -lah 625K Apr 13 13:04 out.js
请注意,这个包的大小为 625KB。看一下输出,我们将找到来自 utils.js 的所有函数,外加来自 lodash 的很多模块。尽管我们在 index.js 中不使用 lodash,但它也被加进了输出,这给我们的生产资产增加了很多额外负担。
现在我们将模块格式更改为 ECMAScript 2015,然后重试。这次,utils.js 将变成如下所示:
export const add = (a, b) => a + b; export const subtract = (a, b) => a - b; export const multiply = (a, b) => a * b; export const divide = (a, b) => a / b; import { maxBy } from 'lodash-es'; export const max = arr => maxBy(arr);
并且 index.js 将使用 ES2015 模块语法从 utils.js 导入:
import { add } from './utils'; console.log(add(1, 2));
使用相同的 webpack 配置,我们可以构建应用程序并打开输出文件。现在大小只有 40 字节,输出如下:
(()=>{"use strict";console.log(1+2)})();
请注意,最后的打包中并没有包含 utils.js 中我们没有用到的任何函数,而且也没有 lodash 的痕迹!更进一步,terser(webpack 使用的 JavaScript 压缩器)在 console.log 中内联了 add 函数。
你可能会问一个问题,为什么使用 CommonJS 会导致输出包大了接近 16,000 倍?当然,上面这个应用只是一个简单的示例,实际应用中的体积差异可能没那么大,但 CommonJS 也很有可能给你的生产构建增添了很大的负担。
一般情况下,CommonJS 模块难以优化,因为它们比 ES 模块动态得多。为确保打包器和压缩器可以成功优化应用程序,请避免依赖 CommonJS 模块,并在整个应用程序中使用 ES2015 模块语法。
请注意,即使你在 index.js 中使用了 ES2015,但如果你使用的模块是 CommonJS,应用程序的打包大小也会受到影响。
为什么 CommonJS 会让应用程序体积更大?
为了回答这个问题,我们将研究 webpack 中 ModuleConcatenationPlugin 的行为,然后讨论静态可分析性。这个插件将所有模块合并为一个闭包,并能让你的代码在浏览器中执行得更快。我们来看一个例子:
// utils.js export const add = (a, b) => a + b; export const subtract = (a, b) => a - b;
// index.js import { add } from ‘./utils'; const subtract = (a, b) => a - b; console.log(add(1, 2));
如上所示,我们有一个 ES2015 模块,然后将其导入 index.js 中。我们还定义了一个 subtract 函数。我们可以使用与上面相同的 webpack 配置来构建项目,但是这次我们将禁用最小化:
const path = require('path'); module.exports = { entry: 'index.js', output: { filename: 'out.js', path: path.resolve(__dirname, 'dist'), }, optimization: { minimize: false }, mode: 'production', };
看一下生成的输出:
/******/ (() => { // webpackBootstrap /******/ "use strict"; // CONCATENATED MODULE: ./utils.js** const add = (a, b) => a + b; const subtract = (a, b) => a - b; // CONCATENATED MODULE: ./index.js** const index_subtract = (a, b) => a - b;** console.log(add(1, 2));** /******/ })();
在上面的输出中,所有函数都在同一个命名空间内。为了防止冲突,webpack 将 index.js 中的 subtract 函数重命名为 index_subtract。
如果让一个压缩器处理上面的源代码,它将:
- 删除未使用的 subtract 和 index_subtract 函数
- 删除所有注释和多余的空格
- 在 console.log 调用中内联 add 函数的主体
开发人员通常将这种移除未使用的导入的操作称为摇树优化(tree-shaking)。因为 webpack 能够静态地(在构建时)了解我们从 utils.js 导入及导出的符号,所以它才能实现摇树优化。
ES 模块默认启用此行为,因为与 CommonJS 相比,它们更容易进行静态分析。
我们来看完全相同的示例,但是这次将 utils.js 更改为使用 CommonJS 模块:
// utils.js const { maxBy } = require('lodash-es'); const fns = { add: (a, b) => a + b, subtract: (a, b) => a - b, multiply: (a, b) => a * b, divide: (a, b) => a / b, max: arr => maxBy(arr) }; Object.keys(fns).forEach(fnName => module.exports[fnName] = fns[fnName]);
这个小小的更新会显著影响输出结果。受限于文章篇幅,这里我只分享其中的一小部分:
... (() => { "use strict"; /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(288); const subtract = (a, b) => a - b; console.log((0,_utils__WEBPACK_IMPORTED_MODULE_0__/* .add */ .IH)(1, 2)); })();
请注意,最终的打包包含一些 webpack“运行时”:也就是注入的代码,负责从打包的模块中导入 / 导出功能。这次,我们不是将 utils.js 和 index.js 中的所有符号放在同一个命名空间下,而是在运行时动态请求使用 webpack_require 的 add 函数。
这是必需的,因为使用 CommonJS,我们可以从任意表达式中获取导出名称。例如,下面的代码是绝对有效的构造:
module.exports[localStorage.getItem(Math.random())] = () => { … };
打包器无法在构建时知道导出的符号是什么名称,因为这里需要的信息在用户浏览器的上下文中,而且仅在运行时可用。
这样压缩器就无法从 index.js 的依赖项中了解它到底使用了哪些内容,因此无法将无用代码优化掉。我们还能观察到第三方模块也有完全相同的行为。如果我们从 node_modules 导入 CommonJS 模块,你的构建工具链将无法正确优化它。
基于 CommonJS 实现摇树优化
由于 CommonJS 模块是动态定义的,因此它们分析起来要困难得多。例如,与 CommonJS 相比,ES 模块中的导入位置始终是一个字面量(前者则是一个表达式)。
在某些情况下,如果你使用的库遵循有关 CommonJS 用法的特别约定,则可以在构建时使用这个第三方 webpack 插件删除未使用的导出。但尽管这个插件增加了对摇树优化的支持,但并未涵盖依赖项使用 CommonJS 的所有可能方式。这意味着你无法获得与 ES 模块相同的保障。此外,除了默认的 webpack 行为外,它还会在构建过程中增加额外的成本。
结论
总之,再次强调,为了确保打包器可以成功优化你的应用程序,请避免依赖 CommonJS 模块,并在整个应用程序中使用 ES2015 模块语法。
【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK
原文发布时间:2020-05-20
本文作者:Minko Gechev
本文来自:“InfoQ”,了解相关信息可以关注“InfoQ”
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
下一代人行横道利用物联网技术提高安全性
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 智能交通基础设施技术提供商Applied Information正在引入可配置的人行横道安全系统(PCSS),以提高基于物联网(IoT)技术的人行横道的安全性。 PCSS是Glance智能城市监控系统产品套件的一部分,可以通过平板电脑或智能手机对其进行监控和管理。 在另一份公告中,Applied Information还获得了两个额外的实验许可,可以在德克萨斯州和夏威夷州测试蜂窝电话到万物(C-V2X)连接的车辆应用。 远程监控 基本配置包括带有按钮激活的闪光灯的人行横道标志、IoT连接、组合式探测器、Glance远程监控以及与TravelSafely智能手机应用程序的连接。 其他功能包括可配置的消息标志和基于雷达的驾驶员反馈。可以无线连接多个单元,以进城市交通道路的部署。而且,所有设备可能都是太阳能的。 应用信息业务发展和营销副总裁彼得·阿什利说:“人行横道应该是行人的安全场所,但是在美国,每年有500多人死在人行横道上。” “PCSS通过使用多种技术,创建了一个主动安全区,使行人和驾...
- 下一篇
轻松扩展机器学习能力:如何在Rancher上安装Kubeflow
随着机器学习领域不断发展,对于处理机器学习的团队来说,在1台机器上训练1个模型已经有些难以为继,并且现在业界的共识是机器学习已经不仅仅是简单的模型训练。 在模型训练之前、过程中和之后,需要进行许多活动,对于要生成自己的ML模型的团队来说尤其如此。下图常常被引用来说明此类情况: 对于许多团队来说,将机器学习的模型从研究环境应用到生产环境这一过程困难重重,背负很大的压力。糟糕的是,市面上处理每类问题的工具都数量惊人,而这些海量工具都有望解决你所有的机器学习难题。 但是整个团队学习新工具通常很耗时,并且将这些工具集成到你当前的工作流程中也并不容易。这时,或许可以考虑Kubeflow,这是为需要建立机器学习流水线的团队而打造的一个机器学习平台,它包括许多其他工具,可以用于服务模型和调整超参数。Kubeflow尝试做的是将同类最好用的ML工具整合在一起,并将它们集成到一个平台中。 来源:https://www.kubeflow.org/docs/started/kubeflow-overview/ 顾名思义,Kubeflow应该部署在Kubernetes上,既然你是通过Rancher的平台阅读到...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 设置Eclipse缩进为4个空格,增强代码规范
- Mario游戏-低调大师作品
- MySQL8.0.19开启GTID主从同步CentOS8