导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1. 跑通webpack
    • 1.1 webpack.config.js
    • 1.2 cli.js
    • 1.3 index.js
    • 1.4 title.js
    • 1.5 main.js
  • 2. 创建Compiler.js
    • 2.1 webpack\index.js
    • 2.2 Compiler.js
    • 2.3 NodeEnvironmentPlugin.js
    • 2.4 WebpackOptionsApply.js
  • 3. 监听make事件
    • 3.1 WebpackOptionsApply.js
    • 3.2 EntryOptionPlugin.js
    • 3.3 SingleEntryPlugin.js
  • 4. make
    • 4.1 webpack\Compiler.js
    • 4.2 Compilation.js
    • 4.3 NormalModuleFactory.js
    • 4.4 NormalModule.js
  • 5. build
    • 5.1 Compilation.js
    • 5.2 NormalModule.js
  • 6. seal封装chunk
    • 6.1 Compiler.js
    • 6.2 Compilation.js
  • 7. emit
    • 7.1 Compiler.js
    • 7.2 Compilation.js
    • 7.3 main.ejs
    • 7.4 Stats.js
  • 8. 支持loader
    • 8.1 NormalModule.js
    • 8.2 index.js
    • 8.3 src\index.less
    • 8.4 less-loader.js
    • 8.5 style-loader.js

1. 跑通webpack #

1.1 webpack.config.js #

const path = require('path');
module.exports = {
    context:process.cwd(),
    mode:'development',
    devtool:'none',
    entry:'./src/index.js',
    output:{
        path:path.resolve(__dirname,'dist'),
        filename:'[name].js'
    }
}

1.2 cli.js #

const webpack = require("webpack");
const webpackOptions = require("./webpack.config");
const compiler = webpack(webpackOptions);
compiler.run((err, stats) => {
  console.log(err);
  console.log(
    stats.toJson({
      entries: true,
      chunks: true,
      modules: true,
      _modules: true,
      assets: true
    })
  );
});

1.3 index.js #

src\index.js

let title = require('./title');
console.log(title);

1.4 title.js #

src\title.js

1.5 main.js #

dist\main.js

(function(modules) { // webpackBootstrap
    // The module cache
    var installedModules = {};

    // The require function
    function __webpack_require__(moduleId) {

        // Check if module is in cache
        if(installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // Create a new module (and put it into the cache)
        var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        };

        // Execute the module function
        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

        // Flag the module as loaded
        module.l = true;

        // Return the exports of the module
        return module.exports;
    }


    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = modules;

    // expose the module cache
    __webpack_require__.c = installedModules;

    // define getter function for harmony exports
    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, { enumerable: true, get: getter });
        }
    };

    // define __esModule on exports
    __webpack_require__.r = function(exports) {
        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
        }
        Object.defineProperty(exports, '__esModule', { value: true });
    };

    // create a fake namespace object
    // mode & 1: value is a module id, require it
    // mode & 2: merge all properties of value into the ns
    // mode & 4: return value when already ns object
    // mode & 8|1: behave like require
    __webpack_require__.t = function(value, mode) {
        if(mode & 1) value = __webpack_require__(value);
        if(mode & 8) return value;
        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
        var ns = Object.create(null);
        __webpack_require__.r(ns);
        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
        return ns;
    };
    // getDefaultExport function for compatibility with non-harmony modules
    __webpack_require__.n = function(module) {
        var getter = module && module.__esModule ?
            function getDefault() { return module['default']; } :
            function getModuleExports() { return module; };
        __webpack_require__.d(getter, 'a', getter);
        return getter;
    };
    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    // __webpack_public_path__
    __webpack_require__.p = "";
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
({
 "./src/index.js":
 (function(module, exports, __webpack_require__) {
  let title = __webpack_require__(/*! ./title */ "./src/title.js");
  console.log(title);
 }),
 "./src/title.js":
 (function(module, exports) {
   module.exports = "title";
 })
});

2. 创建Compiler.js #

2.1 webpack\index.js #

