webpack之【究竟解决了什么问题】

webpack现代化前端应用的基石
  1. 现阶段的大型应用就要求前端必须要有独立项目,独立的项目需要进行工程化获得足够的效率
  2. 具有复杂数据状态的应用开发过程就必须要有合适的框架,采用数据驱动的方式增加可维护性
  3. 复杂项目结构必须进行模块化管理,提高公共部分的可复用性,增加团队的并行协作能力
  4. 重复规律性的工作必须采用自动化工具,提高工作效率,避免人为错误
webpack与模块化开发

前端发展到前后端分离之后:前端项目作为一个独立的个体,已经具备了很高的复杂性,相对管理就变得很难,而在解决这一问题上,出现了前端模块化。将不同功能的代码划分为不同的模块单独维护。

webpack的本质是一个模块打包工具(万物皆可模块:万物皆可打包)

webpack解决了如何在前端项目中更加高效的管理和维护项目中的每一个资源
模块化的演进过程
1.Stage 1 - 文件划分方式

最早期实现模块化,是将每个功能及其状态的数据各自单独放到不同的js文件中,约定每个文件都是一个独立的模块。使用某个模块就将某个模块引入到的页面中,一个script的标签对应一个模块,然后直接调用模块中的成员

└─ stage—1
├── module—a.js
├── module—b.js
└── index.html

// module-a.js 
function foo () {
   console.log('moduleA#foo') 
}


// module-b.js 
var data = 'something'


<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Stage 1</title>
</head>
<body>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    // 直接使用全局成员
    foo() // 可能存在命名冲突
    console.log(data)
    data = 'other' // 数据可能会被修改
  </script>
</body>
</html>

缺点:

  1. 模块直接在全局工作,大量模块成员污染全局作用域
  2. 没有私有空间,所有模块内成员都可以在模块外被访问和修改
  3. 一旦模块过多,容易产生命名冲突
  4. 无法管理模块之间的依赖关系
  5. 维护过程中,难以区分模块成员的的所属关系
2.Stage 2 - 命名空间方式

后来,我们约定每个模块只暴露一个全局对象,所有模块成员都挂载到这个全局对象中,具体做法是在第一阶段的基础上,通过将每个模块“包裹”为一个全局对象的形式实现,这种方式就好像是为模块内的成员添加了“命名空间”,所以我们又称之为命名空间方式。

// module-a.js
window.moduleA = {
  method1: function () {
    console.log('moduleA#method1')
  }
}

// module-b.js
window.moduleB = {
  data: 'something'
  method1: function () {
    console.log('moduleB#method1')
  }
}


<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Stage 2</title>
</head>
<body>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
    // 模块成员依然可以被修改
    moduleA.data = 'foo'
  </script>
</body>
</html>

这种方式解决了全局作用域下的命名冲突,和模块归属问题,但是并没有解决模块之间的依赖问题以及外部访问和修改的问题

3.Stage 3 - IIFE(立即执行函数表达式)

使用立即执行函数表达式(IIFE,Immediately-Invoked Function Expression)为模块提供私有空间。具体做法是将每个模块成员都放在一个立即执行函数所形成的私有作用域中,对于需要暴露给外部的成员,通过挂到全局对象上的方式实现。

// module-a.js
;(function () {
  var name = 'module-a'
  function method1 () {
    console.log(name + '#method1')
  }
  window.moduleA = {
    method1: method1
  }
})()

// module-b.js
;(function () {
  var name = 'module-b'
  function method1 () {
    console.log(name + '#method1')
  }
  window.moduleB = {
    method1: method1
  }
})()

这种方式带来了私有成员的概念,私有成员只能在模块成员内通过闭包的形式访问,这就解决了前面所提到的全局作用域污染和命名冲突的问题。

4. Stage 4 - IIFE 依赖参数

在 IIFE 的基础之上,我们还可以利用 IIFE 参数作为依赖声明使用,这使得每一个模块之间的依赖关系变得更加明显。

