webpack构建原理(实现一个简易的webpack构建器)

webpack构建原理(实现一个简易的webpack构建器)

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<unknown>}
     */
    hasDir(path) {
        return new Promise((resolve, reject) => {
            fs.stat(path, (err, msg) => {
                if (err) {
                    reject(`目录${path}不存在,系统开始自动创建目录...`)
                } else {
                    resolve()
                }
            })
        })
    }

    /**
     * 创建目录
     * @param path
     * @return {Promise<unknown>}
     */
    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\", {\n  value: true\n});\nexports.mini = exports.add = void 0;\n\nvar add = function add(a, b) {\n  return a + b;\n};\n\nexports.add = add;\n\nvar mini = function mini(a, b) {\n  return a - b;\n};\n\nexports.mini = mini;"}})
  • 将main.js文件中的代码拷贝到浏览器的控制台执行,成功输出“hello webpack”
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明致成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值