前言
前端模块化编程思想是很重要的,优秀的模块化打包工具比如webpack、rollup、vite都可以用来进行模块编程。那么如何实现自己的打包工具呢?这里我们模仿webpack的底层打包思想来实现自己的打包工具。
准备
如何在全局下定义自己的打包命令以使用自己的打包工具?
我们需要准备一个文件夹(自定义名字),这里我以mypack命名,目录结构参考下图。
注意package.json里面的"bin"的属性值的’mypack’是命令(后面会使用npm link,然后就可以在全局下使用这个命令了)
mypack.js代码如下(第一行代码的意思是指定用node运行该js文件)
下面代码的template模板是从wepback打包出的bundle.js中提取出来的
#! /usr/bin/env node
//这个文件就要描述如何打包
let entry='./src/index.js';//入口文件
let output='./dist/main.js';//出口文件
let fs=require('fs');
let path=require('path');
let modules={}
let content=fs.readFileSync(entry,'utf8')
let styleLoader=function(source){//负责将结果进行更改 更改后继续走
//source代表的就是样式文件中的内容
return `
let style=document.createElement('style');
style.innerHTML=${JSON.stringify(source).replace(/\\r\\n/g,'')};
document.head.appendChild(style);
`
}
//JSON.stringfy(source)的结果是body{\r\n background:red \r\n}
//处理依赖关系
function replaceFn(name,content){
content=content.replace(/require\(['"](.+?)['"]\)/g,function(){
let name=path.join('./src',arguments[1]);//./src/a.js
let content2=fs.readFileSync(name,'utf8');
if(/\.css$/.test(name)){
content2=styleLoader(content2)
}
replaceFn(name,content2)
return `require('${name}')`
})
modules[name]=content
}
replaceFn(entry,content)
let ejs=require('ejs')
let template=`(function(modules) {
function require(moduleId) {
var module = {
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, require);
return module.exports;
}
return require("<%-entry%>");
})
({
<%Object.keys(modules).forEach((key)=>{%>
"<%-key%>":
(function(module, exports, require) {
eval(\`<%-modules[key]%>\`);
}),
<%})%>
})
`
let result=ejs.render(template,{
entry,
modules
});
//result为替换后的结果,最终要写到output中
fs.writeFileSync(output,result)
console.log('成功')
生成全局打包命令
好了,目录结构和自定义打包代码已经准备好了,我们现在就要把这个mypack文件夹放在本地npm文件夹。
我们使用npm link命令以完成这个操作
使用全局打包命令
好了,经过以上的步骤,我们现在就可以使用我们的全局打包命令了!
我们要确保现在处于项目根目录,因为我们的打包工具里面的代码入口文件是’./src/index.js’
我们现在使用mypack命令打包一下!
打包的结果在项目根目录的dist/main.js
main.js的代码如下
(function(modules) {
function require(moduleId) {
var module = {
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, require);
return module.exports;
}
return require("./src/index.js");
})
({
"src\b.js":
(function(module, exports, require) {
eval(`module.exports='bbbbb'
`);
}),
"src\a.js":
(function(module, exports, require) {
eval(`let result=require('src\b.js')
module.exports=result`);
}),
"src\index.css":
(function(module, exports, require) {
eval(`
let style=document.createElement('style');
style.innerHTML="body{ background-color:blue;}";
document.head.appendChild(style);
`);
}),
"./src/index.js":
(function(module, exports, require) {
eval(`let result=require('src\a.js')
let result2=require('src\b.js')
require('src\index.css')
console.log(result+result2)`);
}),
})
验证效果
我们在项目的根目录的dist/index.html中引入main.js