你知道webpack是如何分析模块各个依赖关系的?是如何将ES6代码编译成浏览器可执行代码的吗?node
mkdir bundler
cd bundler
复制代码
// word.js
export const word="word";
// message.js
import {word} from "./word.js";
const message=`hello ${word}`;
export default message;
// index.js
import message from "./message.js";
console.log(message);
复制代码
若是想直接在浏览器中运行index.js的话,固然是不能的,浏览器没法识别es6的语法,之前咱们都是经过相似webpack的打包工具将es6代码转换成es5的代码,而后直接在浏览器中运行。webpack
在项目根目录下新建一个bundler文件,实现打包过程。其实所谓的webpack编译打包就是经过一些特定的方法函数将源代码转换成浏览器可识别的代码es6
const moduleAnalyser=(filename)=>{
}
moduleAnalyser("./src/index.js");// 入口函数
复制代码
const fs=require("fs");
const moduleAnalyser=(filename)=>{
const content=fs.readFileSync(filename,"utf-8");// 读取文件内容
console.log(content);
}
moduleAnalyser("./src/index.js");// 入口函数
复制代码
在终端中执行node命令web
node bundler.js
复制代码
就会输出index.js的文件内容 npm
(1)执行npm init -y初始化数组
(2)安装一个babel模块浏览器
npm install @babel/parser --save
复制代码
(3)使用parserbash
const fs=require("fs");
const parser=require("@babel/parser");
const moduleAnalyser=(filename)=>{
const content=fs.readFileSync(filename,"utf-8");// 读取文件内容
console.log(parser.parse(content,{
sourceType:"module"
}));
}
moduleAnalyser("./src/index.js");// 入口函数
复制代码
再次执行node命令,node bundler.js,查看文件内容,输出的就是常说的AST,描述了文件的相关依赖关系。 babel
修改一下bundler函数
...
const ast=parser.parse(content,{
sourceType:"module"
})
console.log(ast.program.body);
...
复制代码
执行node bundler.js命令就会获得以下输出内容
npm install @babel/traverse --save
复制代码
(5)使用traverse
...
traverse(ast,{
ImportDeclaration({node}){
console.log(node)// 查看node内容
}
})
...
复制代码
继续改写bundler.js
const dependencies=[];
traverse(ast,{
ImportDeclaration({node}){
dependencies.push(node.source.value);
}
})
console.log(dependencies)// 获得依赖数组
复制代码
继续改写bundler.js
const dependencies={};// 变成对象,key是依赖路径,value是相对依赖路径。便于以后使用
traverse(ast,{
ImportDeclaration({node}){
const dirname=path.dirname(filename);//filename对应的文件夹路径
const newFile="./"+path.join(dirname,node.source.value);
dependencies[node.source.value]=newFile;
}
})
复制代码
(6)安装babel/core转换代码
npm install @babel/core @babel/preset-env --save
复制代码
(7)转换代码
const { code }=babel.transformFromAst(ast,null,{
presets:["@babel/preset-env"]
})//转换ast
console.log(code);
复制代码
执行node bundler.js命令就会获得以下输出内容
入口文件的依赖分析就完成了。完整代码以下:
const fs=require("fs");
const path=require("path");
const babel=require("@babel/core");
const parser=require("@babel/parser");
const traverse=require("@babel/traverse").default;// 默认es module导出
const moduleAnalyser=(filename)=>{
const content=fs.readFileSync(filename,"utf-8");// 读取文件内容
const ast=parser.parse(content,{
sourceType:"module"
})
const dependencies={};
traverse(ast,{
ImportDeclaration({node}){
const dirname=path.dirname(filename);//filename对应的文件夹路径
const newFile="./"+path.join(dirname,node.source.value);
dependencies[node.source.value]=newFile;
}
})
const { code } = babel.transformFromAst(ast,null,{
presets:["@babel/preset-env"]
})//转换ast
return {
filename,
dependencies,
code
}
}
const moduleInfo=moduleAnalyser("./src/index.js");// 入口函数
console.log(moduleInfo);
复制代码
一个项目不可能只有一个文件,这就须要咱们分析整个项目的依赖关系,即生成获得依赖图谱。
const makeDependenciesGraph=(entry)=>{
const entryModule=moduleAnalyser(entry);
console.log(entryModule);
}
复制代码
const makeDependenciesGraph=(entry)=>{
const entryModule=moduleAnalyser(entry);
const graphArray=[entryModule];
for(let i=0;i<graphArray.length;i++){
const item=graphArray[i];
const { dependencies } = item; // 解构出依赖
if(dependencies){
for(let j in dependencies){
// 递归分析依赖,放入依赖图谱数组
graphArray.push(moduleAnalyser(dependencies[j]))
}
}
}
console.log(graphArray);
}
复制代码
执行node bundler.js命令就会获得以下输出内容
const makeDependenciesGraph=(entry)=>{
const entryModule=moduleAnalyser(entry);
const graphArray=[entryModule];
for(let i=0;i<graphArray.length;i++){
const item=graphArray[i];
const { dependencies } = item; // 解构出依赖
if(dependencies){
for(let j in dependencies){
// 递归分析依赖,放入依赖图谱数组
graphArray.push(moduleAnalyser(dependencies[j]))
}
}
}
// 转换为对象 便于使用
const graph={}
graphArray.forEach(item=>{
graph[item.filename]={
dependencies:item.dependencies,
code:item.code
}
});
return graph;
}
复制代码
const generateCode=(entry)=>{
const graph=JSON.stringify(makeDependenciesGraph(entry));
return `
(function(graph){
function require(module){
function localRequire(relativePath){
return require(graph[module].dependencies[relativePath])
}
var exports={};
(function(require,exports,code){
eval(code)
})(localRequire,exports,graph[module].code);
return exports;
};
require('${entry}');
})(${graph})
`;
}
复制代码
执行node bundler.js命令就会获得以下输出内容
bundler文件完整代码:
const fs=require("fs");
const path=require("path");
const babel=require("@babel/core");
const parser=require("@babel/parser");
const traverse=require("@babel/traverse").default;// 默认es module导出
const moduleAnalyser=(filename)=>{
const content=fs.readFileSync(filename,"utf-8");// 读取文件内容
const ast=parser.parse(content,{
sourceType:"module"
})
const dependencies={};
traverse(ast,{
ImportDeclaration({node}){
const dirname=path.dirname(filename);//filename对应的文件夹路径
const newFile="./"+path.join(dirname,node.source.value);
dependencies[node.source.value]=newFile;
}
})
const { code }=babel.transformFromAst(ast,null,{
presets:["@babel/preset-env"]
})//转换ast
return {
filename,
dependencies,
code
}
}
const makeDependenciesGraph=(entry)=>{
const entryModule=moduleAnalyser(entry);
const graphArray=[entryModule];
for(let i=0;i<graphArray.length;i++){
const item=graphArray[i];
const { dependencies } = item; // 解构出依赖
if(dependencies){
for(let j in dependencies){
// 递归分析依赖,放入依赖图谱数组
graphArray.push(moduleAnalyser(dependencies[j]))
}
}
}
// 转换为对象 便于使用
const graph={}
graphArray.forEach(item=>{
graph[item.filename]={
dependencies:item.dependencies,
code:item.code
}
});
return graph;
}
const generateCode=(entry)=>{
const graph=JSON.stringify(makeDependenciesGraph(entry));
return `
(function(graph){
function require(module){
function localRequire(relativePath){
return require(graph[module].dependencies[relativePath])
}
var exports={};
(function(require,exports,code){
eval(code)
})(localRequire,exports,graph[module].code);
return exports;
};
require('${entry}');
})(${graph})
`;
}
const code=generateCode("./src/index.js");// 入口函数
console.log(code);
复制代码
以上就是一个webpack代码转换编译的整个过程。继续学习中!