const NodeEnvironmentPlugin = require("./plugins/NodeEnvironmentPlugin");
const WebpackOptionsApply = require("./WebpackOptionsApply");
const Compiler = require("./Compiler");
function webpack(options) {
  options.context = options.context || path.resolve(process.cwd());
  //创建compiler
  let compiler = new Compiler(options.context);
  //给compiler指定options
  compiler.options = Object.assign(compiler.options, options);
  //设置读写文件的API
  new NodeEnvironmentPlugin().apply(compiler);
  //调用配置文件里配置的插件并依次调用
  if (options.plugins && Array.isArray(options.plugins)) {
    for (const plugin of options.plugins) {
      plugin.apply(compiler);
    }
  }
  compiler.hooks.environment.call(); //设置变量
  compiler.hooks.afterEnvironment.call(); //设置变量完成后
  new WebpackOptionsApply().process(options, compiler); //处理参数
  return compiler;
}

module.exports = webpack;

2.2 Compiler.js #

webpack\Compiler.js

const {
  Tapable,
  SyncHook,
  SyncBailHook,
  AsyncSeriesHook,
  AsyncParallelHook
} = require("tapable");
class Compiler extends Tapable {
  constructor(context) {
    super();
    this.hooks = {
      environment: new SyncHook([]),
      afterEnvironment: new SyncHook([]),
      afterPlugins: new SyncHook(["compiler"])
    };
    this.options = {};
    this.context = context; //设置上下文路径
  }
  run() {
    console.log("开始编译");
  }
}
module.exports = Compiler;

2.3 NodeEnvironmentPlugin.js #

webpack\plugins\NodeEnvironmentPlugin.js

const fs = require("fs");
class NodeEnvironmentPlugin {
  apply(compiler) {
    compiler.inputFileSystem = fs; //设置读文件的模块
    compiler.outputFileSystem = fs; //设置写文件的模块
  }
}
module.exports = NodeEnvironmentPlugin;

2.4 WebpackOptionsApply.js #

webpack\WebpackOptionsApply.js

module.exports = class WebpackOptionsApply {
  process(options, compiler) {
    //插件绑定结束
    compiler.hooks.afterPlugins.call(compiler);
  }
};

3. 监听make事件 #

3.1 WebpackOptionsApply.js #

webpack\WebpackOptionsApply.js

+const EntryOptionPlugin = require("./plugins/EntryOptionPlugin");
module.exports = class WebpackOptionsApply {
  process(options, compiler) {
     //挂载入口文件插件
+    new EntryOptionPlugin().apply(compiler);
    //触发entryOption事件执行
+    compiler.hooks.entryOption.call(options.context, options.entry);
    //插件绑定结束
    compiler.hooks.afterPlugins.call(compiler);
  }
};

3.2 EntryOptionPlugin.js #

webpack\plugins\EntryOptionPlugin.js

const SingleEntryPlugin = require("./SingleEntryPlugin");
class EntryOptionPlugin {
  apply(compiler) {
    compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
      new SingleEntryPlugin(context, entry, "main").apply(compiler);
    });
  }
}

module.exports = EntryOptionPlugin;

3.3 SingleEntryPlugin.js #

webpack\plugins\SingleEntryPlugin.js

module.exports = class EntryOptionPlugin {
  constructor(context, entry, name) {
    this.context = context;
    this.entry = entry;
    this.name = name;
  }
  apply(compiler) {
    compiler.hooks.make.tapAsync(
      "SingleEntryPlugin",
      (compilation, callback) => {
          //入口文件 代码块的名称 context上下文绝对路径
        const { entry, name, context } = this;
        compilation.addEntry(context, entry, name, callback);
      }
    );
  }
};

4. make #

4.1 webpack\Compiler.js #

