【Webpack】Webpack核心原理

这篇主要就讲一下【打包】(bundle是打包,bundler是打包器)

现有问题

a.js.png

b.js.png

index.js.png

上面三个文件的代码都不能直接运行在浏览器中,因为浏览器不支持直接运行带有import和export关键字的代码,所以就引出了下面的问题

问题一:很多浏览器不认识import和export,只有现代浏览器(chrome、firefox、edge等),通过<script type="module" >来支持import和export;

问题二:虽然通过<script type="module" >可以支持import和export,但是不兼容IE8~15,而且可能会导致文件请求过多

平稳的兼容策略:把关键字转译成普通代码;且把所有文件打包成一个文件;
下面就来讲一下如何来实现上面这个策略

编译import和export关键字

项目中新增了bundler_1.ts,可以拿它和deps_4.ts做一下比较,看哪里做了改动;
主要添加了下面几行代码,通过babel把code转译一下:

 const { code: es5Code } = babel.transform(code, {
    presets: ['@babel/preset-env']
  })

运行一下,取其中的a.js的结果看一下:


image.png
image.png

可以看出区别:
①import关键字没有了,变成了require;
②export关键字也没有了,变成了exports['default']
注意:这时这里的code是字符串

把所有代码打包成一个文件

那么这个文件应该是什么样的呢,首先它应该包含所有的模块,其次它还要可以执行所有的模块;
这时就有三个问题我们要来解决一下:
1、depRelation目前是对象,我们要把它变成数组(为什么变成数组呢,因为数组的第一项就是入口呀,而对象没有第一项这么一个概念);
2、code目前是字符串,我们要把它变成函数;
3、完善execude函数(execude函数就是用来执行入口文件的)

把depRelation从对象变成数组

引入bundler_2.ts,把它与bundler_1.ts做一下对比;node -r ts-node/register ./bundler_2.ts运行bundler_2.ts;

bundler_2.png

bundler_1.png

把code从字符串变成函数

步骤
1、把code字符串外面包一个function(require, modules, exports){....};
2、再把这个code写到文件里,注意不要让引号出现在文件中;

完善execude函数

function execute(key) {
  // 如果已经 require 过,就直接返回上次的结果
  if (modules[key]) { return modules[key] }
  // 找到要执行的项目
  var item = depRelation.find(i => i.key === key)
  // 找不到就报错,中断执行
  if (!item) { throw new Error(`${item} is not found`) }
  // 把相对路径变成项目路径
  var pathToKey = (path) => {
    var dirname = key.substring(0, key.lastIndexOf('/') + 1)
    var projectPath = (dirname + path).replace(/\.\//g, '').replace(/\/\//, '/')
    return projectPath
  }
  // 创建 require 函数
  var require = (path) => {
    return execute(pathToKey(path))
  }
  // 初始化当前模块
  modules[key] = { __esModule: true }
  // 初始化 module 方便 code 往 module.exports 上添加属性
  var module = { exports: modules[key] }
  // 调用 code 函数,往 module.exports 上添加导出属性
  // 第二个参数 module 大部分时候是无用的,主要用于兼容旧代码
  item.code(require, module, module.exports)
  // 返回当前模块
  return modules[key]
}

手动构造dist.js文件

最后dist.js文件的主体结构应该如下

var depRelation = [
    {key: 'index.js', deps: ['a.js', 'b.js'], code: function...},
    {key: 'a.js', deps: ['b.js'], code: function...},
    {key: 'b.js', deps: ['a.js'], code: function...},
]
var modules = {}
execute(depRelation[0].key)
function execute(key){
  var require = ...
  var module = ...
  item.code(require, module, module.exports )
....

}

运行dist.js,可以得到和index.js一样的结果


dist.png

如何得到最终文件dist.js

我们已经知道了dist.js内容是什么了,但是该怎么去得到它呢,也就是说怎么自动生成dist.js文件呢?
答:拼凑出字符串,再把这些字符串写入到文件中即可,如下:

var code = ''
code += content
writeFileSync('dist.js', code)

编写bundler_3.ts,把它与bundler_2.ts做一下对比看哪里做了改动;bundler_3.ts就是打包器,可以自动生成最终文件,运行bundler_3.ts,得到dist_2.js(只是把名字变了下),node ./dist_2.js,得到结果与index.js一样;
并把我们通过打包器自动生成的dist_2.js与我们手动写出来的dist.js作对比,发现内容是一样的。
所以bundler_3.ts里的内容就是一个简易打包器,也就是webpack的核心内容!!

具体步骤

1、需要得到一个depRelation来收集依赖,它是一个数组,具体内容如下:

depRelation = [
    {key: 'index.js', deps: ['a.js', 'b.js'], code: `index.js代码内容的字符串`}, 
    {key: 'a.js', deps: ['b.js'], code: `a.js代码内容的字符串`},
    {key: 'b.js', deps: ['a.js'], code: `b.js代码内容的字符串`},
]

2、得到execude函数,函数具体内容可见上面,它接受一个参数,参数为depRelation[0].key,相当于depRelation[0].key(其实就是index.js)就是一个入口文件;

3、编写generateCode函数,它是打包器的核心,主要通过字符串拼接把我们手写出的dist.js里的内容,通过writeFileSync('dist2.js', generateCode())输出到指定文件里面去;这个过程中需要把depRelation中每一项中的code由字符串变成函数;

4、最后生成的dist2.js就是打包出来的文件啦;

不过目前这个简易打包器还有不少问题:
1、生成的代码中有不少重复的函数;
2、目前只能引入和运行JS文件;
3、只能理解import,无法理解require;
4、不支持插件;
5、不支持配置入口文件和dist的文件名;
但本篇文章主旨在于理解打包器是怎么打包的,所以这些问题后面一一再来梳理;

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

推荐阅读更多精彩内容