// module-a.js
;(function ($) { // 通过参数明显表明这个模块的依赖
  var name = 'module-a'
  function method1 () {
    console.log(name + '#method1')
    $('body').animate({ margin: '200px' })
  }
  window.moduleA = {
    method1: method1
  }
})(jQuery)
模块加载的问题

以上四种方式确实解决了一些问题,但是仍然有一些问题并未解决,并且被忽略了

<!DOCTYPE html>
<html>
<head>
  <title>Evolution</title>
</head>
<body>
  <script src="https://unpkg.com/jquery"></script>
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
  </script>
</body>
</html>

最明显的问题就是:模块的加载

我们都是通过 script 标签的方式直接在页面中引入的这些模块,这意味着模块的加载并不受代码的控制,时间久了维护起来会十分麻烦。试想一下,如果你的代码需要用到某个模块,如果 HTML 中忘记引入这个模块,又或是代码中移除了某个模块的使用,而 HTML 还忘记删除该模块的引用,都会引起很多问题和不必要的麻烦。

更为理想的方式应该是在页面中引入一个 JS 入口文件,其余用到的模块可以通过代码控制,按需加载进来。

模块化规范的出现
  1. 一个统一的模块化标准规范
  2. 一个可以自动加载模块的基础库

1.Node.js中的CommonJS规范:

一个文件就是一个模块,每个模块都有单独的作用域,通过module.exports导出成员,再通过require函数载入模块。注意:CommonJS仅支持在node环境中。

CommonJS约定是以同步的方式加载模块,因为Node.js执行机制是在启动时加载模块,执行过程中只是使用模块,所以这种方式没有问题。但是在浏览器端使用同步加载模式,会引起大量的同步模式请求,导致应用效率地下。

2.浏览器端的AMD(异步模块定义规范):

规范出现的同时,推出了一个非常出名的库,叫做Require.js,它实现了AMD模块化规范,本身也是一个非常强大的模块加载器

目前绝大多数第三方库都支持 AMD 规范,但是它使用起来相对复杂,而且当项目中模块划分过于细致时,就会出现同一个页面对 js 文件的请求次数过多的情况,从而导致效率降低。AMD 规范为前端模块化提供了一个标准,但这只是一种妥协的实现方式

模块化的标准规范
  1. 在Node.js环境中,我们遵循Common.js规范来组织模块
  2. 在浏览器环境中,我们遵循ESModules规范
图1 ESModules

1.模块打包工具的出现

模块化思想的进一步引入和发展,前端应用有产生了一些新的问题,比如:

  1. 使用ES Modules模块标准,存在兼容性性问题。尽管主流浏览器的最新版本都支持这一特性,但是我们无法保证用户所使用的浏览器情况。
  2. 模块化分出的模块文件过多,前端应用又运行在浏览器环境中,每一个文件都需要单独从服务器请求回来。零散的模块文件必然会导致浏览器频繁发送网络请求,影响应用的实际体验
  3. 随着前端应用的增大,不仅仅是js需要模块化,html和css也面临相同的问题

所以,我们理想的打包工具需要实现一些功能:

  • 第一,它需要具备编译代码的能力,也就是将我们开发阶段编写的那些包含新特性的代码转换为能够兼容大多数环境的代码,解决我们所面临的环境兼容问题。
  • 第二,能够将散落的模块再打包到一起,这样就解决了浏览器频繁请求模块文件的问题。这里需要注意,只是在开发阶段才需要模块化的文件划分,因为它能够帮我们更好地组织代码,到了实际运行阶段,这种划分就没有必要了。
  • 第三,它需要支持不同种类的前端模块类型,也就是说可以将开发过程中涉及的样式、图片、字体等所有资源文件都作为模块使用,这样我们就拥有了一个统一的模块化方案,所有资源文件的加载都可以通过代码控制,与业务代码统一维护,更为合理。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容