webpack原理以及實(shí)現(xiàn) 一看就懂

webpack是什么?

如果一點(diǎn)不知道webpack是什么的可能不太適合此篇文章爽锥,建議從百度開(kāi)始學(xué)習(xí)皱埠。
一句話總結(jié):webpack就是通過(guò)一個(gè)index.js這樣的入口文件,根據(jù)此入口文件查找各層的js模塊的依賴最終打包成一個(gè)文件爬早,webpack做了依賴收集

主要原理

  1. 匹配js文件中加載模塊的字段 得到模塊目錄和文件哼丈,讀取模塊代碼并保存
  2. 如果模塊中還引用了其他模塊,繼續(xù)讀取此模塊引入的模塊
  3. 將各個(gè)模塊的加載函數(shù)比如require換成__webpack_require字段筛严, __webpack_require這個(gè)方法用作從webpack已保存的模塊中加載模塊
  4. 所有模塊和他們的依賴加載完畢后醉旦,得到一個(gè)有所有模塊的對(duì)象
  5. 然后將所有模塊通過(guò)固定模板 打包到一個(gè)js文件中

比如對(duì)入口文件index.js進(jìn)行打包,index.js中引入了a.js和b.js模塊桨啃,并且b.js模塊引入了c.js模塊车胡,webpack都能將各個(gè)模塊依賴關(guān)系以及各個(gè)模塊的功能全部打包到一個(gè)文件中。
webpack使用者只管配置一下入口文件index.js照瘾,就能根據(jù)入口文件以及它的層層依賴得到一個(gè)打好的包匈棘。

直接上代碼

const path = require('path')
const fs = require('fs')

// webpack 的配置
const config = {
  mode: 'development',
  entry: './src/index.js',// 主文件
  output: {
    filename: 'bundle2.js' // 輸出文件名字
  }
}

// webpack的模板執(zhí)行字符串
const TEMPLATE = `!function start (modules) {
  var installModules = {}

  function __pack_require_ (moduleId) {
    if (installModules[moduleId]) {
      return installModules[moduleId]
    }
    var module = installModules[moduleId] = {
      l: false,
      exports: {}
    }
    modules[moduleId].call(module.exports, module, module.exports, __pack_require_)
    module.l = true
    return module.exports
  }

  return __pack_require_(__pack_require_.s = '__entry__')
}({__content__})
`

// webpack 的構(gòu)造器
class Pack {
  constructor (config) {
    this.config = config
    this.entry = config.entry
    this.root = process.cwd()
    this.modules = {}
  }

  // 查找各個(gè)文件依賴創(chuàng)建模塊
  createModles (modulePath, name) {
    let fileContent = fs.readFileSync(modulePath, 'utf-8')
    let {code, deps} = this.parseModule(fileContent, path.dirname(name))
    this.modules[name] = code
    deps.forEach(dep => {
      this.createModles(path.join(this.root, dep), dep)
    })
  }

