深刻理解 webpack 打包機制。node
依賴環境 node.jswebpack
// index.js
import { message } from './message.js'
console.log(message)
// message.js
import { word } from './word.js'
export const message = `say ${ word }`
// word.js
export const word = 'hello world !'
複製代碼
須要安裝以下 babelweb
const fs = require('fs');
const path = require('path')
const parser = require('@babel/parser'); // 幫助咱們分析字符串塊,即咱們經過 fs 來讀取文件時遇到的 import、export 語法
const traverse = require('@babel/traverse').default // traverse 採用的 ES Module 導出,咱們經過 requier 引入的話就加個 .default
const babel = require('@babel/core');
const moduleAnalyser = (filename) => {
const content = fs.readFileSync(filename, 'utf-8');
// parser 解析咱們的 content 以後會返回一個 AST (抽象語法樹)
const AST = parser.parse(content, {
sourceType: "module"
})
// 依賴收集
const dependencies = {};
// 使用 traverse 來遍歷 AST
traverse(AST, {
ImportDeclaration({ node }) { // 函數名是 AST 中包含的內容,參數是一些節點,node 表示這些節點下的子內容
const dirname = path.dirname(filename); // 咱們從抽象語法樹裏面拿到的路徑是相對路徑,而後咱們要處理它,在 bundler.js 中才能正確使用
const newDirname = './' + path.join(dirname, node.source.value).replace('\\', '/'); // 將dirname 和 獲取到的依賴聯合生成絕對路徑
dependencies[node.source.value] = newDirname; // 將源路徑和新路徑以 key-value 的形式存儲起來
}
})
// 將抽象語法樹轉換成瀏覽器能夠運行的代碼
const { code } = babel.transformFromAst(AST, null, {
presets: ['@babel/preset-env']
})
return {
filename,
dependencies,
code
}
}
// 建立依賴圖譜函數, 遞歸遍歷全部依賴模塊
const makeDependenciesGraph = (entry) => {
const entryModule = moduleAnalyser(entry)
const graghArray = [ entryModule ]; // 首先將咱們分析的入口文件結果放入圖譜數組中
for (let i = 0; i < graghArray.length; i ++) {
const item = graghArray[i];
const { dependencies } = item; // 拿到當前模塊所依賴的模塊
if (dependencies) {
for ( let j in dependencies ) { // 經過 for-in 遍歷對象
graghArray.push(moduleAnalyser(dependencies[j])); // 若是子模塊又依賴其它模塊,就分析子模塊的內容
}
}
}
const gragh = {}; // 將圖譜的數組形式轉換成對象形式
graghArray.forEach( item => {
gragh[item.filename] = {
dependencies: item.dependencies,
code: item.code
}
})
return gragh;
}
const graghInfo = makeDependenciesGraph('./src/index.js')
console.log(graghInfo)
複製代碼
下圖graghArray的數據結構數組
建立生成代碼的函數,傳入咱們的路口文件的路徑,整合以前的代碼 。瀏覽器
const generateCode = (entry) => {
// 注意:咱們的 gragh 是一個對象,key是咱們全部模塊的絕對路徑,須要經過 JSON.stringify 來轉換
const gragh = JSON.stringify(makeDependenciesGraph(entry));
// 咱們知道,webpack 是將咱們的全部模塊放在閉包裏面執行的,因此咱們寫一個自執行的函數
// 注意: 咱們生成的代碼裏面,都是使用的 require 和 exports 來引入導出模塊的,而咱們的瀏覽器是不認識的,因此須要構建這樣的函數
return `
(function( gragh ) {
function require( module ) {
// 相對路徑轉換成絕對路徑的方法
function localRequire(relativePath) {
return require(gragh[module].dependencies[relativePath])
}
const exports = {};
(function( require, exports, code ) {
eval(code)
})( localRequire, exports, gragh[module].code )
return exports;
}
require('${ entry }')
})(${ gragh })
`;
}
const code = generateCode('./src/index.js');
console.log(code)
複製代碼
將代碼整合以後,在命令行運行,會打印出一段代碼,將其複製到瀏覽器中運行。bash
大功告成 !babel