webpack手写打包单文件

  • 步骤:

    • 首先需要安装webpack
    • 然后定制打包的配置文件
    • 然后通过npx webpack根据指定的配置文件中的内容去打包指定的文件

  • 首先创建工具模块

  • 在node_modulesnhwpack目录下初始化package.json文件,并创建bin目录,将需要通过指令执行的js文件放在bin目录下,修改package.json的配置,再通过npm link链接到全局,才能使用 npx nhwpack 或者 nhwpack 执行index.js中的内容

    • bin/index.js:

      #! /usr/bin/env node // 配置执行该文件的环境,在环境变量下查找node,用node执行该文件
      console.log("nhw");
    • package.json:

      {
        "name": "nhwpack",
        "version": "1.0.0",
        "description": "",
        "main": "bin/index.js",
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "bin": {
          "nhwpack": "bin/index.js"
        },
        "keywords": [],
        "author": "",
        "license": "ISC"
      }

工具模块目录


  • 以上完成了工具模块的封装,接下来就是封装打包单个文件

  • 创建需要打包的js和html模板:

![image-20210409142143082]()

  • 配置webpack.config.js:

    const path = require("path");
    
    module.exports = {
        devtool: 'cheap-module-eval-source-map',
        mode: "development",
        // 入口文件
        entry: "./src/index.js",
        // 打包后输出的文件夹及文件
        output: {
            filename: "./index.js",
            path: path.resolve(__dirname, "bundle")
        },
    };
  • 配置binindex.js:

    #! /usr/bin/env node
    // process.cwd()作用: 获取当前执行指令的工作路径
    const path = require("path");
    const configPath = path.resolve(process.cwd(), "webpack.config.js");
    const config = require(configPath);
    • 以上打印结果就是webpack.config.js文件的内容,这样就可以拿到入口文件
  • 那么就可以定义一个编译类去打包入口文件:

最终目录

class Complier {
    constructor(config) {
        this.config = config;
    }
    // 执行编译的方法
    run(){}
}

module.exports = Complier; // 将Complier暴露出去
  • 继续在binindex.js添加配置:

    #! /usr/bin/env node
    // process.cwd()作用: 获取当前执行指令的工作路径
    const path = require("path");
    const Complier = require("../lib/Complier.js");  // 引入Complier类
    
    const configPath = path.resolve(process.cwd(), "webpack.config.js");
    const config = require(configPath); // 拿到webpack.config.js文件的内容
    const cp = new Complier(config);// 创建Complier对象,传入webpack.config.js文件
    cp.run();                            // 执行编译的方法
  • 接下来编写run方法:

    const fs = require("fs");
    class Complier {
        constructor(config) {
            // 保存配置
            this.config = config;
            // 保存模块的依赖
            this.modules = {};
        }
        // 执行编译
        run() {
            // 构建模块
            this.buildModule();
        }
        // 构建模块
        buildModule() {
            // 传入入口文件,获取文件中的内容
            let code = this.getSource(this.config.entry);
            // 路径作为key,内容作为value
            this.modules[this.config.entry] = code;
        }
        // 读取文件,返回文件内容
        getSource(modulePath) {
            return fs.readFileSync(modulePath, "utf-8");
        }
    }
    
    module.exports = Complier;

  • 经发现,通过webpack打包后的js文件可以使用EJS模板替换部分内容。

    • EJS和前面我们学习的artTemplate一样,都是模板引擎
    • 不同的是artTemplate使用生成HTML,而EJS用于生成JS
  • 安装EJS:npm install ejs
  • 定义模板libmain.js:

    (function (modules) { // webpackBootstrap
        // 1.初始化一个缓存,用来存放加载模块,缓存有则在缓存中取,没有才去读取文件
        var installedModules = {};
     
        // 2.自实现require方法,moduleId是自己调用自己传入的模块路径
        function __webpack_require__(moduleId) {
     
          // 2.1判断缓存中有无当前需要的模块
          if (installedModules[moduleId]) {
            return installedModules[moduleId].exports;
          }
          // 2.2创建一个模块,将其放入缓存
          var module = installedModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
          };
     
          /* 通过call执行模块函数,modules是传入的对象,key是模块路径,value是需要执行的函数,修改函数this为空对象,将module和module.exports传递给函数*/
          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 = "<%-entryId%>");
      })({
        "<%-entryId%>": (function (module, exports) {
            <%-modules[entryId]%>
        })
      });
    • 仅替换以下内容:<%- 输出非转义的数据到模板

      return __webpack_require__(__webpack_require__.s = "<%-entryId%>");
      
      "<%-entryId%>": (function (module, exports) {
          <%-modules[entryId]%>
      })
  • 配置Complier.js文件:

    • 引入ejs和path模块
    const ejs = require("ejs");
    const path = require("path");
    
    run() {
        // 构建模块
        this.buildModule();
        // 发送ejs模板文件
        this.emitFile()
    }
    
    emitFile() {
        // 拿到ejs模板文件路径
        let templatePath = path.resolve(__dirname, "main.ejs");
        // 读取模板
        let template = fs.readFileSync(templatePath, "utf-8");
        // 根据模板替换内容
        let resultCode = ejs.render(template, {
            entryId: this.config.entry,
            modules: this.modules,
        });
        //拿到输出路径
        let outputDir = this.config.output.path;
        // 如果不存在输出路径,则创建
        if (!fs.existsSync(outputDir)) {
            fs.mkdirSync(outputDir);
        }
        // 获取文件
        let outputPath = path.resolve(outputDir, this.config.output.filename);
        // 将内容写入文件中
        fs.writeFileSync(outputPath, resultCode);
    }
  • npx nhwpack 打包js文件

最后修改:2021 年 04 月 11 日 02 : 10 AM
如果觉得我的文章对你有用,请随意赞赏!