+const {Tapable,SyncHook,SyncBailHook,AsyncSeriesHook,AsyncParallelHook} = require("tapable");
+const Compilation  = require('./Compilation');
class Compiler extends Tapable {
  constructor(context) {
    super();
    this.hooks = {
      environment: new SyncHook([]),
      afterEnvironment: new SyncHook([]),
      afterPlugins: new SyncHook(["compiler"]),
      entryOption: new SyncBailHook(["context", "entry"]),
+      beforeRun: new AsyncSeriesHook(["compiler"]),
+      run: new AsyncSeriesHook(["compiler"]),
+      beforeCompile: new AsyncSeriesHook(["params"]),
+      compile: new SyncHook(["params"]),
+      make: new AsyncParallelHook(["compilation"]),
+      thisCompilation: new SyncHook(["compilation", "params"]),
+      compilation: new SyncHook(["compilation", "params"]),
+      done: new AsyncSeriesHook(["stats"])
    };
    this.options = {};
    this.context = context; //设置上下文路径
  }

+  run(finalCallback) {
+    //编译完成后的回调
+    const onCompiled = (err, compilation) => {
+     
+    };
+    //准备运行编译
+    this.hooks.beforeRun.callAsync(this, err => {
+      //运行
+      this.hooks.run.callAsync(this, err => {
+        //开始编译,编译完成后执行conCompiled回调
+        this.compile(onCompiled);
+      });
+    });
+  }
+  newCompilation(params){
+    const compilation = new Compilation(this);
+    this.hooks.thisCompilation.call(compilation, params);
+      this.hooks.compilation.call(compilation, params);
+    return compilation;
+  }
+  compile(onCompiled){
+    this.hooks.beforeCompile.callAsync({}, err => {
+      this.hooks.compile.call();
+      const compilation = this.newCompilation();
+      this.hooks.make.callAsync(compilation, err => {
+          console.log(err,'make完成')
+      });
+    });
+  }
}
module.exports = Compiler;

4.2 Compilation.js #

webpack\Compilation.js

const normalModuleFactory = require('./NormalModuleFactory');
const {Tapable,SyncHook,SyncBailHook,AsyncSeriesHook,AsyncParallelHook} = require("tapable");
const path = require('path');
class Compilation extends Tapable {
    constructor(compiler) {
        super();
        this.compiler = compiler;
        this.options = compiler.options;
        this.context = compiler.context;
        this.inputFileSystem = compiler.inputFileSystem;
            this.outputFileSystem = compiler.outputFileSystem;
        this.hooks = {
          addEntry: new SyncHook(["entry", "name"])
        }
        this.entries=[];
    }
    //context ./src/index.js main callback(终级回调)
    addEntry(context, entry, name, finallyCallback) {
      this.hooks.addEntry.call(entry, name);//开始增加入口
      this._addModuleChain(context,entry,name);
      finallyCallback();
   }
   _addModuleChain(context,entry,name){
     let module = normalModuleFactory.create(
         {name,  //模块所属的代码块的名称
         context:this.context,//上下文
         request:path.posix.join(context,entry)});//模块完整路径
     module.build(this);//开始编译模块
     this.entries.push(module);//把编译好的模块添加到入口列表里面
   }
}
module.exports = Compilation;

4.3 NormalModuleFactory.js #

webpack\NormalModuleFactory.js

const path = require("path");
const NormalModule = require('./NormalModule');
class NormalModuleFactory{
  create(data) {
    return new NormalModule(data);
  }
}
module.exports = new NormalModuleFactory();

4.4 NormalModule.js #

webpack\NormalModule.js

class NormalModule{
    constructor({name,context,request}){
      this.name = name;
      this.context = context;
      this.request = request;  
    }
    build(compilation){
        console.log('开始编译入口模块');
    }
}
module.exports = NormalModule;

5. build #

5.1 Compilation.js #

webpack\Compilation.js

