Rumah hujung hadapan web tutorial js 深入讲解webpack模块的基本原理

深入讲解webpack模块的基本原理

May 31, 2018 pm 01:58 PM
web webpack asas

这篇文章主要介绍了浅谈webpack组织模块的原理,现在分享给大家,也给大家做个参考。

现在前端用Webpack打包JS和其它文件已经是主流了,加上Node的流行,使得前端的工程方式和后端越来越像。所有的东西都模块化,最后统一编译。Webpack因为版本的不断更新以及各种各样纷繁复杂的配置选项,在使用中出现一些迷之错误常常让人无所适从。所以了解一下Webpack究竟是怎么组织编译模块的,生成的代码到底是怎么执行的,还是很有好处的,否则它就永远是个黑箱。当然了我是前端小白,最近也是刚开始研究Webpack的原理,在这里做一点记录。

编译模块

编译两个字听起来就很黑科技,加上生成的代码往往是一大坨不知所云的东西,所以常常会让人却步,但其实里面的核心原理并没有什么难。所谓的Webpack的编译,其实只是Webpack在分析了你的源代码后,对其作出一定的修改,然后把所有源代码统一组织在一个文件里而已。最后生成一个大的bundle JS文件,被浏览器或者其它Javascript引擎执行并返回结果。

在这里用一个简单的案例来说明Webpack打包模块的原理。例如我们有一个模块mA.js

var aa = 1;

function getDate() {
 return new Date();
}

module.exports = {
 aa: aa,
 getDate: getDate
}
Salin selepas log masuk

我随便定义了一个变量aa和一个函数getDate,然后export出来,这里是用CommonJS的写法。

然后再定义一个app.js,作为main文件,仍然是CommonJS风格:

var mA = require('./mA.js');

console.log('mA.aa =' + mA.aa);
mA.getDate();
Salin selepas log masuk

现在我们有了两个模块,使用Webpack来打包,入口文件是app.js,依赖于模块mA.js,Webpack要做几件事情:

  1. 从入口模块app.js开始,分析所有模块的依赖关系,把所有用到的模块都读取进来。

  2. 每一个模块的源代码都会被组织在一个立即执行的函数里。

  3. 改写模块代码中和require和export相关的语法,以及它们对应的引用变量。

  4. 在最后生成的bundle文件里建立一套模块管理系统,能够在runtime动态加载用到的模块。

我们可以看一下上面这个例子,Webpack打包出来的结果。最后的bundle文件总的来说是一个大的立即执行的函数,组织层次比较复杂,大量的命名也比较晦涩,所以我在这里做了一定改写和修饰,把它整理得尽量简单易懂。

首先是把所有用到的模块都罗列出来,以它们的文件名(一般是完整路径)为ID,建立一张表:

var modules = {
 './mA.js': generated_mA,
 './app.js': generated_app
}
Salin selepas log masuk
Salin selepas log masuk

关键是上面的generated_xxx是什么?它是一个函数,它把每个模块的源代码包裹在里面,使之成为一个局部的作用域,从而不会暴露内部的变量,实际上就把每个模块都变成一个执行函数。它的定义一般是这样:

function generated_module(module, exports, webpack_require) {
  // 模块的具体代码。
  // ...
}
Salin selepas log masuk

在这里模块的具体代码是指生成代码,Webpack称之为generated code。例如mA,经过改写得到这样的结果:

function generated_mA(module, exports, webpack_require) {
 var aa = 1;
 
 function getDate() {
  return new Date();
 }

 module.exports = {
  aa: aa,
  getDate: getDate
 }
}
Salin selepas log masuk

乍一看似乎和源代码一模一样。的确,mA没有require或者import其它模块,export用的也是传统的CommonJS风格,所以生成代码没有任何改动。不过值得注意的是最后的module.exports = ...,这里的module就是外面传进来的参数module,这实际上是在告诉我们,运行这个函数,模块mA的源代码就会被执行,并且最后需要export的内容就会被保存到外部,到这里就标志着mA加载完成,而那个外部的东西实际上就后面要说的模块管理系统。

接下来看app.js的生成代码:

function generated_app(module, exports, webpack_require) {
 var mA_imported_module = webpack_require('./mA.js');
 
 console.log('mA.aa =' + mA_imported_module['aa']);
 mA_imported_module['getDate']();
}
Salin selepas log masuk

