webpack之【如何实现模块化打包】

模块化打包诉求:
  1. 能够将散落的模块打包到一起;
  2. 能够编译代码中的新特性;
  3. 能够支持不同种类的前端资源模块;
  4. 目前能够很好满足上诉需求的主流工具:Webpack、Parcel、Rollup
webpack
  1. webpack作为一个模块打包工具,本身就可以解决模块代码打包问题,将零散的js代码打包到一个js文件中。
  2. 对于环境兼容问题的代码,Webpack可以在打包过程中通过Loader机制对其实现编译转换,然后再进行打包。
  3. 对于不同类型的前端模块类型,Webpack支持在js代码中以模块形式载入任一类型的资源文件。例如:我们可以通过Webpack实现在js中加载css文件,被加载的css文件将会通过style标签的方式工作。
  4. Webpack还具有代码的拆分能力,避免打包后的单个文件过大。我们可以把首次加载所必须的模块打包到一起,其他模块再单独打包,等到应用过程中实际需要用到某个模块,再异步加载该模块,实现增量加载,或者叫做渐进式加载,非常适合现代化的大型web应用
Webpack 快速上手

案例:

└─ 02-configuation
│ ├── src
│ │ ├── heading.js
│ │ └── index.js
│ └── index.html

// ./src/heading.js
export default () => {
  const element = document.createElement('h2')
  element.textContent = 'Hello webpack'
  element.addEventListener('click', () => alert('Hello webpack'))
  return element
}


// ./src/index.js
import createHeading from './heading.js'
const heading = createHeading()
document.body.append(heading)


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Webpack - 快速上手</title>
</head>
<body>
  <script type="module" src="src/index.js"></script>
</body>
</html>

注意:type=“module” 这种用法是ESModules中提出的标准,用来区分普通js脚本和模块

示例中对于支持ES Modules的浏览器可以正常工作,但是对于不支持的浏览器,会出现错误,所以我们需要使用Webpack这样的工具。

  1. Webpack是一个npm工具模块所以我们先初始化一个package.json文件,用来管理npm依赖版本,完成之后,再来安装Webpack的核心模块以及它的CLI模块,具体操作如下:

$ npm init --yes

$ npm i webpack webpack-cli --save-dev

注:webpack是一个Webpack的核心模块,webpack-cli是Webpack的CLI程序,用来在命令行中调用Webpack。

  1. webpack-cli所提供的CLI程序就会出现在node_modules/.bin目录当中,我们可以通过npx快速找到CLI并运行它,具体操作如下:

$ npx webpack --version
v4.42.1

注:npx 是 npm 5.2 以后新增的一个命令,可以用来更方便的执行远程模块或者项目 node_modules 中的 CLI 程序。

  1. 这里我们使用的Webpack版本是v4.42.1,有了Webpack后,就可以直接运行webpack命令来打包js模块代码,具体操作如下:

$ npx webpack

这个命令在执行的过程中,Webpack会自动从src/index.js文件开始打包,然后根据代码中的模块导入操作,自动将所有用到的模块代码打包到一起。


图3-1

这里我们回到index.html中修改引入文件的路径,由于打包后的代码就不会再有import和export了,所以我们可以删除type=“module”。代码可以正常工作。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Webpack - 快速上手</title>
</head>
<body>
  <script src="dist/main.js"></script>
</body>
</html>

我们也可以将Webpack命令定义到npm scripts中,这样每次使用起来会更加方便,具体如下:

{
  "name": "01-getting-started",
  "version": "0.1.0",
  "main": "n/a",
  "author": "zhangkai",
  "license": "MIT",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.42.1",
    "webpack-cli": "^3.3.11"
  }
}
配置Webpack的打包过程

Webpack 4 以后的版本支持零配置的方式直接启动打包,整个打包过程会按照约定将src/index.js作为打包入口,最终打包的结果会存放到dist/main.js中。

很多时候我们需要自定义路径,例如,在下面这个案例中,我们需要它的打包入口是src/main.js,我们可以通过在项目根目录下添加一个webpack.config.js。具体结构如下:

webpack.config.js

└─ 02-configuation
 ├── src
 ├ ├── heading.js
 ├ └── main.js
 ├── index.html
 ├── package.json
 └── webpack.config.js ···················· Webpack 配置文件

webpack.config.js是一个运行在Node.js环境中的js文件,也就是说我们需要按照CommonJS的方式编写代码,这个文件可以导出一个对象,我们可以通过所导出对象的属性完成相应的配置选项。

// ./webpack.config.js
module.exports = {
  entry: './src/main.js'
}
// ./webpack.config.js
const path = require('path')
module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'output')
  }
}

注:webpack.config.js是运行在Node.js环境中的代码,所以直接可以使用path之类的Node.js内置模块。

导入 Webpack 配置对象的类型

其目的是为了标注config对象的类型,从而实现智能提示。而在配置完成以后要注释掉这段辅助代码,node环境不知道import语句
由此便衍生了一下写法:

// ./webpack.config.js
/** @type {import('webpack').Configuration} */  //类型注释中使用 import 动态导入类型
const config = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js'
  }
}
module.exports = config

这种写法实现动态载入配置对象类型,这种导入类型不是ES Modules中的所规范的,而是typeScript中提供的特性。虽然我们是以js文件举例啊,并没有涉及typescript,但是在vscode中的类型系统是基于typescript的。

Webpack工作模式
  1. production模式:启动内置优化插件,自动优化打包结果,打包速度偏慢
  2. development模式:自动优化打包速度,添加一些调试过程中的辅助插件
  3. none模式:运行最原始的打包,不做任何处理

如果没有配置明确的值,webpack会给予对应的配置警告,并且默认使用production模式工作

修改Webpack工作模式的方式有两种:

  1. 通过CLI --mode 参数传入;
  2. 通过配置文件设置mode属性;

打包结果运行原理
注:将Webpack工作模式设置为none,打包后的结果更加利于理解和阅读
如下:打包后的js文件


图3-2

把代码全部折叠起来:
TIPS:

  • VSCode 中折叠代码的快捷键是 Ctrl + K,Ctrl + 0 (macOS:Command + K,Command + 0)

整体生成的代码其实就是一个立即执行函数,这个函数是Webpack工作入口(webpackBootsstrap),它接收一个modules参数,调用时传入了一个数组


图3-3

展开下方的数组,图3-4(事实上我本地打包展开是一个对象图3-5,应该是版本不同,作用都是一样的),里面所有的元素均是参数列表相同的函数。这里的函数都对应我们源代码中的模块,在图3-5的版本中,更为明显,通过键值方式映射。每个模块最终都被包括在了这样的函数中,从而实现模块的私有作用域


图3-4

图3-5

接下来展开Webpack工作入口函数,图3-6

这个函数最开始定义了一个installedModules对象用于存放或者缓存加载过的模块。紧接着定义了一个require模块,顾名思义,这个函数是加载模块的,再往后就是require函数上挂在了一些其他的数据和工具函数。
这个函数执行到最后调用require函数,传入模块id为0,实际上id就是模块的数组元素下标,对应的是图3-4的数组版本情况,而图3-7中的传入的则是文件路径,无论哪种情况传入的都是入口文件。


图3-6

图3-7
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容