我想很多“前端工程师”都听过说过 “JavaScript 模块”,那你们都知道如何处理它,以及它在日常工作中如何发挥作用吗?

JS 模块系统到底是什么呢

随着 JavaScript 开发越来越广泛,命名空间和依赖项变得越来越难以处理,极客们早已经开发出不同的模块系统解决方案来解决该问题。

为什么理解 JS 模块系统很重要

我的日常工作是设计和项目架构,并且我很快意识到跨项目需要许多通用功能。我总是一次又一次地将这些功能复制粘贴到新项目中。

问题是,每当更改一部分代码时,我都需要在所有项目中手动同步这些更改。为了避免所有这些繁琐的手动任务,我决定提取通用功能并从中组成一个 NPM 软件包。这样,团队中的其他人将能够将它们重新用作依赖项,并在每次推出新版本时都可以对其进行更新。

这种方法具有一些优点:

  • 如果核心库中有一些更改,则只需在一个地方进行更改,而无需为同一件事重构所有应用程序的代码。
  • 所有应用程序保持同步。无论何时进行更改,所有应用程序仅需要运行 npm update  命令。

库的源码

因此,下一步是发布库

这是最困难的部分,因为我脑海中突然跳出一堆东西,例如:

  1. 如何使用摇树优化
  2. 应该针对哪些 JS 模块系统(CommonJS、AMD、ES modules)
  3. 需要转译源码吗
  4. 需要打包源码吗
  5. 应该发布哪些文件

在发布第三方库(组件库,工具库)时,我们每个人的脑海中都应该冒出这些问题。

来, 我们一步步解决以上的问题。

不同类型的 JS 模块系统

1. CommonJS

  • 由 Node.js 实现
  • 多用在服务器端安装模块时
  • 没有 runtime/async 模块
  • 通过 require  导入模块
  • 通过 module.exports  导出模块
  • 无法使用摇树优化,因为当你导入时会得到一个模块时,得到的是一个对象,所以属性查找在运行时进行,无法静态分析
  • 会得到一个对象的副本,因此模块本身不会实时更改
  • 循环依赖的不能优雅处理
  • 语法简单

2. AMD 异步模块定义

  • 由  RequireJs  实现
  • 当你在客户端(浏览器)环境中,异步加载模块时使用
  • 通过 require  实现导入
  • 语法复杂

3. UMD 通用模块定义

  • CommonJs + AMD  的组合(即 CommonJs 的语法 + AMD 的异步加载)
  • 可以用于 AMD/CommonJs 环境。
  • UMD 还支持全局变量定义,因此,UMD 模块能够在客户端和服务器上工作。

4. ES modules

  • 用于服务器/客户端
  • 支持模块的 Runtime/static loading
  • 当你导入时,获得是实际对象
  • 通过 import  导入,通过 export  导出
  • 静态分析——你可以决定编译时的导入和导出(静态),你只需要看源码,不需要执行它
  • 由于 ES6 支持静态分析,因此摇树优化是可行的
  • 始终获取实际值,以便实时更改模块本身
  • 比 CommonJS 有更好的循环依赖管理

现在,我们了解了不同类型的 JS 模块系统以及它们如何演变。

尽管所有工具和现代浏览器都支持 ES modules,但我们在发布库时不知道用户如何利用我们的库。因此,我们必须确保我们的库在所有环境中都能正常工作。

让我们深入研究并设计一个示例库,更好地回答与发布库有关的所有问题。

我已经建立了一个小型的 UI 库(你可以在 GitHub 上找到源代码),并且我将分享我在编译,打包和发布中的所有经验和探索。


目录结构

在这里,我们有一个小的 UI 库,其中包含 3 个组件:Button,Card 和 NavBar。让我们一步步进行编译并发布。

发布前的最佳实践

1. 摇树优化(Tree Shaking)

webpack 官方文档有说明

  • 摇树优化是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 [import][4]  和 [export][5]。这个术语和概念实际上是兴起于 ES2015 模块打包工具 rollup。新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json  的 "sideEffects"  属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是纯的 ES2015 模块,由此可以安全地删除文件中未使用的部分。
  • webpack 和 Rollup 都支持摇树优化,这意味着我们需要牢记某些事情,以便我们的代码可被 Tree Shaking。