const normalModuleFactory = require('./NormalModuleFactory');
const {Tapable,SyncHook,SyncBailHook,AsyncSeriesHook,AsyncParallelHook} = require("tapable");
const path = require('path');
class Compilation extends Tapable {
    constructor(compiler) {
        super();
        this.compiler = compiler;
        this.options = compiler.options;
        this.context = compiler.context;
        this.inputFileSystem = compiler.inputFileSystem;
            this.outputFileSystem = compiler.outputFileSystem;
        this.hooks = {
          addEntry: new SyncHook(["entry", "name"])
        }
+        this.entries=[];
+        this._modules = {}; //模块代码
+        this.modules=[];
    }
    //context ./src/index.js main callback(终级回调)
    addEntry(context, entry, name, finallyCallback) {
      this.hooks.addEntry.call(entry, name);//开始增加入口
      this._addModuleChain(context,entry,name);
+      console.log('编译完成');
+      console.log(this);
      finallyCallback();
   }
   //增加模块链
   _addModuleChain(context,entry,name){
     let module = normalModuleFactory.create(
         {name,  //模块所属的代码块的名称
         context:this.context,//上下文
         request:path.posix.join(context,entry)});//模块完整路径
     module.build(this);//开始编译模块
     this.entries.push(module);//把编译好的模块添加到入口列表里面
   }
   //编译依赖的模块
+   buildDependencies(module,dependencies){
+    module.dependencies = dependencies.map(data =>{//映射老模块到新的模块
+       let module = normalModuleFactory.create(data);//创建新的模块
+       return module.build(this);//编译模块并返回自己
+    });
+  }
}
module.exports = Compilation;

5.2 NormalModule.js #

webpack\NormalModule.js

+const fs = require('fs');
+const ejs = require('ejs');
+const path = require('path');
+const babylon = require('babylon');
+const t = require('babel-types');
+const generate = require('babel-generator').default;
+const traverse = require('babel-traverse').default;
class NormalModule{
    constructor({name,context,request}){
      this.name = name;
      this.context = context;
      this.request = request;  
+      this.dependencies = [];
+      this.moduleId;
+      this._ast;
+      this._source;
    }
    build(compilation){
+        let originalSource = compilation.inputFileSystem.readFileSync(this.request,'utf8');
+        const ast = babylon.parse(originalSource);
+        let dependencies = [];
+        traverse(ast,{
+            CallExpression:(nodePath)=>{
+                if (nodePath.node.callee.name == 'require') {
+                    //获取当前节点
+                    let node = nodePath.node;
+                    //修改require为__webpack_require__
+                    node.callee.name = '__webpack_require__';
+                    //获取要加载的模块ID
+                    let moduleName = node.arguments[0].value;
+                    let extension = moduleName.split(path.posix.sep).pop().indexOf('.')==-1?'.js':'';
+                    //获取依赖模块的绝对路径
+                    let dependencyRequest = path.posix.join(path.posix.dirname(this.request),moduleName+extension);
+                    //获取依赖模块的模块ID
+                    let dependencyModuleId = './'+path.posix.relative(this.context,dependencyRequest);
+                    //把依赖对象添加到依赖列表里
+                    dependencies.push({name:this.name,context:this.context,request:dependencyRequest});
+                    //修改加载的模块ID名称
+                    node.arguments = [t.stringLiteral(dependencyModuleId)];
+                }
+            }
+        });
+        //生成新的代码
+        let {code} = generate(ast);
+        //获取模块的来源代码
+        this._source = code;
+        //获得语法树
+        this._ast = ast;
+        //获取模块ID
+        this.moduleId = './'+path.posix.relative(this.context,this.request);
+        //添加到模块数组里
+        compilation.modules.push(this);
+        //KEY为模块的绝对路径 值为模块转译后的代码
+        compilation._modules[this.request] = code;
+        //编译依赖项
+        compilation.buildDependencies(this,dependencies);
+        return this;
    }
}
module.exports = NormalModule;

6. seal封装chunk #

6.1 Compiler.js #

webpack\Compiler.js

this.hooks = {
      compilation: new SyncHook(["compilation", "params"]),
+      afterCompile:new SyncHook(["params"]),
      done: new AsyncSeriesHook(["stats"])
    };

