webpack简单实现

webpack简单实现

1. 注册命令行指令(可以直接通过mypack指令打包)

  • 项目根目录创建mypack文件夹

  • npm initnpm初始化,并修改bin为主文件路径

{
  "name": "mypack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": {
    "mypack": "bin/mypack.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  • 第一行标识运行环境为node,然后使用npm link创建软链接,实现命令行直接调用运行
#! /usr/bin/env node   
console.log('mypack start');

2. webpack打包ejs模板

传入参数是所有引用的路径和对应执行函数,函数里面存了代码,通过eval执行

// 自执行函数,modules就是传入的模块及对应代码
(function (modules) {
  function myRequire(moduleName) {
    var module = {
      exports: {}
    };
    // 执行该文件对应的内容代码,里面若含有调用会继续使用myRequire函数
    modules[moduleName].call(module.exports,module, module.exports, myRequire);
    // 代码执行过则会将结果返回
    return module.exports;
  }
  // 从入口开始
  return myRequire("<%-entry%>");
})({
  // 入口模块
  "<%-entry%>": function (module, exports, myRequire) {
    // 将代码放到字符串中,eval执行
    eval(\`<%-script%>\`);
  }
  // ...其他模块
  <%for (let i = 0; i < modules.length; i++) {%> ,
    "<%-modules[i].name%>": function (module, exports, myRequire) {
      eval(\`<%-modules[i].content%>\`);
    }
  <%}%>
})

3. 读取文件转化为字符串

从入口文件开始读取文件,对文件中的字符串匹配require,将require涉及的文件进行读取,对每一个content进行replace校验,转化为调用自定义函数加载代码

// 设置模板所需数据
let entry = "./src/index.js";
let output = "./dist/main.js";
let script = fs.readFileSync(entry, 'utf8');// 读取到入口文件代码内容
let modules = [];// 存模块路径和对应函数

// 递归,将所有require替换,并且将所有依赖代码转化为字符串
function setContent(content) {
  return content.replace(/require\(['"](.+?)['"]\)/g, function () {
    // 拼接文件路径
    let filePath = arguments[1];
    let name = path.join('./src', filePath).replace(/\\/, '/');// src/1.js

    // 递归处理content
    content = setContent(fs.readFileSync(name, 'utf8'));
    modules.push({ name, content });

    // 调用函数
    return `myRequire('${name}')`;
  })
}
script = setContent(script);

4. loader使用

function styleLoader(cssText) {
  return `
    var style = document.createElement('style');
    style.innerText = ${JSON.stringify(cssText).replace(/\\r\\n/g, '')};
    document.head.appendChild(style);
  `
}

function setContent(content) {
  return content.replace(/require\(['"](.+?)['"]\)/g, function () {
    // 省略...
    // loader加载css文件
    if (/\.css$/.test(name)) {
      content = styleLoader(content);
    }

    modules.push({ name, content });

    // 调用函数
    return `myRequire('${name}')`;
  })
}

5. 文件导出

let template = `
// 自执行函数,modules就是传入的模块及对应代码
(function (modules) {
  function myRequire(moduleName) {
    var module = {
      exports: {}
    };
    // 执行该文件对应的内容代码,里面若含有调用会继续使用myRequire函数
    modules[moduleName].call(module.exports,module, module.exports, myRequire);
    // 代码执行过则会将结果返回
    return module.exports;
  }
  // 从入口开始
  return myRequire("<%-entry%>");
})({
  // 入口模块
  "<%-entry%>": function (module, exports, myRequire) {
    // 将代码放到字符串中,eval执行
    eval(\`<%-script%>\`);
  }
  // ...其他模块
  <%for (let i = 0; i < modules.length; i++) {%> ,
    "<%-modules[i].name%>": function (module, exports, myRequire) {
      eval(\`<%-modules[i].content%>\`);
    }
  <%}%>
})
`
let result = ejs.render(template, { entry, script, modules });
// 将结果代码写入
fs.writeFileSync(output, result);
console.log('success');

6. 总结

果然,学习一样技能最好的方式就是实现,原本高深莫策的webpack其实就是将代码转化为了路径和代码块调用函数的映射,在调用时使用eval来执行代码块,同事对传入的引用参数进行更改和使用,而loader就是一个处理函数,对文本进行处理,通过js插入到文档中,以上为我的一些初体会,如有错误欢迎指正,代码已上传至仓库,仓库:mypack

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值