首先创建一台文件夹叫webpack
然后生成默认的package.json文件 npm init -y
在webpack文件夹中新建src文件,然后在src下创建index.js,hello.js,addFun.js文件
// index.js
import { sayHello } from './hello.js';
document.write(sayHello('hello'));
//webpack打包步骤
//1、从入口模块开始分析有哪些依赖,并且转换代码
//2、递归的分析其他依赖模块,分析有哪些依赖,并且转换代码
//3、生成可在浏览器端执行的bundle文件
——————————————————————————————————————
// hello.js
import { addFun } from './addFun.js'
export function sayHello(str){
return str + addFun(1, 2);
}
——————————————————————————————————————
// addFun.js
export function addFun(a, b){
return a + b;
}
在webpack文件夹中新建webpack.config.js,定义一下entry和output
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'mAIn.js',
path: path.resolve(__dirname, './dist')
}
}
在webpack文件夹中新建webpack.js 设置启动文件
// 启动webpack node webpack.js
const Complier = require('./lib/complier.js');
const options = require('./webpack.config.js');
new Complier(options).run();
下面开始写编译文件complier.js,在webpack中新建文件夹为lib并创建complier.js文件和parser.js文件。其中需要安装一些babel的插件,如@babel/parser将代码转换为抽象语法树(AST)的插件,如@babel/traverse处理抽象语法树的插件,如@babel/core转换抽象语法树的插件和@babel/preset-env转换代码的插件
// parser.js
const fs = require('fs');
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const path = require('path');
const { transformFromAst } = require('@babel/core');
module.exports = {
// 分析模块 获得AST
getAst: (filename) => {
let content = fs.readFileSync(filename,'utf-8');
return parser.parse(content,{
sourceType: "module"
})
},
// 获取依赖
getDep: (ast, filename) => {
const dep = {};// 保存相对路径和根路径两种信息
traverse(ast,{
//把符合ImportDeclaration的节点返回给我们
ImportDeclaration({node}){
//当前push进dep中的为相对路径
//dep.push(node.source.value)
const dirname = path.dirname(filename);
//拼接成绝对路径
const newPath = './' + path.join(dirname,node.source.value);
dep[node.source.value] = newPath;
}
});
return dep;
},
// 转换代码
getCode: (ast) => {
const { code } = transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
});
return code;
}
}
——————————————————————————————————————
// complier.js
const { getAst, getDep, getCode } = require('./parser.js');
const fs = require('fs');
const path = require('path');
module.exports = class Complier{
constructor(options){
this.entry = options.entry;
this.output = options.output;
this.modules = [];
}
run(){
const info = this.build(this.entry);
this.modules.push(info);
for(let i = 0;i<this.modules.length;i++){
const item = this.modules;
const { dep } = item;
if(dep){
for(let j in dep){
//递归处理依赖
this.modules.push(this.build(dep[j]));
}
}
}
//转换数据结构
const obj = {};
this.modules.forEach((item) => {
return obj[item.filename] = {
dep: item.dep,
code: item.code
}
})
//生成代码文件
this.file(obj);
}
build(filename){
let ast = getAst(filename);
let dep = getDep(ast, filename);
let code = getCode(ast);
return {
filename,
dep,
code
}
}
file(code){
// 获取输出信息 一些路径信息/dist/main.js
const filePath = path.join(this.output.path,this.output.filename);
const newCode = JSON.stringify(code);
// relative为newCode里面使用require时传入的依赖路径 const a = require(&#39;./a.js&#39;)
const bundle = `(function(graph){
function require(module){
function localRequire(relativePath){
return require(graph[module].dep[relativePath])
}
var exports = {};
(function(require,exports,code){
eval(code)
})(localRequire,exports,graph[module].code)
return exports;
}
require(&#39;${this.entry}&#39;);
})(${newCode})`;
fs.writeFileSync(filePath,bundle,&#39;utf-8&#39;)
}
} |