webpack构建原理(实现一个简易的webpack构建器)
- webpack的构建原理:
- 实现代码:
- 执行```node bundle.js```的结果
webpack的构建原理:
- webpack的构建原理所在的核心文件:./lib/webpack-structure.js
- webpack配置文件:./webpack.config.js
- webpack的执行文件:./bundle.js
- 源码所在的文件:./src/index.js
- 源码依赖的文件:./src/expo.js
- 工程结构:
文章图片
- webpack-structure.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 = class Webpack {
constructor(options) {
const {entry, output} = options;
this.entry = entry;
this.output = output;
// 构建结果存储
this.modules = []
}run() {
// 1、处理入口文件
const info = this.parse(this.entry)
this.modules.push(info);
///2、处理入口文件的相关依赖
for (let i = 0;
i < this.modules.length;
i++) {
const item = this.modules[i]
const {dependencies} = item
if (dependencies) {
for (let key in dependencies) {
this.modules.push(this.parse(dependencies[key]))
}
}
}///3、结果数组结构转换为对象
const obj = {}
this.modules.forEach(item => {
obj[item.entryFile] = {
dependencies: item.dependencies,
code: item.code
}
})// console.log("webpack构建后的文件输出:\n", obj);
///4、生成浏览器可执行的代码字符串
this.generateFile(obj)}
/**
* 解析入口文件以及相关依赖模块
* @param entryFile
* @return {{code: *, entryFile: *, dependencies: {}}}
*/
parse(entryFile) {// 拿到入口文件的内容
const content = fs.readFileSync(entryFile, "utf-8")// 把内容抽象成语法树,分析哪些是依赖
const ast = parser.parse(content, {
sourceType: "module"
})// 提取依赖模块的路径
const dependencies = {}
traverse(ast, {
ImportDeclaration({node}) {
const newPathName = `${path.dirname(entryFile)}/${node.source.value.split("/")[1]}`;
dependencies[node.source.value] = newPathName
}
})// 分析编译内容
const {code} = transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
})// 返回处理后的相关信息
return {
entryFile,
dependencies,
code
}
}
/**
* 文件编译输出
* @param code
*/
generateFile(code) {// 生成bundle.js=> ./dist/bundle.js
const filePath = path.join(this.output.path, this.output.filename)const newCode = JSON.stringify(code)
const bundle = `(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('${this.entry}')})(${newCode})`this.hasDir(this.output.path).then(msg => {fs.writeFileSync(filePath, bundle, "utf-8")}).catch(error => {console.log(error);
return this.createDir(this.output.path)}).then(msg => {console.log(msg)
fs.writeFileSync(filePath, bundle, "utf-8")
console.log(`webpack编译文件成功,内容输出到文件${filePath}`)}).catch(error => {console.log(error);
})
}
}
/**
* 判断输出目录是否存在,不存在则创建
* @param path
* @return {Promise}
*/
hasDir(path) {
return new Promise((resolve, reject) => {
fs.stat(path, (err, msg) => {
if (err) {
reject(`目录${path}不存在,系统开始自动创建目录...`)
} else {
resolve()
}
})
})
}/**
* 创建目录
* @param path
* @return {Promise}
*/
createDir(path) {
return new Promise((resolve, reject) => {
let err = fs.mkdirSync(path, {})
if (err) {
reject(`创建目录${path}失败...`)
} else {
resolve(`系统创建目录${path}成功,webpack开始编译文件...`)
}
})
}
- webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
}
};
- bundle.js
// 获取webpack的配置文件
const options = require("./webpack.config.js")// 引入webpack构建原理的核心文件
const Webpack = require("./lib/webpack-structure.js")new Webpack(options).run()
- index.js
import {add} from "./expo.js"add(1, 2)console.log("hello webpack");
- expo.js
export const add = function (a, b) {
return a + b
}
执行
node bundle.js
的结果
- 在dist目录下生成main.js文件
(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('./src/index.js')})({"./src/index.js":{"dependencies":{"./expo.js":"./src/expo.js"},"code":"\"use strict\";
\n\nvar _expo = require(\"./expo.js\");
\n\n(0, _expo.add)(1, 2);
\nconsole.log(\"hello webpack\");
"},"./src/expo.js":{"dependencies":{},"code":"\"use strict\";
\n\nObject.defineProperty(exports, \"__esModule\", {\nvalue: true\n});
\nexports.mini = exports.add = void 0;
\n\nvar add = function add(a, b) {\nreturn a + b;
\n};
\n\nexports.add = add;
\n\nvar mini = function mini(a, b) {\nreturn a - b;
\n};
\n\nexports.mini = mini;
"}})
- 将main.js文件中的代码拷贝到浏览器的控制台执行,成功输出“hello webpack”
文章图片
推荐阅读
- 自动化构建工具(二)(特性)
- 区块链|理解以太坊DApp及开发工具
- webpack 使用 (一) 原理的介绍
- webpack打包原理
- 动态引入(require, import)
- javascript|react使用笔记及生命周期
- webpack 构建 npm 包优化
- gulp|如何进行清除web端缓存
- 自动化构建工具篇|【解决方案】webpack `Invalid Host/Origin header`问题