博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
webpack由浅入深——(webapck简易版)
阅读量:7018 次
发布时间:2019-06-28

本文共 25363 字,大约阅读时间需要 84 分钟。

webpack系列文章

Webpack流程概括

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

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

原生webpack产出

在当前目录下创建usewebpack文件夹,然后在给目录下执行以下操作:

  1. 安装依赖的模块
$ npm init -y$ yarn add webpack webpack-cli html-webpack-plugin复制代码
  1. 编写webpack配置文件
const path = require('path');module.exports = {    mode: 'development',    entry: './src/index.js',    output: {        path: path.resolve(__dirname, 'dist'),        filename: 'bundle.js'    },    module: {},    plugins: []}复制代码
  1. 源文件
  • src/index.js
let a=require('./a');console.log(a);复制代码
  • src/a.js
let b=require('./base/b');module.exports='a'+b;复制代码
  • src/base/b.js
module.exports='b';复制代码
  1. 产出bundle.js
(function(modules) {// 启动函数    // 模块的缓存    var installedModules = {};     // webpack实现的require方法    function __webpack_require__(moduleId) {      // 检查缓存中是否存在此模块ID      if (installedModules[moduleId]) {        return installedModules[moduleId].exports;      }      // 缓存中没有此模块ID,创建一个模块并且放置到缓存中      var module = (installedModules[moduleId] = {        i: moduleId,        l: false,        exports: {}      });      // 执行模块函数为module.export赋值      modules[moduleId].call(        module.exports,        module,        module.exports,        __webpack_require__      );      // 标志模块已经加载      module.l = true;      // 返回模块的export属性      return module.exports;    }    // 加载入口模块并且返回export    return __webpack_require__((__webpack_require__.s = "./src/index.js"));  })({    "./src/a.js": function(module, exports, __webpack_require__) {      eval(        "let b=__webpack_require__(\"./src/base/b.js\");\r\nmodule.exports='a'+b;\n\n"      );    },    "./src/base/b.js": function(module, exports) {      eval("module.exports='b';\n\n");    },    "./src/index.js": function(module, exports, __webpack_require__) {      eval(        'let a=__webpack_require__("./src/a.js");\r\nconsole.log(a);\r\n\n\n'      );    }  }); 复制代码

编写mwebpack

在当前目录下创建mwebpack文件夹,并且在文件下创建mwebpack,然后执行以下操作:

1. 创建项目package.json

{  "name": "mwebpack",  "version": "1.0.0",  "description": "",  "main": "index.js",  // 添加了bin选项,使用命令行来运行./bin/mwebpack.js  "bin": {    "mwebpack": "./bin/mwebpack.js"  },  "keywords": [],  "author": "",  "license": "ISC"}复制代码

2. 创建/bin/mwebpack.js

  • 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数
#! /usr/bin/env node        /*标注文件的运行环境*/const path = require('path');const fs = require('fs');//当前工作目录const root = process.cwd();//配置文件和 Shell 语句中读取与合并参数,这里简化逻辑,没有处理shell部分let options = require(path.resolve('webpack.config.js'));复制代码
  • 开始编译:用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
#! /usr/bin/env node    const path = require('path');const fs = require('fs');const root = process.cwd();//引入Compilerconst Compiler = require('../lib/Compiler'); let options = require(path.resolve('webpack.config.js'));//初始化compiler对象加载所有配置的插件let compiler = new Compiler(options); // 执行对象的 run 方法开始执行编译compiler.run();复制代码

3. 初始化Compiler

在当mwebpack目录下创建/bin/Compiler.js

const path = require('path');const fs = require('fs');class Compiler {    constructor(options){        this.options = options;    }    run(){        console.log('---------start---------')    }}module.exports = Compiler复制代码

4. 连接usewebpack和mwebpack

  • 将nmwebpack/bin/mwebpack.js链接到全局D:/dev/node.js/mwebpack(node安装在D盘)
  1. 用命令行切换到mwebpack目录,
  2. 然后执行npm link,那么nmwebpack就和npm和npx一样成为nodejs的命令了
  • 在usewebpack中使用mwebpack编译文件
  1. 用命令行切换到usewebpack目录,
  2. 然后执行npx mwebpack命令,可以看到usewabpack下node_modules中的.bin目录下出现了mwebpack.cmd和mwebpack的包。
  3. mwebpack.cmd会调用全局的mwebpack命令,然后再调用mwebpack/bin/mwebpack.js.

5. 完善run函数

  • 确定入口:根据配置中的entry找出所有的入口文件
  • 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统
const path = require('path');const fs = require('fs');class Compiler {    constructor(options){        this.options = options;    }    run(){        let that = this;        let {entry} = this.options; // 获取webpck.config.js中的entry        this.root = process.cwd();              this.entryId = null;        //记录入口的id,这里采用单入口简化        this.modules = {};          //缓存入口的依赖,这里采用单入口简化                // 找出该模块依赖的模块        //再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理        this.buildModule(path.resolve(this.root, entry), true);                // 输出资源        this.emitFile();    }}module.exports = Compiler复制代码

6. 编写buildModule

  • 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  • 完成模块编译:在经过第4步使用Loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
const path = require('path');const fs = require('fs');class Compiler {    constructor(options){        this.options = options;    }    run(){        let that = this;        let {entry} = this.options;         this.root = process.cwd();              this.entryId = null;                this.modules = {};                  this.buildModule(path.resolve(this.root, entry), true);        this.emitFile();    }     getSource(modulePath) {        let source = fs.readFileSync(modulePath, 'utf8');                //TODO:loader的处理逻辑写在这里,后面会提到                return source;             }    buildModule(modulePath,isEntry){        let that = this;         let source = this.getSource(modulePath);//获取源代码                //生成相对于工作根目录的模块ID,相对路径exp:'./sec/index'        let moduleId = './' + path.relative(this.root, modulePath);                //如果是入口的话把id赋给compiler对象的入口        if (isEntry) {            this.entryId = moduleId;        }            //获取AST的编译结果,获取依赖的模块,并且将代码进行转换        let { dependencies, sourcecode } = this.parse(source, path.dirname(moduleId));        this.modules[moduleId] = sourcecode;                //递归解析依赖的模块        dependencies.forEach(dependency => that.buildModule(path.join(that.root, dependency)));    }    emitFile(){            }}module.exports = Compiler复制代码

7. 编写parse函数

  • 编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

代码转换成AST,webpack中使用的Acorn,这里使用babel-types,babel-traverse,babel-generator替代:

  • babylon把源码转成AST
  • babel-types生成节点或者判断节点类型
  • babel-traverse遍历AST,捕获指定的节点
  • babel-generator将AST重新生成代码
npm install babylon babel-types babel-generator babel-traverse复制代码

查看原生webpack生成的bundle.js,需要将require换成__webpack_require__,并且将路径修改为相对于根目录的相对路径

{    "./src/a.js": function(module, exports, __webpack_require__) {      eval(        "let b=__webpack_require__(\"./src/base/b.js\");\r\nmodule.exports='a'+b;\n\n"      );    },    "./src/base/b.js": function(module, exports) {      eval("module.exports='b';\n\n");    },    "./src/index.js": function(module, exports, __webpack_require__) {      eval(        'let a=__webpack_require__("./src/a.js");\r\nconsole.log(a);\r\n\n\n'      );    }  }复制代码

利用https://astexplorer.net/可以看到require转换成AST:

const path = require('path');const fs = require('fs');const babylon = require('babylon');const t = require('babel-types');//采用es6的写法,所以要在后面添加.defaultconst traverse = require('babel-traverse').default;const generator = require('babel-generator').default;class Compiler {    constructor(options){        this.options = options;    }    run(){        let that = this;        let {entry} = this.options;         this.root = process.cwd();              this.entryId = null;                this.modules = {};                  this.buildModule(path.resolve(this.root, entry), true);        this.emitFile();    }    getSource(modulePath) {        let source = fs.readFileSync(modulePath, 'utf8');        //TODO:loader的处理逻辑写在这里,后面会提到        return source;     }    buildModule(modulePath,isEntry){        let that = this;        let source = this.getSource(modulePath);        let moduleId = './' + path.relative(this.root, modulePath);        if (isEntry) {            this.entryId = moduleId;        }        let { dependencies, sourcecode } = this.parse(source, path.dirname(moduleId));        this.modules[moduleId] = sourcecode;        dependencies.forEach(dependency => that.buildModule(path.join(that.root, dependency)));    }    parse(source, parentPath) {        let that = this;        let ast = babylon.parse(source);    //源码转语法树        let dependencies = [];      //存储依赖的模块路径        //遍历AST找到对应的节点进行修改        traverse(ast, {            CallExpression(p) {//p当前路径                if (p.node.callee.name == 'require') {                    let node = p.node;                    //修改方法名                    node.callee.name = '__webpack_require__';                    // 得到模块名exp:'./a'                    let moduleName = node.arguments[0].value;                    //如果需要的话,添加.js后缀                     moduleName += (moduleName.lastIndexOf('.') > 0 ? '' : '.js');                    //得到依赖模块的id,exp:'./src/a'                    let moduleId = './' + path.relative(that.root, path.join(parentPath, moduleName));                    //相对于根目录的相对路径                    node.arguments = [t.stringLiteral(moduleId)];                    //把模块id放置到当前模块的依赖列表里                    dependencies.push(moduleId);                }            }        });        //将修改的AST重新生成代码        let sourcecode = generator(ast).code;        return { sourcecode, dependencies };    }    emitFile(){            }}module.exports = Compiler复制代码

8. 编写emitFile函数

  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会

每次编译打包后,都会发现webpack打包后的结果很大部分都是一样的,可以抽离出一个模板用来构建每次打包的结果:

// MainTemplate这里采用ejs模板简化(function(modules) {    var installedModules = {};     function __webpack_require__(moduleId) {      if (installedModules[moduleId]) {        return installedModules[moduleId].exports;      }      var module = (installedModules[moduleId] = {        i: moduleId,        l: false,        exports: {}      });      modules[moduleId].call(        module.exports,        module,        module.exports,        __webpack_require__      );      module.l = true;      return module.exports;    }    return __webpack_require__((__webpack_require__.s = "<%-entryId%>"));  })({   <%for (let moduleId in modules) {
let source = modules[moduleId];%> "<%-moduleId%>":(function(module,exports,__webpack_require__){
eval(`<%-source%>`);}), <% }%> }); 复制代码

完善emitFile函数

const path = require('path');const fs = require('fs');const babylon = require('babylon');const t = require('babel-types');const traverse = require('babel-traverse').default;const generator = require('babel-generator').default;const ejs = require('ejs');     //引入ejsclass Compiler {    constructor(options){        this.options = options;    }    run(){        let that = this;        let {entry} = this.options;         this.root = process.cwd();              this.entryId = null;                this.modules = {};                  this.buildModule(path.resolve(this.root, entry), true);        this.emitFile();    }    getSource(modulePath) {        let source = fs.readFileSync(modulePath, 'utf8');        //TODO:loader的处理逻辑写在这里,后面会提到        return source;     }    buildModule(modulePath,isEntry){        let that = this;        let source = this.getSource(modulePath);        let moduleId = './' + path.relative(this.root, modulePath);        if (isEntry) {            this.entryId = moduleId;        }        let { dependencies, sourcecode } = this.parse(source, path.dirname(moduleId));        this.modules[moduleId] = sourcecode;        dependencies.forEach(dependency => that.buildModule(path.join(that.root, dependency)));    }    parse(source, parentPath) {        let that = this;        let ast = babylon.parse(source);            let dependencies = [];            traverse(ast, {            CallExpression(p) {                if (p.node.callee.name == 'require') {                    let node = p.node;                    node.callee.name = '__webpack_require__';                    let moduleName = node.arguments[0].value;                    moduleName += (moduleName.lastIndexOf('.') > 0 ? '' : '.js');                    let moduleId = './' + path.relative(that.root, path.join(parentPath, moduleName));                    node.arguments = [t.stringLiteral(moduleId)];                    dependencies.push(moduleId);                }            }        });        let sourcecode = generator(ast).code;        return { sourcecode, dependencies };    }    emitFile(){        // 读取模板文件        let entryTemplate = fs.readFileSync(path.join(__dirname, 'entry.ejs'), 'utf8');        // 获取渲染的数据        let { entryId, modules } = this;        // 将数据渲染到模板上        let source = ejs.compile(entryTemplate)({            entryId,            modules        });        //找到目标路径        let target = path.join(this.options.output.path, this.options.output.filename);        //将渲染后的模板目标文件        fs.writeFileSync(target, source);    }}module.exports = Compiler复制代码

输出的bundle.js文件:

(function(modules) {    var installedModules = {};     function __webpack_require__(moduleId) {      if (installedModules[moduleId]) {        return installedModules[moduleId].exports;      }      var module = (installedModules[moduleId] = {        i: moduleId,        l: false,        exports: {}      });      modules[moduleId].call(        module.exports,        module,        module.exports,        __webpack_require__      );      module.l = true;      return module.exports;    }    return __webpack_require__((__webpack_require__.s = "./src\index.js"));  })({    "./src\index.js":(function(module,exports,__webpack_require__){
eval(`let a = __webpack_require__("./src\\a.js");console.log(a);`);}), "./src\a.js":(function(module,exports,__webpack_require__){
eval(`let b = __webpack_require__("./src\\base\\b.js");module.exports = 'a' + b;`);}), "./src\base\b.js":(function(module,exports,__webpack_require__){
eval(`module.exports = 'b';`);}), }); 复制代码

9. 实现loader功能

上面的webpack已经具备打包js的功能了,但是还不能打包css等文件,原生的webpack是通过各种loader来打包css等其他文件的,所以再getSource时调用loader,将其他文件处理成js,然后进行后面的操作

const path = require('path');const fs = require('fs');const babylon = require('babylon');const t = require('babel-types');const traverse = require('babel-traverse').default;const generator = require('babel-generator').default;const ejs = require('ejs');     //引入ejsclass Compiler {    constructor(options){        this.options = options;    }    run(){        let that = this;        let {entry} = this.options;         this.root = process.cwd();              this.entryId = null;                this.modules = {};                  this.buildModule(path.resolve(this.root, entry), true);        this.emitFile();    }    getSource(modulePath) {        let source = fs.readFileSync(modulePath, 'utf8');                //获取webpack.config.js中的rules        let rules = that.options.module.rules;        //遍历rules调用loader        for (let i = 0; i < rules.length; i++) {            let rule = rules[i];            // 用rule的test中正则匹配文件的类型是否需要使用laoder            if (rule.test.test(modulePath)) {                //获取rule中的loaders,例如['style-laoder','css-loader']                let loaders = rule.use;                let length = loaders.length;    //loader的数量                 let loaderIndex = length - 1;   // 往右向左执行                                // loader遍历器                function iterateLoader() {                    let loaderName = loaders[loaderIndex--];                    //loader只是一个包名,需要用require引入                    let loader = require(join(that.root, 'node_modules', loaderName));                    //使用loader,可以看出loader的本质是一个函数                    source = loader(source);                    if (loaderIndex >= 0) {                        iterateLoader();                    }                }                                //遍历执行loader                iterateLoader();                break;            }        }        return source;     }    buildModule(modulePath,isEntry){        let that = this;        let source = this.getSource(modulePath);        let moduleId = './' + path.relative(this.root, modulePath);        if (isEntry) {            this.entryId = moduleId;        }        let { dependencies, sourcecode } = this.parse(source, path.dirname(moduleId));        this.modules[moduleId] = sourcecode;        dependencies.forEach(dependency => that.buildModule(path.join(that.root, dependency)));    }    parse(source, parentPath) {        let that = this;        let ast = babylon.parse(source);            let dependencies = [];            traverse(ast, {            CallExpression(p) {                if (p.node.callee.name == 'require') {                    let node = p.node;                    node.callee.name = '__webpack_require__';                    let moduleName = node.arguments[0].value;                    moduleName += (moduleName.lastIndexOf('.') > 0 ? '' : '.js');                    let moduleId = './' + path.relative(that.root, path.join(parentPath, moduleName));                    node.arguments = [t.stringLiteral(moduleId)];                    dependencies.push(moduleId);                }            }        });        let sourcecode = generator(ast).code;        return { sourcecode, dependencies };    }    emitFile(){        let entryTemplate = fs.readFileSync(path.join(__dirname, 'entry.ejs'), 'utf8');        let { entryId, modules } = this;        let source = ejs.compile(entryTemplate)({            entryId,            modules        });        let target = path.join(this.options.output.path, this.options.output.filename);        fs.writeFileSync(target, source);    }}module.exports = Compiler复制代码

在usewebpack创建mode_modules/less-loader.js(为了说明loader的原理不使用的第三方的loader)

//less-loader的作用将less文件转化为css文件var less = require('less');module.exports = function (source) {    let css;    less.render(source, (err, output) => {        css = output.css;    });    return css.replace(/\n/g, '\\n', 'g');}复制代码

在usewebpack创建mode_modules/less-loader.js

//style-loader的功能就是将加载的css文件放在style标签中插入到页面module.exports = function (source) {    let str = `      let style = document.createElement('style');      style.innerHTML = ${JSON.stringify(source)};      document.head.appendChild(style);    `;    return str;}复制代码

在usewebpack创建/src/index.less,

@color:red;body{    color:@color;}复制代码

修改usewebpack中/src/index.js,

require('index.less')复制代码

修改usewebpack中package.json

const path = require('path');module.exports = {    mode: 'development',    entry: './src/index.js',    output: {        path: path.resolve(__dirname, 'dist'),        filename: 'bundle.js'    },    module: {        rules: [            {                test: /\.less$/,                use: ['style-loader', 'less-loader']            }        ]    },    plugins: []}复制代码

创建一个页面引用打包后的js,在浏览器中运行:

10. 实现plugin功能

原生webpack支持很多种插件,在webpack编译的过程中的各个阶段使用,常见的一些钩子:

  • entryOption 读取配置文件
  • afterPlugins 加载所有的插件
  • run 开始执行编译流程
  • compile 开始编译
  • afterCompile 编译完成
  • emit 写入文件
  • done 完成整体流程
  • 修改bin/mwebpack.js

注册规则阶段的钩子,供用户订阅来执行插件。

const path = require('path');const fs = require('fs');const babylon = require('babylon');const t = require('babel-types');const traverse = require('babel-traverse').default;const generator = require('babel-generator').default;const ejs = require('ejs');//使用tapable来创建发布者,利用call等来触发const { SyncHook } = require('tapable');class Compiler {    constructor(options){        this.options = options;         this.hooks = {            entryOption: new SyncHook(),            afterPlugins: new SyncHook(),            run: new SyncHook(),            beforeCompile: new SyncHook(),            afterCompile: new SyncHook(),            emit: new SyncHook(),            afterEmit: new SyncHook(),            done: new SyncHook(),        }    }    run(){        let compiler = this;        compiler.hooks.run.call();              //触发run        let {entry} = this.options;         this.root = process.cwd();              this.entryId = null;                this.modules        compiler.hooks.beforeCompile.call();    //触发beforeCompile        this.buildModule(path.resolve(this.root, entry), true);        compiler.hooks.afterCompile.call();     //afterCompile        this.emitFile();        compiler.hooks.afterEmit.call();        //触发afterEmit        compiler.hooks.done.call();             //触发done    }    getSource(modulePath) {        let source = fs.readFileSync(modulePath, 'utf8');        let rules = that.options.module.rules;        for (let i = 0; i < rules.length; i++) {            let rule = rules[i];            if (rule.test.test(modulePath)) {                let loaders = rule.use;                let length = loaders.length;                    let loaderIndex = length - 1;                 function iterateLoader() {                    let loaderName = loaders[loaderIndex--];                    let loader = require(join(that.root, 'node_modules', loaderName));                    source = loader(source);                    if (loaderIndex >= 0) {                        iterateLoader();                    }                }                iterateLoader();                break;            }        }        return source;     }    buildModule(modulePath,isEntry){        let that = this;        let source = this.getSource(modulePath);        let moduleId = './' + path.relative(this.root, modulePath);        if (isEntry) {            this.entryId = moduleId;        }        let { dependencies, sourcecode } = this.parse(source, path.dirname(moduleId));        this.modules[moduleId] = sourcecode;        dependencies.forEach(dependency => that.buildModule(path.join(that.root, dependency)));    }    parse(source, parentPath) {        let that = this;        let ast = babylon.parse(source);            let dependencies = [];            traverse(ast, {            CallExpression(p) {                if (p.node.callee.name == 'require') {                    let node = p.node;                    node.callee.name = '__webpack_require__';                    let moduleName = node.arguments[0].value;                    moduleName += (moduleName.lastIndexOf('.') > 0 ? '' : '.js');                    let moduleId = './' + path.relative(that.root, path.join(parentPath, moduleName));                    node.arguments = [t.stringLiteral(moduleId)];                    dependencies.push(moduleId);                }            }        });        let sourcecode = generator(ast).code;        return { sourcecode, dependencies };    }    emitFile(){        this.hooks.emit.call();             //触发emit        let entryTemplate = fs.readFileSync(path.join(__dirname, 'entry.ejs'), 'utf8');        let { entryId, modules } = this;        let source = ejs.compile(entryTemplate)({            entryId,            modules        });        let target = path.join(this.options.output.path, this.options.output.filename);        fs.writeFileSync(target, source);    }}module.exports = Compiler复制代码
#! /usr/bin/env node    const path = require('path');const fs = require('fs');const root = process.cwd();const Compiler = require('../lib/Compiler');let options = require(path.resolve('webpack.config.js'));let compiler = new Compiler(options); compiler.hooks.entryOption.call();     //触发entryOptionslet {plugins} = options;        //获取webpack.config.js中的plugns进行注册plugins.forEach(plugin => {    plugin.apply(compiler)});compiler.hooks.afterPlugins.call(),     //触发afterPluginscompiler.run();复制代码

修改usewebpack中的webpack.config.js

const path = require('path');//为了简要说明webpack插件的原理,不采用require第三方的插件class EntryOptionWebpackPlugin {    apply(compiler) {        compiler.hooks.entryOption.tap('Plugin', (option) => {            console.log('EntryOptionWebpackPlugin');        });    }}class AfterPlugins {    apply(compiler) {        compiler.hooks.afterPlugins.tap('Plugin', (option) => {            console.log('AfterPlugins');        });    }}class RunPlugin {    apply(compiler) {        compiler.hooks.run.tap('Plugin', (option) => {            console.log('RunPlugin');        });    }}class CompileWebpackPlugin {    apply(compiler) {        compiler.hooks.compile.tap('Plugin', (option) => {            console.log('CompileWebpackPlugin');        });    }}class AfterCompileWebpackPlugin {    apply(compiler) {        compiler.hooks.afterCompile.tap('Plugin', (option) => {            console.log('AfterCompileWebpackPlugin');        });    }}class EmitWebpackPlugin {    apply(compiler) {        compiler.hooks.emit.tap('Plugin', () => {            console.log('EmitWebpackPlugin');        });    }}class DoneWebpackPlugin {    apply(compiler) {        compiler.hooks.done.tap('Plugin', (option) => {            console.log('DoneWebpackPlugin');        });    }}module.exports = {    mode: 'development',    entry: './src/index.js',    output: {        path: path.resolve(__dirname, 'dist'),        filename: 'bundle.js'    },    module: {        rules: [            {                test: /\.less$/,                use: ['style-loader', 'less-loader']            }        ]    },    plugins: [        new EntryOptionWebpackPlugin(),        new AfterPlugins(),        new RunPlugin(),        new CompileWebpackPlugin(),        new AfterCompileWebpackPlugin(),        new EmitWebpackPlugin(),        new DoneWebpackPlugin()    ]}复制代码

执行npx mwebpack 可以看到

##结语 webpack的主要工作:

  1. 合并option,获取plugin注册插件
  2. run获得入口文件,用loader对入口文件进行处理,
  3. 将其转化为AST进行代码修改,递归分析其依赖的模块
  4. 根据入口文件的依赖项,将其渲染到对应的模板文件,然后写到出口文件中

转载地址:http://hxzxl.baihongyu.com/

你可能感兴趣的文章
英国电信 云计算还不成熟
查看>>
运行于显卡(GPU)的Rootkit木马和键盘记录器问世
查看>>
三星推出高容量、企业级绿色SSD
查看>>
PHP/如何在Linux服务器中隐藏PHP版本
查看>>
高速加密:IBM发布更猛更快的z14大型机
查看>>
任何口令都可访问Advantech工业网关
查看>>
如何让CIO的ITIL实施“一帆风顺”
查看>>
迅雷移动端业务发展迅猛 Q2广告收入同比上涨41.6%
查看>>
迎接数字化社会,华为积极布局
查看>>
阿里云成为CNCF金牌会员 提供云端Kubernetes解决方案
查看>>
F5:亡羊补牢为时已晚,投资网络安全比处理漏洞更划算
查看>>
青云QingCloud推出HBase集群服务 支持SQL等高级功能
查看>>
Windows远程桌面漏洞Esteemaudit(CVE-2017-9073)补丁简要分析
查看>>
Arbor Networks凭借业界全面的 DDoS 防御组合为各类客户提供可用性保护
查看>>
达沃时代帮助VSAN提供NAS方案
查看>>
买服务器网络设备时该考虑哪些因素?
查看>>
悲催的CISO:数据泄露事故的替罪羊
查看>>
2015年十大安全故事回顾
查看>>
数据挖掘与预测分析术语总结
查看>>
寻找“高级威胁”防御的答案
查看>>