2. 发布所有模块形态

  • 我们应该发布所有模块形态,例如 UMD 和 ES Module,因为我们永远不知道用户在哪个版本的浏览器或 webpack 中使用此库/包。
  • 即使所有打包程序(如  webpack  和  Rollup)都能解析 ES Module ,但如果我们的使用者使用的是 webpack 1.x,则它无法解析 ES 模块。
// package.json{  "name": "js-module-system",  "version": "0.0.1",

  • package.json 文件的 main 字段通常用于指向 UMD 版本的库/包。
  • package.jso 文件的 module 字段用于指向 ES 版本的库/包。

鲜为人知的事实:webpack 使用 resolve.mainfields 确定检查 package.json  中的哪些字段。

性能提示:由于所有现代浏览器现在都支持 ES 模块,因此也请务必发布  ES 版本的库/包。这样一来,可以减少编译次数,最终可以减少向用户交付的代码。这将提高应用程序的性能。

那么,下一步是什么?编译还是打包?我们应该使用什么工具?啊,这是最棘手的部分!让我们深入研究研究。

webpack vs Rollup vs Babel

这些我们在日常工作中使用的工具,用于承载我们的应用程序/库/软件包。没有它们,我无法想象现代的 Web 开发有多么糟糕。因此,我们无法将它们进行比较 ❌

每种工具都有其自身的优势,并根据使用者的需求达到不同的目的。

现在让我们看一下这些工具:

webpack

webpack 是一个很棒的模块打包工具, 它被广泛接受并且主要用于构建 SPA。它提供了开箱即用的所有功能,例如代码拆分、按需加载、摇树优化等,并且它本身使用的是 CommonJS 模块系统。

RollupJS

RollupJS 还是类似于 webpack 的模块打包器。但是,RollupJS 的主要优点是它遵循 ES6 修订版中包含的代码模块的新标准化格式,因此你可以使用它来打包  ES module variant 的 library/package,但它不支持按需加载。

Babel

Babel 是 JavaScript 的编译器,以将 ES6 代码转换为可在你的浏览器(或服务器)中运行的代码而闻名。请记住,它只是编译而不会打包你的代码。

我的建议:对库使用 Rollup.js,对应用程序使用 webpack。

编译(Babel-ify)源代码还是直接打包源代码

在构建我的 NPM 库时,我花费了大量时间来试图找出该问题(如何编译、如何打包)的答案。我开始挖掘自己的 node_modules,查找所有优秀的库并检查它们的构建系统。

对比 libraries/packages 构建的输出

在查看了不同 libraries/packages 的构建输出之后,我清楚地了解了这些库的作者在发布之前可能会想到的不同策略。以下是我的观察。

如你在上图中所看到的,我已根据它们的特性将这些库/软件包分为两组:

  • UI Libraries-UI 库(styled-components, material-ui)
  • Core Packages-核心包(reactreact-dom)

你可能已经弄清楚了这两组之间的区别。

UI Libraries

  • 有一个 dist 文件夹,该文件夹是针对 ES 和 UMD/CJS 模块系统 的打包和压缩版本。
  • 有一个 lib 文件夹,用来存放被编译后的代码。

Core Packages

  • 只有一个文件夹,其中包含针对 CJS 或 UMD 模块系统的打包和压缩版本。

但是,为什么 UI Libraries 和 Core Packages 的构建输出有所不同?

UI Libraries

想象一下,如果我们只是发布库的 bundled version 将其托管在 CDN 上,我们的用户将直接在  标记中使用它。现在,如果使用者只想使用  组件,则他们必须加载整个库。另外,在浏览器中,没有可以解决 tree shaking 的打包工具,最终我们会将整个库代码发送给我们的使用者。因此,我们不能像如下代码引入整个库文件。

<script type="module">import { Button } from "https://unpkg.com/uilibrary/index.js";script>

现在,如果我们只是简单地将 src 转换为 lib 并将该 lib 托管在 CDN 上,那么我们的使用者实际上可以得到他们想要的任何东西而没有任何开销,“代码更少,加载更快” ✅

<script type="module">import { Button } from "https://unpkg.com/uilibrary/lib/button.js";script>

Core Packages

Core Packages(核心包)永远不会通过  标记使用,因为它们必须是主应用程序的一部分。因此,我们可以安全地发布这些软件包的构建版本( UMDES),并将构建后的系统交给用户使用。

例如,他们可以使用 UMD 而不使用摇树优化,或者如果打包器能够识别并获得摇树优化的好处,则可以使用 ES

// CJS requireconst Button = require("uilibrary/button");// ES importimport {Button} from "uilibrary";