compile(onCompiled){
    this.hooks.beforeCompile.callAsync({}, err => {
      this.hooks.compile.call();
      const compilation = this.newCompilation();
      this.hooks.make.callAsync(compilation, err => {
+         compilation.seal(err => {
+           this.hooks.afterCompile.callAsync(compilation, err => {
+              return onCompiled(null, compilation);
+           });
+         });
      });
    });
  }

6.2 Compilation.js #

webpack\Compilation.js

+ let Chunk = require('./Chunk');
class Compilation extends Tapable {
    constructor(compiler) {
        super();
        this.hooks = {
          addEntry: new SyncHook(["entry", "name"]),
+          seal: new SyncHook([]),
+                beforeChunks: new SyncHook([]),
+                afterChunks: new SyncHook(["chunks"])
        }
        this.entries=[];    //入口模块
        this._modules = {}; //模块代码
        this.modules=[];    //所有模块
+        this.chunks = [];   //代码块
    }
    //context ./src/index.js main callback(终级回调)
    addEntry(context, entry, name, finallyCallback) {
      this.hooks.addEntry.call(entry, name);//开始增加入口
      this._addModuleChain(context,entry,name);
      finallyCallback();
   }

+  seal(callback){
+    this.hooks.seal.call();
+    this.hooks.beforeChunks.call();//生成代码块之前
+    for (const module of this.entries) {//循环入口模块
+      const chunk = new Chunk(module);//创建代码块
+          this.chunks.push(chunk);//把代码块添加到代码块数组中
+      //把代码块的模块添加到代码块中
+      chunk.modules = this.modules.filter(module=>module.name == chunk.name);
+    }
+    this.hooks.afterChunks.call(this.chunks);//生成代码块之后
+    callback();//封装结束
+  }
}
module.exports = Compilation;

7. emit #

7.1 Compiler.js #

webpack\Compiler.js

const {Tapable,SyncHook,SyncBailHook,AsyncSeriesHook,AsyncParallelHook} = require("tapable");
const Compilation  = require('./Compilation');
+ const Stats = require('./Stats');
+ const mkdirp = require('mkdirp');
+ const path = require('path');
class Compiler extends Tapable {
  constructor(context) {
    super();
    this.hooks = {
      environment: new SyncHook([]),
      afterEnvironment: new SyncHook([]),
      afterPlugins: new SyncHook(["compiler"]),
      entryOption: new SyncBailHook(["context", "entry"]),
      beforeRun: new AsyncSeriesHook(["compiler"]),
      run: new AsyncSeriesHook(["compiler"]),
      beforeCompile: new AsyncSeriesHook(["params"]),
      compile: new SyncHook(["params"]),
      make: new AsyncParallelHook(["compilation"]),
      thisCompilation: new SyncHook(["compilation", "params"]),
      compilation: new SyncHook(["compilation", "params"]),
      afterCompile:new SyncHook(["params"]),
+       emit: new AsyncSeriesHook(["compilation"]),
      done: new AsyncSeriesHook(["stats"])
    };
    this.options = {};
    this.context = context; //设置上下文路径
  }
+   emitAssets(compilation,callback){
+     const emitFiles = err => {
+       let assets = compilation.assets;
+       for(let file in assets){
+         let source = assets[file];
+         const targetPath = path.posix.join(this.options.output.path,file);
+         let content = source;
+         this.outputFileSystem.writeFileSync(targetPath, content);
+       }
+       callback();
+     }
    this.hooks.emit.callAsync(compilation, err => {
            mkdirp(this.options.output.path, emitFiles);
        });
  }
  run(finalCallback) {
    //编译完成后的回调
    const onCompiled = (err, compilation) => {
+       this.emitAssets(compilation, err => {
+         const stats = new Stats(compilation);
+         this.hooks.done.callAsync(stats, err => {
+            return finalCallback(null, stats);
+         });
+       })
    };
    //准备运行编译
    this.hooks.beforeRun.callAsync(this, err => {
      //运行
      this.hooks.run.callAsync(this, err => {
        //开始编译,编译完成后执行conCompiled回调
        this.compile(onCompiled);
      });
    });
  }
  newCompilation(params){
    const compilation = new Compilation(this);
    this.hooks.thisCompilation.call(compilation, params);
      this.hooks.compilation.call(compilation, params);
    return compilation;
  }
  compile(onCompiled){
    this.hooks.beforeCompile.callAsync({}, err => {
      this.hooks.compile.call();
      const compilation = this.newCompilation();
      this.hooks.make.callAsync(compilation, err => {
          compilation.seal(err => {
            this.hooks.afterCompile.callAsync(compilation, err => {
              return onCompiled(null, compilation);
            });
          });
      });
    });
  }
}
module.exports = Compiler;

