webpack打包原理分析和实现(一)

2022-09-29 08:32:55 浏览数 (1)

  • webpack打包原理分析和实现(一)
  • webpack打包原理分析和实现(二)
  • webpack打包原理分析和实现(三)

首先,新建一个空文件夹,编辑器(webstrom)打开文件夹,执行npm init -y,生成package.json,在根目录新建webpack.config.js,加入如下代码(webpack 4.0的基础配置)

代码语言:javascript复制
const path=require('path');
module.exports={
    entry:'./src/index.js',
    mode:'development',
    output:{
        path:path.resolve(__dirname,'./dist'),
        filename:'main.js'
    }
};

新建src目录,添加index.js,exop.js

expo.js

代码语言:javascript复制
export const add=(a,b)=>{
    return a b
}
export const minus=function (a,b) {
    return a-b
}

index.js

代码语言:javascript复制
import {add,minus} from "./expo.js"
add(1,2)
console.log("hello webpack")

执行命令 npx webpack,看到生成的文件/dist/main.js:

代码语言:javascript复制
 (function(modules) { // webpackBootstrap
 	// The module cache
 	var installedModules = {};

 	// The require function
 	function __webpack_require__(moduleId) {

 		// Check if module is in cache
 		if(installedModules[moduleId]) {
 			return installedModules[moduleId].exports;
 		}
 		// Create a new module (and put it into the cache)
 		var module = installedModules[moduleId] = {
 			i: moduleId,
 			l: false,
 			exports: {}
 		};

 		// Execute the module function
 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

 		// Flag the module as loaded
 		module.l = true;

 		// Return the exports of the module
 		return module.exports;
 	}
	...

 	// Object.prototype.hasOwnProperty.call
 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

 	// __webpack_public_path__
 	__webpack_require__.p = "";


 	// Load entry module and return exports
 	return __webpack_require__(__webpack_require__.s = "./src/index.js");
 })
 ...

打包后生成的代码,可以直接在浏览器的控制台运行,大致的意思就是,webpack实现了一个webpack_require来实现自己的模块化,把代码都缓存在installedModules里,代码文件以对象传递进来,key是路径,value是包裹的代码字符串,并且代码内部的require,都被替换成了webpack_require处理依赖模块的路径

如何自己实现一个简单的webpack打包流程呢?

实现步骤

  • 基础配置,webpack会读取配置
    • 找到入口模块
  • 入口分析
    • 分析依赖模块(拿到模块的路径)
    • 分析内容(并对内容处理)
    • 编译内容
  • 依赖模块(递归找到依赖)
    • 分析依赖模块(并对内容处理)
    • 编译内容
  • 生成bundle.js,代码可以直接在浏览器中运行

自己实现一个bundle.js

  • 模块分析:读取入口文件,分析代码 新建文件./lib/webpack.js,其中利用了node的fs,读取文件内容,为了拿到文件中依赖,不推荐使用字符串截取,引入的模块名越多,就越麻烦,不灵活,推荐使用@babel/parser,这是babel7的工具,分析包括es6的内部的语法,返回一个ast抽象树 npm i @babel/parser --save
代码语言:javascript复制
const fs = require('fs')//node的核心模块fs
constructor(options) {
        console.log(options)
        const {entry, output} = options
        this.entry = entry
        this.output = output
        //存所有模块信息
        this.modules = []
    }
     run() {//入口函数
        const info = this.parse(this.entry)
        console.log(info)
        }
     parse(entryFile) {
        //! 分析入口模块的内容
        const content = fs.readFileSync(entryFile, 'utf-8')
        console.log(content)

        //!分析出哪些是依赖?以及依赖的路径
        const ast = parser.parse(content, {
            sourceType: 'module'
        })
        console.log(ast.program.body)
    }

新建build.js

代码语言:javascript复制
//! 拿到webpack的配置文件
const options =require("./webpack.config.js")
const webpack=require('./lib/webpack')
//类实例化
new webpack(options).run()

执行node build.js,打印ast body部分的内容

代码语言:javascript复制
[ Node {
    type: 'ImportDeclaration',
    start: 0,
    end: 35,
    loc: SourceLocation { start: [Position], end: [Position] },
    specifiers: [ [Node], [Node] ],
    source:
     Node {
       type: 'StringLiteral',
       start: 24,
       end: 35,
       loc: [SourceLocation],
       extra: [Object],
       value: './expo.js' },
    trailingComments: [ [Object] ] },
  Node {
    type: 'ExpressionStatement',
    start: 44,
    end: 52,
    loc: SourceLocation { start: [Position], end: [Position] },
    expression:
     Node {
       type: 'CallExpression',
       start: 44,
       end: 52,
       loc: [SourceLocation],
       callee: [Node],
       arguments: [Array] } },
  Node {
    type: 'ExpressionStatement',
    start: 54,
    end: 82,
    loc: SourceLocation { start: [Position], end: [Position] },
    expression:
     Node {
       type: 'CallExpression',
       start: 54,
       end: 82,
       loc: [SourceLocation],
       callee: [Node],
       arguments: [Array] },
    trailingComments: [ [Object] ] } ]

可以看到,index.js中的三行代码分别被解析成导入、表达式、表达式

代码语言:javascript复制
import {add,minus} from "./expo.js" //解析导入
add(1,2)解析成表达式
console.log("hello webpack")//解析成表达式

0 人点赞