你知道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命令就會獲得以下輸出內容
輸出的就是文件相關依賴,type爲ImportDeclaration表示是引入聲明,type爲ExpressionStatement表示是表達式。接下來要作的就是遍歷body的內容獲得依賴關係。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命令就會獲得以下輸出內容
將輸入內容拷貝後到瀏覽器console中執行,便會獲得代碼的正常輸出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代碼轉換編譯的整個過程。繼續學習中!