可以看到,app.js的源代码中关于引入的模块mA的部分做了修改,因为无论是require/exports,或是ES6风格的import/export,都无法被JavaScript解释器直接执行,它需要依赖模块管理系统,把这些抽象的关键词具体化。也就是说,webpack_require就是require的具体实现,它能够动态地载入模块mA,并且将结果返回给app。

到这里你脑海里可能已经初步逐渐构建出了一个模块管理系统的想法,我们来看一下webpack_require的实现:

// 加载完毕的所有模块。
var installedModules = {};

function webpack_require(moduleId) {
 // 如果模块已经加载过了,直接从Cache中读取。
 if (installedModules[moduleId]) {
  return installedModules[moduleId].exports;
 }

 // 创建新模块并添加到installedModules。
 var module = installedModules[moduleId] = {
  id: moduleId,
  exports: {}
 };
 
 // 加载模块,即运行模块的生成代码,
 modules[moduleId].call(
  module.exports, module, module.exports, webpack_require);
 
 return module.exports;
}
Salin selepas log masuk

注意倒数第二句里的modules就是我们之前定义过的所有模块的generated code:

var modules = {
 './mA.js': generated_mA,
 './app.js': generated_app
}
Salin selepas log masuk
Salin selepas log masuk

webpack_require的逻辑写得很清楚,首先检查模块是否已经加载,如果是则直接从Cache中返回模块的exports结果。如果是全新的模块,那么就建立相应的数据结构module,并且运行这个模块的generated code,这个函数传入的正是我们建立的module对象,以及它的exports域,这实际上就是CommonJS里exports和module的由来。当运行完这个函数,模块就被加载完成了,需要export的结果保存到了module对象中。

所以我们看到所谓的模块管理系统,原理其实非常简单,只要耐心将它们抽丝剥茧理清楚了,根本没有什么深奥的东西,就是由这三个部分组成:

// 所有模块的生成代码
var modules;
// 所有已经加载的模块,作为缓存表
var installedModules;
// 加载模块的函数
function webpack_require(moduleId);
Salin selepas log masuk

当然以上一切代码,在整个编译后的bundle文件中,都被包在一个大的立即执行的匿名函数中,最后返回的就是这么一句话:

return webpack_require(‘./app.js');
Salin selepas log masuk

即加载入口模块app.js,后面所有的依赖都会动态地、递归地在runtime加载。当然Webpack真正生成的代码略有不同,它在结构上大致是这样:

(function(modules) {
 var installedModules = {};
 
 function webpack_require(moduleId) {
   // ...
 }

 return webpack_require('./app.js');
}) ({
 './mA.js': generated_mA,
 './app.js': generated_app
});
Salin selepas log masuk

可以看到它是直接把modules作为立即执行函数的参数传进去的而不是另外定义的,当然这和上面的写法没什么本质不同,我做这样的改写是为了解释起来更清楚。

ES6的import和export

以上的例子里都是用传统的CommonJS的写法,现在更通用的ES6风格是用import和export关键词,在使用上也略有一些不同。不过对于Webpack或者其它模块管理系统而言,这些新特性应该只被视为语法糖,它们本质上还是和require/exports一样的,例如export:

export aa
// 等价于:
module.exports['aa'] = aa

export default bb
// 等价于:
module.exports['default'] = bb
Salin selepas log masuk

而对于import:

import {aa} from './mA.js'
// 等价于
var aa = require('./mA.js')['aa']
Salin selepas log masuk

比较特殊的是这样的:

import m from './m.js'
Salin selepas log masuk

情况会稍微复杂一点,它需要载入模块m的default export,而模块m可能并非是由ES6的export来写的,也可能根本没有export default,所以Webpack在为模块生成generated code的时候,会判断它是不是ES6风格的export,例如我们定义模块mB.js:

let x = 3;

let printX = () => {
 console.log('x = ' + x);
}

export {printX}
export default x
Salin selepas log masuk

它使用了ES6的export,那么Webpack在mB的generated code就会加上一句话:

function generated_mB(module, exports, webpack_require) {
 Object.defineProperty(module.exports, '__esModule', {value: true});
 // mB的具体代码
 // ....
}
Salin selepas log masuk

也就是说,它给mB的export标注了一个__esModule,说明它是ES6风格的export。这样在其它模块中,当一个依赖模块以类似import m from './m.js'这样的方式加载时,会首先判断得到的是不是一个ES6 export出来的模块。如果是,则返回它的default,如果不是,则返回整个export对象。例如上面的mA是传统CommonJS的,mB是ES6风格的:

// mA is CommonJS module
import mA from './mA.js'
console.log(mA);

// mB is ES6 module
import mB from './mB.js'
console.log(mB);
Salin selepas log masuk

我们定义get_export_default函数:

function get_export_default(module) {
 return module && module.__esModule? module['default'] : module;
}
Salin selepas log masuk

这样generated code运行后在mA和mB上会得到不同的结果:

var mA_imported_module = webpack_require('./mA.js');
// 打印完整的 mA_imported_module
console.log(get_export_default(mA_imported_module));

var mB_imported_module = webpack_require('./mB.js');
// 打印 mB_imported_module['default']
console.log(get_export_default(mB_imported_module));
Salin selepas log masuk

这就是在ES6的import上,Webpack需要做一些特殊处理的地方。不过总体而言,ES6的import/export在本质上和CommonJS没有区别,而且Webpack最后生成的generated code也还是基于CommonJS的module/exports这一套机制来实现模块的加载的。

模块管理系统

以上就是Webpack如何打包组织模块,实现runtime模块加载的解读,其实它的原理并不难,核心的思想就是建立模块的管理系统,而这样的做法也是具有普遍性的,如果你读过Node.js的Module部分的源代码,就会发现其实用的是类似的方法。这里有一篇文章可以参考。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

vue iview组件表格 render函数的使用方法详解

微信小程序实现换肤功能

nodejs实现解析xml字符串为对象的方法示例

Atas ialah kandungan terperinci 深入讲解webpack模块的基本原理. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Tutorial Bermula VUE3: Membungkus dan Membina dengan Webpack Tutorial Bermula VUE3: Membungkus dan Membina dengan Webpack Jun 15, 2023 pm 06:17 PM

Vue ialah rangka kerja JavaScript yang sangat baik yang boleh membantu kami membina aplikasi web yang interaktif dan cekap dengan cepat. Vue3 ialah versi terkini Vue, yang memperkenalkan banyak ciri dan fungsi baharu. Webpack kini merupakan salah satu pembungkus modul JavaScript dan alat binaan yang paling popular, yang boleh membantu kami mengurus pelbagai sumber dalam projek kami. Artikel ini akan memperkenalkan cara menggunakan Webpack untuk membungkus dan membina aplikasi Vue3. 1. Pasang Webpack

Menggunakan Jetty7 untuk pemprosesan pelayan Web dalam pembangunan API Java Menggunakan Jetty7 untuk pemprosesan pelayan Web dalam pembangunan API Java Jun 18, 2023 am 10:42 AM

Menggunakan Jetty7 untuk Pemprosesan Pelayan Web dalam Pembangunan JavaAPI Dengan pembangunan Internet, pelayan Web telah menjadi bahagian teras pembangunan aplikasi dan juga menjadi tumpuan banyak perusahaan. Untuk memenuhi keperluan perniagaan yang semakin meningkat, ramai pembangun memilih untuk menggunakan Jeti untuk pembangunan pelayan web, dan fleksibiliti dan skalabilitinya diiktiraf secara meluas. Artikel ini akan memperkenalkan cara menggunakan Jetty7 dalam pembangunan JavaAPI untuk We

Perlindungan masa nyata terhadap rentetan penyekat muka di web (berdasarkan pembelajaran mesin) Perlindungan masa nyata terhadap rentetan penyekat muka di web (berdasarkan pembelajaran mesin) Jun 10, 2023 pm 01:03 PM

Benteng penghalang muka bermakna sebilangan besar benteng terapung tanpa menyekat orang dalam video, menjadikannya kelihatan seperti terapung dari belakang orang itu. Pembelajaran mesin telah popular selama beberapa tahun, tetapi ramai orang tidak tahu bahawa keupayaan ini juga boleh dijalankan dalam penyemak imbas Artikel ini memperkenalkan proses pengoptimuman praktikal dalam rentetan video penyelesaian ini, dengan harapan dapat membukanya beberapa idea. mediapipeDemo (https://google.github.io/mediapipe/) menunjukkan prinsip pelaksanaan rentetan penyekat muka arus perdana atas permintaan sehingga pengiraan latar belakang pelayan video untuk mengekstrak kawasan potret dalam skrin video dan menukarnya kepada storan svg klien semasa memainkan video Muat turun svg dari pelayan dan gabungkannya dengan rentetan, potret

Bagaimana untuk mengkonfigurasi nginx untuk memastikan bahawa pelayan frps dan web berkongsi port 80 Bagaimana untuk mengkonfigurasi nginx untuk memastikan bahawa pelayan frps dan web berkongsi port 80 Jun 03, 2023 am 08:19 AM

Pertama sekali, anda akan ragu-ragu, apakah itu frp? Ringkasnya, frp ialah alat penembusan intranet Selepas mengkonfigurasi klien, anda boleh mengakses intranet melalui pelayan. Sekarang pelayan saya telah menggunakan nginx sebagai laman web, dan hanya terdapat satu port 80. Jadi apakah yang perlu saya lakukan jika pelayan FRP juga mahu menggunakan port 80? Selepas membuat pertanyaan, ini boleh dicapai dengan menggunakan proksi terbalik nginx. Untuk menambah: frps ialah pelayan, frpc ialah pelanggan. Langkah 1: Ubah suai fail konfigurasi nginx.conf dalam pelayan dan tambahkan parameter berikut pada http{} dalam nginx.conf, server{listen80

Bagaimana untuk melaksanakan pengesahan borang untuk aplikasi web menggunakan Golang Bagaimana untuk melaksanakan pengesahan borang untuk aplikasi web menggunakan Golang Jun 24, 2023 am 09:08 AM

Pengesahan borang adalah pautan yang sangat penting dalam pembangunan aplikasi web Ia boleh menyemak kesahihan data sebelum menyerahkan data borang untuk mengelakkan kelemahan keselamatan dan ralat data dalam aplikasi. Pengesahan borang untuk aplikasi web boleh dilaksanakan dengan mudah menggunakan Golang Artikel ini akan memperkenalkan cara menggunakan Golang untuk melaksanakan pengesahan borang untuk aplikasi web. 1. Elemen asas pengesahan borang Sebelum memperkenalkan cara melaksanakan pengesahan borang, kita perlu mengetahui apakah elemen asas pengesahan borang. Unsur bentuk: unsur bentuk ialah

Bagaimana untuk mendayakan akses pentadbiran daripada UI web kokpit Bagaimana untuk mendayakan akses pentadbiran daripada UI web kokpit Mar 20, 2024 pm 06:56 PM

Kokpit ialah antara muka grafik berasaskan web untuk pelayan Linux. Ia bertujuan terutamanya untuk memudahkan pengurusan pelayan Linux untuk pengguna baharu/pengguna pakar. Dalam artikel ini, kami akan membincangkan mod akses Cockpit dan cara menukar akses pentadbiran kepada Cockpit daripada CockpitWebUI. Topik Kandungan: Mod Kemasukan Kokpit Mencari Mod Akses Kokpit Semasa Dayakan Capaian Pentadbiran untuk Kokpit daripada CockpitWebUI Melumpuhkan Capaian Pentadbiran untuk Kokpit daripada CockpitWebUI Kesimpulan Mod Kemasukan Kokpit Kokpit mempunyai dua mod capaian: Capaian Terhad: Ini adalah lalai untuk mod capaian kokpit. Dalam mod akses ini anda tidak boleh mengakses pengguna web dari kokpit

Apakah standard web? Apakah standard web? Oct 18, 2023 pm 05:24 PM

Piawaian web ialah satu set spesifikasi dan garis panduan yang dibangunkan oleh W3C dan organisasi lain yang berkaitan Ia termasuk penyeragaman HTML, CSS, JavaScript, DOM, kebolehcapaian Web dan pengoptimuman prestasi Dengan mengikut piawaian ini, keserasian halaman boleh dipertingkatkan. kebolehcapaian, kebolehselenggaraan dan prestasi. Matlamat standard web adalah untuk membolehkan kandungan web dipaparkan dan berinteraksi secara konsisten pada platform, pelayar dan peranti yang berbeza, memberikan pengalaman pengguna yang lebih baik dan kecekapan pembangunan.

apakah maksud web apakah maksud web Jan 09, 2024 pm 04:50 PM

Web ialah rangkaian kawasan luas global, juga dikenali sebagai World Wide Web, yang merupakan bentuk aplikasi Internet. Web ialah sistem maklumat berdasarkan hiperteks dan hipermedia, yang membolehkan pengguna melompat antara halaman web yang berbeza melalui hiperpautan untuk menyemak imbas dan mendapatkan maklumat. Asas Web ialah Internet, yang menggunakan protokol dan bahasa yang bersatu dan piawai untuk membolehkan pertukaran data dan perkongsian maklumat antara komputer yang berbeza.

See all articles