7.2 Compilation.js #

webpack\Compilation.js

const normalModuleFactory = require('./NormalModuleFactory');
const {Tapable,SyncHook,SyncBailHook,AsyncSeriesHook,AsyncParallelHook} = require("tapable");
const path = require('path');
+ const Chunk = require('./Chunk');
+ const fs = require('fs');
+ const ejs = require('ejs');
+ const mainTemplate = fs.readFileSync(path.join(__dirname, 'main.ejs'), 'utf8');
+ const mainRender = ejs.compile(mainTemplate);
class Compilation extends Tapable {
    constructor(compiler) {
        super();
        this.compiler = compiler;
        this.options = compiler.options;
        this.context = compiler.context;
        this.inputFileSystem = compiler.inputFileSystem;
            this.outputFileSystem = compiler.outputFileSystem;
        this.hooks = {
          addEntry: new SyncHook(["entry", "name"]),
          seal: new SyncHook([]),
                beforeChunks: new SyncHook([]),
                afterChunks: new SyncHook(["chunks"])
        }
        this.entries=[];    //入口模块
        this._modules = {}; //模块代码
        this.modules=[];    //所有模块
        this.chunks = [];   //代码块
+       this.files=[];
+       this.assets = {};   //资源 
    }
    //context ./src/index.js main callback(终级回调)
    addEntry(context, entry, name, finallyCallback) {
      this.hooks.addEntry.call(entry, name);//开始增加入口
      this._addModuleChain(context,entry,name);
      finallyCallback();
   }
   //增加模块链
   _addModuleChain(context,entry,name){
     let module = normalModuleFactory.create(
         {name,  //模块所属的代码块的名称
         context:this.context,//上下文
         request:path.posix.join(context,entry)});//模块完整路径
     module.build(this);//开始编译模块
     this.entries.push(module);//把编译好的模块添加到入口列表里面
   }
   //编译依赖的模块
   buildDependencies(module,dependencies){
    module.dependencies = dependencies.map(data =>{//映射老模块到新的模块
       let module = normalModuleFactory.create(data);//创建新的模块
       return module.build(this);//编译模块并返回自己
    });
  }
  seal(callback){
    this.hooks.seal.call();
    this.hooks.beforeChunks.call();//生成代码块之前
    for (const module of this.entries) {//循环入口模块
      const chunk = new Chunk(module);//创建代码块
          this.chunks.push(chunk);//把代码块添加到代码块数组中
      //把代码块的模块添加到代码块中
      chunk.modules = this.modules.filter(module=>module.name == chunk.name);
    }
    this.hooks.afterChunks.call(this.chunks);//生成代码块之后
+     this.createChunkAssets();
    callback();//封装结束
  }
+   createChunkAssets(){
+     for (let i = 0; i < this.chunks.length; i++) {
+       const chunk = this.chunks[i];
+       chunk.files = [];
+             const file = chunk.name+'.js';
+       const source = mainRender({ entryId:chunk.entryModule.moduleId, modules:chunk.modules});
+       chunk.files.push(file);
+       this.emitAsset(file, source);
+     }
+   }
+   emitAsset(file, source){
+      this.assets[file] = source;
+      this.files.push(file);
+   }
}
module.exports = Compilation;

7.3 main.ejs #

webpack\main.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 id in modules){
              let {moduleId,_source} = modules[id];%>
              "<%-moduleId%>":
              (function (module, exports,__webpack_require__) {
                <%-_source%>
              }),
           <%}
        %>
    });

