webpack机制
简介
以下仅为个人粗略总结和代码,看不懂的稍加理解,本文主要用做个人记录。
先大致总结一下
- 1.从哪里开始:webpack根据入口模块开始。
- 2.如何进行:递归读取每个文件,会形成一个依赖列表,依赖列表的,依赖列表是一个以文件相对路径为key,文件内容为value的对象。
- 3.如何处理:对于每个文件会通过AST解析语法树,返回源码。
- 4.loader在哪:loader是何时何地进行处理?它是在读取文件的时候开始起作用,通过正则匹配文件是否需要处理。然后读取配置文件的loader配置,然后通过递归的方式处理文件。
- 5. Plugins呢:plugins是在合适的时机开始工作,那么这个合适时机如何控制呢,是通过tapable事件流机制,实现发布订阅模式。
- 6.最后:最后返回的是一个匿名自执行函数,定义了一个webpack__require方法,解析传入的依赖列表,递归执行。
然后看下代码
核心代码如下:
//入口文件webpack.js #! /usr/bin/env node //第一步:找到当前执行命令的路径,拿到webpack.config.js let path = require("path") let config = require(path.resolve('webpack.config.js')) console.log(path.resolve(),'resolve--------------->') let Compiler = require('../lib/Compiler') let compiler = new Compiler(config) //标识运行编译 compiler.run()
//Compiler.js const fs = require('fs') const path = require('path') const babylon = require('babylon') const travere = require('@babel/traverse').default const t = require('@babel/types') const generator = require('@babel/generator').default const ejs = require('ejs') const {SyncHook} = require('tapable') //babylon把源码转为AST //@babel/traverse //@babel/generator //@babel/types class Compiler { constructor(config) { this.config = config //保存入口文件路径 this.entryID = '' //主模块入口路径 this.modules = {} //存放模块依赖关系 this.entry = config.entry //入口路径 this.root = process.cwd() //当前工作目录 this.hooks = { entryOption: new SyncHook(), compile: new SyncHook(), afterCompile: new SyncHook(), afterPlugins: new SyncHook(), run: new SyncHook(), emit: new SyncHook(), done: new SyncHook(), } //如果传递了plugins参数 let plugins = this.config.plugins if(Array.isArray(plugins)) { plugins.forEach(plugin => { plugin.apply(this) }) } } run() { //执行,并创建模块的依赖关系 this.buildModule(path.resolve(this.root, this.entry), true) //发射一个文件,就是打包后的文件 this.emitFile() } //构建模块 buildModule(modulePath, isEntry) { //首先读取入口文件 let source = this.getSource(modulePath) //模块的ID = this.root - modulePath let moduleName = './' + path.relative(this.root, modulePath) if(isEntry) { this.entryID = moduleName //保存入口名字 } //解析,需要把source源码进行改造,返回一个依赖列表 let {sourceCde, dependencies } = this.parse(source, path.dirname(moduleName)) this.modules[moduleName] = sourceCde dependencies.forEach(dep => { //附模块的递归加载 this.buildModule(path.join(this.root, dep), false) }) } //解析源码, AST解析语法树 parse(source, parentPath) { // console.log(source, parentPath) let ast = babylon(source) let dependencies = [] //依赖数组 travere(ast, { CallExpression() { let node = p.node if(node.callee.name === 'require') { node.callee.name = '_webpack_require_' let moduleName = node.arguments[0].value //这里就是引用模块的名字 moduleName = moduleName + (path.extname(moduleName) ? '' : '.js') moduleName = './' + path.join(parentPath, moduleName) //'src/a.js' dependencies.push(moduleName) node.arguments = [t.stringLiteral(moduleName)] } } }) let sourceCode = generator(ast).code return {sourceCode, dependencies} } //公用读文件的方法 getSource(modulePath) { let content = fs.readFileSync(modulePath, 'utf8') let rules = this.config.module.rules //拿到规则 //拿到每个规则来处理 for(let i=0; i<rules.length; i++) { let rule = rules[i] let { test, use } = rule let len = use.length - 1 if(test.test(modulePath)) { //这个模块需要通过loader转换 function normalLoader() { let loader = require(use[len]) //获取对应loader函数 content = loader(content) //递归调用loader if(len >= 0) { normalLoader() } } normalLoader() } } return content } //发射文件 emitFile() { //用数据 渲染我们的 //拿到输出到哪个目录下 let main = path.join(this.config.output.path, this.config.output.filename) //模板路径 let templateStr = this.getSource(path.join(__dirname, 'main.ejs')) let code = ejs.render(templateStr, {entryId: this.entryID, modules: this.modules}) this.assets = { //资源中路径对应的代码 } this.assets[main] = code fs.writeFileSync(main, this.assets[main]) } } module.exports = Compiler
相关推荐
yezitoo 2020-06-06
gloria0 2020-10-26
yqoxygen 2020-06-02
不知道该写啥QAQ 2020-11-12
webfullStack 2020-11-09
Yvettre 2020-09-15
想做大牛的蜗牛 2020-10-30
gaojie0 2020-09-11
SelinaChan 2020-08-14
不知道该写啥QAQ 2020-08-09
gloria0 2020-08-09
不知道该写啥QAQ 2020-08-02
hline 2020-07-29
SelinaChan 2020-07-28
wangdianyong 2020-07-23
webpackvuees 2020-07-23
yqoxygen 2020-07-20
不知道该写啥QAQ 2020-07-18