js--webpack核心原理

121 阅读5分钟

webpack是一个打包工具,核心概念

  • entry:入口,webpack构建第一步,从哪儿开始
  • output:输出
  • loader:模块转换器,用于将模块的原内容按照需求转换成新内容(默认只能处理json和js文件)
  • plugin:扩展插件,在webpack构建过程的特定时机注入扩展逻辑,用来改变或优化构建结果
  • mode:控制打包环境,development、production或者none之中的一个,默认production
  • devserver:是一个小型的nodejs,Express服务器,使用webpack-dev-middleware中间件来为通过webpack打包生成的资源文件提供web服务---特点:处理静态服务(图片)、live reloading(实时重新加载)、反向代理接口

核心原理(源码):读取所有文件放入一个js文件,index.js是入口。期间会遇到require和exports。

// index.js 
   var add = require('add.js').default 
   console.log(add(1 , 2)) 

// add.js 
   exports.default = function(a,b) {return a + b}

  • 1、处理(模拟)exports对象:块内定义exports={ }, 包含exports的语句,直接eval出去,相当于不做操作,使用闭包防止变量污染,帮助理解,require函数才是真正的核心
(闭包)创建exports对象,获取并保存导出的内容字符串,eval输出
var exports = {}
(function (exports, code) {
	eval(code)
})(exports, 'exports.default = function(a,b){return a + b}')
  • 2、处理(模拟)require函数:全部复制,添加对象exports={ },return exports闭包连接exports对象。
function require(file) {
	var exports = {};
	(function (exports, code) {
		eval(code)
	})(exports, 'exports.default = function(a,b){return a + b}')
  return exports
}
var add = require('add.js').default
console.log(add(1 , 2))

  • 3、最后,来了多个文件的时候,建立一个key-value(文件名:文件内容)表传入参数执行: 1)require('index.js'): return {},内部执行:require('add.js').default和console

2)require('add.js'):return {default:fn},内部执行:exports赋值

(function (list) {
  function require(file) {
    var exports = {};
    (function (exports, code) {
      eval(code);
    })(exports, list[file]);
    return exports;
  }
  require("index.js");
})({
  "index.js": `
    var add = require('add.js').default
    console.log(add(1 , 2))
        `,
  "add.js": `exports.default = function(a,b){return a + b}`,
});

4、真正webpack生成的bundle.js文件中还需要增加模块间的依赖关系。叫做依赖图(Dependency Graph)

{
  "./src/index.js": {
    "deps": { "./add.js": "./src/add.js" },
    "code": "....."
  },
  "./src/add.js": {
    "deps": {},
    "code": "......"
  }
}

5、总结步骤:分析依赖、es6转es5、替换exports和require

功能实现:从file:'./index.js'开始读取,遇到require,就引入所有内容

  • 单独解析一个模块:读文件fs=>转成抽象语法树AST("@babel/parser")=>依赖收集("@babel/traverse")+转成ES5代码("@babel/core")=> moduleInfo = { file, deps, code };
  • 定义一个数组,遍历deps,生成新的moduleInfo添加到数组,递归遍历孩子的deps并添加
  • 生成bundle文件:
function bundle(file) {
  const depsGraph = JSON.stringify(parseModules(file));
  return `(function (graph) {
        function require(file) {
            function absRequire(relPath) {
                return require(graph[file].deps[relPath])
            }
            var exports = {};
            (function (require,exports,code) {
                eval(code)
            })(absRequire,exports,graph[file].code)
            return exports
        }
        require('${file}')
    })(${depsGraph})`;
}

const content = bundle('./index.js')
!fs.existsSync("./dist") && fs.mkdirSync("./dist");
fs.writeFileSync("./dist/bundle.js", content);

  • 概述:Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

    • 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数\
    • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译\
    • 确定入口:根据配置中的 entry 找出所有的入口文件\
    • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理\
    • 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系\
    • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会\
    • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
简单说

初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中

  • 简述webpack原理与构建过程 原理
    Webpack的构建原理是通过 最外层的自执行函数+闭包的模式,去模拟实现 exports 和 require函数,从入口文件开始,分析每一个模块之间的相互依赖关系,形成依赖图,然后把入口文件和所有依赖打包到一个 chunk,最终输出到 output 的过程

构建流程

  • 初始化流程:从配置文件和 shell 语句中读取和合并参数,并初始化需要使用的插件和配置插件等执行环境所需的参数
  • 编译构建流程:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归进行编译处理
  • 输出流程:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件夹