对于 UI 库

  • 当我们针对  es  模块系统构建时,需要  Babel  编译源代码,并将编译后的代码放置在  lib  文件夹中。我们甚至可以将 lib 托管在 CDN 上。
  • 当我们针对  cjs/umd  模块系统和  es  模块系统 等多个模块系统构建时,需要 rollup  ? 打包和压缩代码。

下面我们修改 package.json  以指向对应的模块系统。

// package.json{  "name": "js-module-system",  "version": "0.0.1",  // for umd/cjs builds  "main": "dist/index.js",  // for es build  "module": "dist/index.es.js"}

对于 core packages,我们不需要 lib 版本。我们只需要针对 cjs/umd 模块系统和 es 模块系统,使用 rollup 进行 ? 打包和压缩源代码即可。

提示:对于愿意通过  标记下载整个库/软件包的用户,我们也可以在 CDN 上托管 dist 文件夹

我们怎么进行打包

我们应在在 package.json  中为了不同的目的编写不同的脚本。你能在 GitHub 上面找到 Rollup 的一些配置—— rollup config。

// package.json{  "scripts": {    "clean": "rimraf dist",    "build": "run-s clean && run-p build:es build:cjs build:lib:es",    "build:es": "NODE_ENV=es rollup -c",    "build:cjs": "NODE_ENV=cjs rollup -c",    "build:lib:es": "BABEL_ENV=es babel src -d lib"  }}

我们应该发布哪些东西

  • License
  • README
  • Changelog
  • Metadata( "main"  , "module"  , "bin"  )— package.json
  • Control through package.json  "files"  property

package.json  中,  "files"  字段是一个数组类型 ,用来表示软件包被当做第三方依赖安装时,都有哪些文件或文件夹需要下载到业务项目中。如果你在数组中加入了一个文件夹,那么在你 npm install  时,文件夹及下面的文件都会被下载。

在我的示例项目中,我在 "files"  中加入了 libdist 文件夹。

// package.json{  "files": ["dist", "lib"]}

最后,终于可以准备发布了。只需在终端中键入 npm run build  命令,你就看到以下输出。仔细查看 distlib 文件夹都有哪些东西。


可以发布了?

总结

至此,我们已经了解了 JavaScript 模块系统以及如何创建自己的库并发布它。下面是一些注意事项:

1. 是否可以启用摇树优化

2. 至少需要构建 ES modules  and CommonJS  两种模块系统

3. 使用 Babel 和 Bundlers 搭建 libraries

4. 使用 Bundlers 搭建 Core packages

5. 在 package.json  中使用 "module"  字段 来构建 es 模块的版本(PS:这有助于使用 tree shaking)

6. 发布已编译的文件夹以及模块的编译版


原文链接:https://www.freecodecamp.org/news/anatomy-of-js-module-systems-and-building-libraries-fadcd8dbd0e/

作者:Kamlesh Chandnani

译者:古月

校对者:水歌

非营利组织 freeCodeCamp.org 自 2014 年成立以来,以“帮助人们免费学习编程”为使命,创建了大量免费的编程教程,包括交互式课程、视频课程、文章等。我们正在帮助全球数百万人学习编程,希望让世界上每个人都有机会获得免费的优质的编程教育资源,成为开发者或者运用编程去解决问题。

你也想成为

freeCodeCamp 社区的贡献者吗

欢迎点击以下文章了解

✨✨招募丨freeCodeCamp 翻译计划成为 freeCodeCamp 专栏作者,与世界各地的开发者分享技术知识

在 freeCodeCamp 专栏

