WebPack系列教程(二):动机

来源:互联网 发布:炒股必备软件 编辑:程序博客网 时间:2024/05/16 12:59

当今的网站已经发展为web app:

  • 越来越多的js脚本放入页面;
  • 在现代浏览器中,我们能做越来越的事情;
  • 如今很少有网站会采用整页面重载,因而页面的代码越来越多;

最终导致大量的代码放入客户端中了。

庞大的前端代码库需要重新组织。模块系统提供了这样的能力来帮助你拆分代码库到各个模块中。

目录
  • Module system styles
    • <script>标签(无模块化)
    • CommonJs: (同步加载)
    • AMD: (异步加载)
    • ES6 模块系统
    • 公正的解决方案
  • 传输
    • 分块传输
  • 为什么仅仅是Javascript
  • 静态分析
    • 策略

Module system styles

如今有很多标准来定义依赖(dependencies)和导出(export)的值:

  • <script>标签(无模块化)
  • CommonJs: (同步加载)
  • AMD: (异步加载)
  • ES6 模块系统
  • 更多…

<script>标签(无模块化)

下面是未使用模块系统来加载使用已经模块化了的代码库

<script src="module1.js"></script><script src="module2.js"></script><script src="libraryA.js"></script><script src="module3.js"></script>

这种方式会把加载的模块对象添加到全局对象上,如window对象。模块能通过window对象访问到其所依赖的各模块对象。

存在的问题
  • 全局对象命名冲突;
  • 加载顺序非常重要;
  • 开发人员不得不解决模块/库之间的依赖;
  • 在大型项目中,如此多的引入脚本是相当费时和难以维护的;

这种方式使用同步require方法加载依赖并返回对外的接口。一个模块能通过给exports对象添加属性或者给module.exports设置值来指定开放哪些接口。

require("module");require("../file.js");exports.doStuff = function() {};module.exports = someValue;

CommonJs多用于服务端NodeJs

支持者
  • 服务端模块能复用;
  • npm中已经有很多模块使用该方式;
  • 非常简单和容易使用;
反对者
  • 阻塞式的加载不适用于网络环境,网络请求通常都是异步的;
  • 无法并行请求多个模块;
基于该方式实现的库
  • node.js - server-side
  • browserify
  • modules-webmake - compile to one bundle
  • wreq - client-side

AMD: (异步加载)

异步模块定义Asynchronous Module Definition
对于浏览器端的模块系统使用同步加载的方式会存在问题,因而引入了异步加载的方式。

require(["module", "../file"], function(module, file) { /* ... */ });define("mymodule", ["dep1", "dep2"], function(d1, d2) {  return someExportedValue;});
支持者
  • 适合网络环境的异步请求方式;
  • 多模块的并行加载;
反对者
  • 编码开销:更难以读和编写;
  • 看起来像某种变通方案;
基于该方式实现的库
  • require.js - client-side
  • curl - client-side

阅读更多关于CommonJs and AMD.

ES6模块系统

EcmaScript6添加了一些语言结构到javascript当中,使之形成了另一套模块系统。

import "jquery";export function doStuff() {}module "localModule" {}
支持者
  • 静态分析很容易;
  • 成为ES标准:不会过时;
反对者
  • 本地浏览器支持尚需时日;
  • 使用这种方式的模块太少;

公正的解决方案

允许开发者自己选择使用哪种模块系统;兼容现有的代码;添加自定义模块更加容易;

传输

模块代码需要在客户端执行,因此它们必须要从服务端传输到浏览器中。

有两种传输模块的极端方式:

  • 一个请求一个模块;
  • 一个请求所有模块;

两种方式都使用很广,但都不是最佳方案:

  • 一个请求一个模块;

    • 支持者:只传输必须加载的模块;
    • 反对者:很多的请求意味着更多的开销;
    • 反对者:因为延迟加载,导致应用程序启动变慢;
  • 一个请求所有模块;

    • 支持者:更少的请求开销,更少的延迟;
    • 反对者:会加载不需要(多余)的模块;

分块传输

一种更灵活的传输方式可能会更好。在很多案例中对于上述两种极端情形进行折中会更合适。

当编译所有模块时:可以将多个模块组成模块集分别放入多个更小的打包分块的文件中。

我们会得到大量更小的请求。大模块集不会在初始化时就加载,只有当需要时才会去加载它们。初次加载不会请求项目所有的代码库。

代码分割的“拆分点 ”取决于开发者,并且是可选的。

编写大代码库成为可能。

注:该思想来源于Google’s GWT

阅读更多关于代码拆分

为什么仅仅是Javascript

为什么一个模块系统只能帮助开发者处理Javascript。要知道前端代码中存在很多需要被处理的其他静态资源:

  • 样式表
  • 图片
  • 网络字体
  • html模板
  • 等等…

同时还有:

  • coffeescript → javascript
  • elm → javascript
  • less/sass stylesheets→ css stylesheets
  • jade templates → javascript which generates html
  • i18n files → something
  • 等等…

这些都应该同样可以简单使用:

require("./style.css");
require("./style.less");require("./template.jade");require("./image.png");

阅读更多关于Using Loaders和Loaders

静态分析

当编译所有模块时,静态分析试图查找所有的依赖。

通常静态分析只能通过简单没有表达式的字符串进行查找,但是像require("./template/" + templateName + ".jade")是一种很常见的结构。

很多库使用不同的风格,有些事非常怪异的…

策略

一个聪明的解析器需要能够保证大量已经存在的代码可以运行。如果开发人员使用了一些怪异的方式编写,解析器要能找到最兼容的解决方案。

0 0
原创粉丝点击