7.4 Stats.js #

webpack\Stats.js

class Stats{
    constructor(compilation){
        this.files = compilation.files;
        this.modules = compilation.modules;
        this.chunks = compilation.chunks;
    }
}
module.exports = Stats;

8. 支持loader #

8.1 NormalModule.js #

webpack\NormalModule.js

const fs = require('fs');
const ejs = require('ejs');
const path = require('path');
const babylon = require('babylon');
const t = require('babel-types');
const generate = require('babel-generator').default;
const traverse = require('babel-traverse').default;
class NormalModule{
    constructor({name,context,request}){
      this.name = name;
      this.context = context;
      this.request = request;  
      this.dependencies = [];
      this.moduleId;
      this._ast;
      this._source;
    }
+    getSource(request,compilation){
+        let source = compilation.inputFileSystem.readFileSync(this.request,'utf8');
+        let { module: { rules } } = compilation.options;
+        for (let i = 0; i < rules.length; i++) {
+            let rule = rules[i];
+            if (rule.test.test(request)) {
+                let loaders = rule.use;
+                let loaderIndex = loaders.length - 1;
+                let iterateLoaders = ()=>{
+                    let loaderName = loaders[loaderIndex];
+                    let loader = require(path.resolve(this.context, 'loaders', loaderName));
+                    source = loader(source);
+                    if (loaderIndex > 0) {
+                        loaderIndex--;
+                        iterateLoaders();
+                    }
+                }
+                iterateLoaders();
+                break;
+            }
+        }
+        return source;
+    }
    build(compilation){
+        let originalSource = this.getSource(this.request,compilation);
        const ast = babylon.parse(originalSource);
        let dependencies = [];
        traverse(ast,{
            CallExpression:(nodePath)=>{
                if (nodePath.node.callee.name == 'require') {
                    //获取当前节点
                    let node = nodePath.node;
                    //修改require为__webpack_require__
                    node.callee.name = '__webpack_require__';
                    //获取要加载的模块ID
                    let moduleName = node.arguments[0].value;
                    let extension = moduleName.split(path.posix.sep).pop().indexOf('.')==-1?'.js':'';
                    //获取依赖模块的绝对路径
                    let dependencyRequest = path.posix.join(path.posix.dirname(this.request),moduleName+extension);
                    //获取依赖模块的模块ID
                    let dependencyModuleId = './'+path.posix.relative(this.context,dependencyRequest);
                    //把依赖对象添加到依赖列表里
                    dependencies.push({name:this.name,context:this.context,request:dependencyRequest});
                    //修改加载的模块ID名称
                    node.arguments = [t.stringLiteral(dependencyModuleId)];
                }
            }
        });
        //生成新的代码
        let {code} = generate(ast);
        //获取模块的来源代码
        this._source = code;
        //获得语法树
        this._ast = ast;
        //获取模块ID
        this.moduleId = './'+path.posix.relative(this.context,this.request);
        //添加到模块数组里
        compilation.modules.push(this);
        //KEY为模块的绝对路径 值为模块转译后的代码
        compilation._modules[this.request] = code;
        //编译依赖项
        compilation.buildDependencies(this,dependencies);
        return this;
    }
}
module.exports = NormalModule;

8.2 index.js #

src\index.js

+require('./index.less');
let title = require('./title');
console.log(title);

8.3 src\index.less #

index.less

@color:red;
body{
    background-color:@color;
}

8.4 less-loader.js #

loaders\less-loader.js

var less = require('less');
module.exports = function (source) {
    let css;
    less.render(source, (err, output) => {
        css = output.css;
    });
    return css;
}

8.5 style-loader.js #

loaders\style-loader.js

module.exports = function (source) {
    let str = `
      let style = document.createElement('style');
      style.innerHTML = ${JSON.stringify(source)};
      document.head.appendChild(style);
    `;
    return str;
}

访问验证

请输入访问令牌

Token不正确,请重新输入