-
Notifications
You must be signed in to change notification settings - Fork 0
motivation.cn
Today's websites are evolving into web apps: 当今的网站正演变成 web app:
-
More and more JavaScript is being used.
-
Modern browsers are offering a wider range of interfaces.
-
Fewer full page reloads → even more code in a page.
-
越来越多的JavaScript代码被使用
-
在现代浏览器中提供了广泛的接口
-
更少的整页刷新 → 意味着更多的页面代码
As a result there is a lot of code on the client side!
结果就是,客户端有一大堆代码。
A big code base needs to be organized. Module systems offer the option to split your code base into modules.
因此,一个大型的代码库需要良好规划。模块系统是切分代码库成模块的一种选择。
There are multiple standards for how to define dependencies and export values:
有多个标准制定了如何去定义依赖项和输出值
-
<script>-tag style (without a module system) -
CommonJs
-
AMD and some dialects of it
-
ES6 modules
-
and more...
-
<script>标签风格(没有模块系统) -
CommonJs
-
AMD 及其变种
-
ES6 模块
-
还有更多...
This is how you would handle a modularized code base if you didn't use a module system.
如果不使用一个模块系统,一般你会这样处理一个模块化的代码库。
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>Modules export an interface to the global object, i. e. the window object. Modules can access the interface of dependencies over the global object.
模块导出接口到全局对象中,如 window 对象,也通过全局对象来访问依赖模块的接口。
- Conflicts in the global object.
- Order of loading is important.
- Developers have to resolve dependencies of modules/libraries.
- In big projects the list can get really long and difficult to manage.
- 全局变量冲突
- 加载的顺序是很重要的
- 开发人员必须解决模块/库的依赖关系
- 大项目中依赖列表越来越长而难以维护
This style uses a synchronous require method to load a dependency and return an exported interface. A module can specify exports by adding properties to the exports object or setting the value of module.exports.
这种方式使用同步的 require 来加载一个依赖并返回一个输出接口。模块通过给 exports 添加属性或给 module.exports 赋值来指定输出。
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;It's used on server-side by node.js.
这种方式用于 [node.js][http://nodejs.org]。
- Server-side modules can be reused.
- There are already many modules written in this style (npm).
- Very simple and easy to use.
- 重用服务端模块
- npm 上已经有很多这种风格书写的模块
- 简单且易于使用
- Blocking calls do not apply well on networks. Network requests are asynchronous.
- No parallel require of multiple modules
- 阻塞式请求不适用于网络环境,网络请求是异步的
- 不能并行请求多个模块
-
node.js - server-side
-
modules-webmake - compile to one bundle
-
wreq - client-side
-
node.js - 服务端
-
modules-webmake - 打包成一个文件
-
wreq - 客户端
Asynchronous Module Definition
Other module systems (for the browser) had problems with the synchronous require (CommonJS) and introduced an asynchronous version (and a way to define modules and exporting values):
其他(基于浏览器的)模块系统不能使用同步的require(CommonJs),而引入了一个异步版本(以及一种定义模块和输出值的方案)
require(["module", "../file"], function(module, file) { /* ... */ });
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});- Fits the asynchronous request style in networks.
- Parallel loading of multiple modules.
- 适合异步的网络请求
- 并行加载多个模块
- Coding overhead. More difficult to read and write.
- Seems to be some kind of workaround.
- 开发成本更高,更难阅读和编写。
- 似乎是某种形式的变通方案
-
require.js - client-side
-
curl - client-side
-
require.js - 客户端
-
curl - 客户端
Read more about CommonJS and AMD.
EcmaScript6 adds some language constructs to JavaScript, which form another module system.
EcmaScript6 给 JavaScript 增添了一些新的语言结构,形成了另一个模块系统。
import "jquery";
export function doStuff() {}
module "localModule" {}- Static analysis is easy
- Future-proof as ES standard
- 易于静态分析
- 未来的标准
-
Native browser support will take time
-
Very few modules in this style
-
浏览器原生支持还需要时间
-
这种风格的模块还很少
Let the developer choose their module style. Allow existing codebases and packages to work. Make it easy to add custom module styles.
开发者可自主选择模块风格,兼容已有代码,易于增加自定义模块风格。
Modules should be executed on the client, so they must be transferred from the server to the browser.
因为模块是在客户端执行的,所以首先需要把模块从服务端传输到浏览器。
There are two extremes on how to transfer modules:
关于传输模块,有两个极端的方案:
- 1 request per module
- all modules in one request
- 每个模块一个请求
- 所有模块一个请求
Both are used in the wild, but both are suboptimal:
这两种方案都被广泛使用,但都不是最优的。
-
1 request per module
- Pro: only required modules are transferred
- Con: many requests means much overhead
- Con: slow application startup, because of request latency
-
all modules in one request
- Pro: less request overhead, less latency
- Con: not (yet) required modules are transferred too
-
每个模块一个请求
- 优点: 仅仅调用的模块才会被请求传输(按需加载)
- 缺点: 许多请求意味着很多开销
- 缺点: 因为有网络延迟,应用启动缓慢
-
所有模块一个请求
- 优点: 请求的开销更少,更少的延迟
- 缺点: 还不需要的模块也被请求了
A more flexible transferring would be better. A compromise between the extremes is better in most cases.
一个更具弹性的传输方案会更好。多数情况下,极端方案间的相互折衷会更好
→ While compiling all modules: Split the set of modules into multiple smaller batches (chunks).
→ 编译模块时,将模块集拆分成多个更小的批次(分块)
This allows for multiple smaller, faster requests. The chunks with modules that are not required initially can be loaded on demand. This speeds up the initial load but still lets you grab more code when it will actually be used.
这样允许多个更小更快的请求。一开始不需要的 chunk 可以按需加载。这样加速了初始加载,但又可以在需要的时候拉取更多代码。
The "split points" are up to the developer.
「拆分点」由开发者自定义。
→ A big code base is possible!
→ 可以组合成一个大的代码库!
Note: The idea is from Google's GWT.
注:此思路来自idea is from Google's GWT.
Read more about Code Splitting. 更多可参考Code Splitting.
Why should a module system only help the developer with JavaScript? There are many other resources that need to be handled:
为什么仅给 JavaScript 开发者提供模块系统?实际上还有很多别的资源需要处理,如:
- stylesheets
- images
- webfonts
- html for templating
- etc.
- 样式
- 图片
- web 字体
- html 模板
- 等等
Or translated/processed:
- coffeescript → javascript
- elm → javascript
- less stylesheets → css stylesheets
- jade templates → javascript which generates html
- i18n files → something
- etc.
或者是转译或处理:
- coffeescript → javascript
- elm → javascript
- less stylesheets → css stylesheets
- jade templates → 生成模板的 javascript
- i18n files → 别的资源
- etc.
This should be as easy as:
应该像这样一样简单才对:
require("./style.css");require("./style.less");
require("./template.jade");
require("./image.png");Read more about Using loaders and Loaders.
更多可参考 Using loaders and Loaders.
When compiling all these modules, a static analysis tries to find its dependencies.
Traditionally this could only find simple stuff without expression, but require("./template/" + templateName + ".jade") is a common construct.
Many libraries are written in different styles. Some of them are very weird...
在编译所有模块时,会尝试用静态分析来获取依赖关系。
一般来说,这只能找到没有表达式的简单形式,但像 require("./template/" + templateName + ".jade") 确实是一种常见的用法。
不同的代码库往往使用不同的风格,其中不少的风格还是比较怪异的……
A clever parser would allow most existing code to run. If the developer does something weird, it would try to find the most compatible solution.
一个智能的解析器应当支持运行绝大多数的现存代码。如果开发者用了一些不寻常的方法,那么解析器应尝试找寻一个最具兼容性的解决方案。
此篇翻译参照了liunian/webpack-doc 和 Wooleners/webpack-doc-cn 对应的文档