webpack打包原理分析和實現(一)

首先,新建一個空文件夾,編輯器(webstrom)打開文件夾,執行npm init -y,生成package.json,在根目錄新建webpack.config.js,加入如下代碼(webpack 4.0的基礎配置)

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

export const add=(a,b)=>{
    return a+b
}
export const minus=function (a,b) {
    return a-b
}

index.js

import {add,minus} from "./expo.js"
add(1,2)
console.log("hello webpack")

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

 (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
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

//! 拿到webpack的配置文件
const options =require("./webpack.config.js")
const webpack=require('./lib/webpack')
//類實例化
new webpack(options).run()

執行node build.js,打印ast body部分的內容

[ 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中的三行代碼分別被解析成導入、表達式、表達式

import {add,minus} from "./expo.js" //解析導入
add(1,2)////解析成表達式
console.log("hello webpack")//解析成表達式

如果你熱愛編程,或者遇到了什麼問題,歡迎加入羣663077768,一起學習交流

發佈了57 篇原創文章 · 獲贊 47 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章