导出库的版本_了解 JavaScript 模块系统基础知识,搭建自己的库相关推荐

  1. 计算机组成原理基础知识试题及答案,[电脑基础知识]计算机组成原理试题库.doc...

    [电脑基础知识]计算机组成原理试题库.doc 计算机组成原理练习题一. 单项选择题CPU响应中断的时间是. A中断源提出请求: B取指周期结束: C执行周期结束: D间址周期结束. 2下列说法中是正确 ...

  2. 公务员考试中公共基础知识计算机,2012山东省公务员考试公共基础知识最新考试试题库(完整版)...

    2012山东省公务员考试公共基础知识最新考试试题库(完整版) 件将会____. A.永远不再发送 B.需要对方再次发次 C.保存在服务商的主机上 D.退回发信人 18.下列部件中,不属于计算机主机内的 ...

  3. 处理器_深度学习及 KPU 基础知识

    kpu 处理器_深度学习及 KPU 基础知识_weixin_39909212的博客-CSDN博客深度学习及 KPU 基础知识1. 阅读完本章文档可以了解什么?了解深度学习一些基础内容了解 K210 内 ...

  4. 配有p4cpu的微型计算机,2011江苏省基层公共基础知识最新考试试题库(完整版)

    2011江苏省基层公共基础知识最新考试试题库(完整版) 1.冯?诺依曼计算机工作原理是____. A.顺序存储和程序控制 B.存储程序和程序控制 C.集中存储和程序控制 D.运算存储分离 2.对WIN ...

  5. 总工会招聘计算机及答案,2019 年事业单位工会系统招聘考试《工会基础知识》 真题库及答案【2019版】.pdf...

    [各省.市.县事业单位考试真题,有答案解析] [内部考试重点资料,可打印] 2019 年事业单位工会系统招聘考试<工会基础知识> 真题库及答案[2019 版] 一.单选题 1 . 基层工会 ...

  6. python excel 库 知乎_办公自动化利器openpyxl,高效操作excel的Python库

    你是不是会经常简单且重复地操作excel表格?并且这些操作的技术含量低. 本文给你介绍如何使用python高效操作excel,按照本文的教程,你可以快速高效地完成各种excel的骚操作. 你需要做的只 ...

  7. python的科学计算库有哪些_《用Python进行科学计算》——SciPy数值计算库

    SciPy函数库在NumPy库的基础上增加了众多的数学.科学以及工程计算中常用的库函数.例如线性代数.常微分方程数值求解.信号处理.图像处理.稀疏矩阵等. 最小二乘拟合 假设有一组实验数据(x[i], ...

  8. python的excell库_扣丁学堂Python基础教程之Excel处理库openpyxl详解

    扣丁学堂Python基础教程之Excel处理库openpyxl详解 2018-05-04 09:49:49 3197浏览 openpyxl是一个第三方库,可以处理xlsx格式的Excel文件.pipi ...

  9. 服务器架设笔记——Apache模块开发基础知识

    通过上节的例子,我们发现Apache插件开发的一个门槛便是学习它自成体系的一套API.虽然Apache的官网上有对这些API的详细介绍,但是空拿着一些零散的说明书,是很难快速建立起一套可以运行的系统. ...

最新文章

  1. Sg.js框架核心概念
  2. 从在线教育交易平台看系统阶段性演进
  3. 商务之路有多远,贿赂就有多远吗? 续一
  4. java if else嵌套,减少的 if else 嵌套 可以使用java 8的Consumer
  5. php引用数据库实例,PHP单例模式实例,连接数据库对类的引用
  6. 欧拉折线法解常微分方程C语言,05常微分方程数值解.ppt
  7. 10.4.4 使用ctypes调用kernel32.dll中的函数
  8. Win7下硬盘安装Redhat双系统
  9. 易华录发布蓝光存储新品
  10. 不想加班你敢说出来吗?华为员工公开说我不想加班
  11. keil5中如何实时查看变量值
  12. Win7-64bit下MapX的安装和使用
  13. HSC-1th misc——DORAEMON
  14. 面试官没想到我对redis数据结构这么了解,直接给offer
  15. 在iOS 14中使用带有SF Symbols 2的彩色图标
  16. oracle 密码解锁
  17. 简单的flask入门,自己来写网页
  18. 华硕和兰博基尼将发布iPhone的对手
  19. 求一个方阵的主对角线及次对角线的和(C语言)(二维数组)
  20. 一键设置电脑锁屏后程序仍然运行

热门文章

  1. python如何读取数据时出现错误_在python3中,关于redis读取数据带有‘b’的问题...
  2. activemq java 重发_java – 无法让ActiveMQ重新发送我的消息
  3. mysql innodb 报错_mysql报错1286 Unknown storage engine 'InnoDB'
  4. mysql密码攻击_MYSQL用户root密码为弱口令又一攻击方法
  5. 计算机用户里dell占c盘太多,我的本是戴尔,用的是Win732位旗舰版系统,C盘里面的用户总是爆满是为什么,求解...
  6. 转--ASP.NET页面之间传值方式
  7. linux 定时任务,压缩 日志,并删除掉 指定日期之前的 日志
  8. npm在项目目录安装插件需要使用sudo
  9. Day Tips:关于搜索小问题
  10. Servlet实现文件下载