webpack原理以及实现 一看就懂

webpack是什么?

如果一点不知道webpack是什么的可能不太适合此篇文章,建议从百度开始学习。
一句话总结:webpack就是通过一个index.js这样的入口文件,根据此入口文件查找各层的js模块的依赖最终打包成一个文件,webpack做了依赖收集

主要原理

  1. 匹配js文件中加载模块的字段 得到模块目录和文件,读取模块代码并保存
  2. 如果模块中还引用了其他模块,继续读取此模块引入的模块
  3. 将各个模块的加载函数比如require换成__webpack_require字段, __webpack_require这个方法用作从webpack已保存的模块中加载模块
  4. 所有模块和他们的依赖加载完毕后,得到一个有所有模块的对象
  5. 然后将所有模块通过固定模板 打包到一个js文件中

比如对入口文件index.js进行打包,index.js中引入了a.js和b.js模块,并且b.js模块引入了c.js模块,webpack都能将各个模块依赖关系以及各个模块的功能全部打包到一个文件中。
webpack使用者只管配置一下入口文件index.js,就能根据入口文件以及它的层层依赖得到一个打好的包。

直接上代码

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

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

// webpack的模板执行字符串
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 的构造器
class Pack {
  constructor (config) {
    this.config = config
    this.entry = config.entry
    this.root = process.cwd()
    this.modules = {}
  }

  // 查找各个文件依赖创建模块
  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}
  }

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

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

  // 开始打包
  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()

调用示例


入口文件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')

打包结果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')
  }
})

执行打包结果bundle2.js得到

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

对比src/index.js入口文件的内容

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

你可能感兴趣的:(webpack原理以及实现 一看就懂)