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文件