  // 存下js文件的require引入的依賴以及替換成自己的__pack_require_
  parseModule (code, parent) {
    let deps = []
    var r = /require\((.*)\)/g
    let match
    code = code.replace(r, function (match, arg) {
      const retPath = path.join(parent, arg.replace(/'|"/g, '')).replace('\\', '/')
      deps.push(retPath)
      return `__pack_require_("${retPath}")`
    })
    return {code, deps}
  }

  // 將各個(gè)模塊生成字文本字符串
  generateModuleStr () {
    let fmTmp = ''
    Object.keys(this.modules).forEach(name => {
      fmTmp += `'${name}':function(module, exports, __pack_require_){${this.modules[name]}},`
    })
    return fmTmp
  }

  // 根據(jù)已查找模塊生成最終Bundle文件
  outputBundleFile () {
    let template = TEMPLATE.replace('__entry__', this.entry)
      .replace('__content__', this.generateModuleStr())
    fs.writeFileSync('./' + this.config.output.filename, template) // 文件生成位置
  }

  // 開(kāi)始打包
  start () {
    const entryPath = path.resolve(this.root, this.entry)
    this.createModles(entryPath, this.entry)
    this.outputBundleFile()
    console.log('\n pack  success!!!')
  }
}

const myPack = new Pack(config)

myPack.start()

調(diào)用示例


入口文件src/index.js

const a = require('./a.js')
const b = require('./b.js')
a.aSayHi()
console.log(b.value)

a.js模塊

module.exports = {
  aSayHi () {
    console.log('a.js say hi')
  }
}

b.js模塊

require('./c.js')

module.exports = {
  value: 'this.is B'
}

c.js模塊

console.log('this c.js')

打包結(jié)果bundle2.js

!function start (modules) {
  var installModules = {}

  function __pack_require_ (moduleId) {
    if (installModules[moduleId]) {
      return installModules[moduleId]
    }
    var module = installModules[moduleId] = {
      l: false,
      exports: {}
    }
    modules[moduleId].call(module.exports, module, module.exports, __pack_require_)
    module.l = true
    return module.exports
  }

  return __pack_require_(__pack_require_.s = './src/index.js')
}({
  './src/index.js': function (module, exports, __pack_require_) {
    const a = __pack_require_('src/a.js')
    const b = __pack_require_('src/b.js')
    a.aSayHi()
    console.log(b.value)
  }, 'src/a.js': function (module, exports, __pack_require_) {
    module.exports = {
      aSayHi () {
        console.log('a.js say hi')
      }
    }
  }, 'src/b.js': function (module, exports, __pack_require_) {
    __pack_require_('src/c.js')

    module.exports = {
      value: 'this.is B'
    }
  }, 'src/c.js': function (module, exports, __pack_require_) {
    console.log('this c.js')
  }
})

執(zhí)行打包結(jié)果bundle2.js得到

this c.js
a.js say hi
this.is B

對(duì)比src/index.js入口文件的內(nèi)容

const a = require('./a.js')
const b = require('./b.js')
a.aSayHi()
console.log(b.value)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市网杆,隨后出現(xiàn)的幾起案子羹饰,更是在濱河造成了極大的恐慌伊滋,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件队秩,死亡現(xiàn)場(chǎng)離奇詭異笑旺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)馍资,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)筒主,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鸟蟹,你說(shuō)我怎么就攤上這事乌妙。” “怎么了建钥?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵藤韵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我熊经,道長(zhǎng)泽艘,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任镐依,我火速辦了婚禮匹涮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘槐壳。我一直安慰自己然低,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布务唐。 她就那樣靜靜地躺著雳攘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绍哎。 梳的紋絲不亂的頭發(fā)上来农,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音崇堰,去河邊找鬼沃于。 笑死,一個(gè)胖子當(dāng)著我的面吹牛海诲,可吹牛的內(nèi)容都是我干的繁莹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼特幔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼咨演!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蚯斯,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤薄风,失蹤者是張志新(化名)和其女友劉穎饵较,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體遭赂,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡循诉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撇他。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茄猫。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖困肩,靈堂內(nèi)的尸體忽然破棺而出划纽,到底是詐尸還是另有隱情,我是刑警寧澤锌畸,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布勇劣,位于F島的核電站,受9級(jí)特大地震影響蹋绽,放射性物質(zhì)發(fā)生泄漏芭毙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一卸耘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粘咖,春花似錦蚣抗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至讽坏,卻和暖如春锭魔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背路呜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工迷捧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胀葱。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓漠秋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抵屿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庆锦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在現(xiàn)在的前端開(kāi)發(fā)中,前后端分離轧葛、模塊化開(kāi)發(fā)搂抒、版本控制艇搀、文件合并與壓縮、mock數(shù)據(jù)等等一些原本后端的思想開(kāi)始...
    Charlot閱讀 5,443評(píng)論 1 32
  • 作者:小 boy (滬江前端開(kāi)發(fā)工程師)本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處誉帅。原文地址:https://www.smas...
    iKcamp閱讀 2,759評(píng)論 0 18
  • 目錄第1章 webpack簡(jiǎn)介 11.1 webpack是什么淀散? 11.2 官網(wǎng)地址 21.3 為什么使用 web...
    lemonzoey閱讀 1,736評(píng)論 0 1
  • 寫(xiě)在開(kāi)頭 先說(shuō)說(shuō)為什么要寫(xiě)這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,289評(píng)論 4 31
  • 原文首發(fā)于:Webpack 3档插,從入門(mén)到放棄 Update (2017.8.27) : 關(guān)于 output.pub...
    昵稱(chēng)都被用完了衰閱讀 1,897評(píng)論 4 19