
Merhaba! Hoş geldiniz, hoş geldiniz, sizi burada görmek çok güzel! Bugün gerçekten basit bir JavaScript modül paketleyicisi oluşturacağız.
Başlamadan önce birkaç teşekkür etmek istiyorum. Bu makale ağırlıklı olarak aşağıdaki kaynaklardan yararlanmaktadır:
- JavaScript modül paketleyicisinin paketini kaldırma - Luciano Mammino
- Mini paket - Ronen Amiel
Tamam, bir modül paketleyicinin gerçekte ne olduğu ile başlayalım.
Modül Paketleyici Nedir?
Modül paketleyici, JavaScript parçalarını ve bağımlılıklarını alıp bunları genellikle tarayıcıda kullanılmak üzere tek bir dosyada paketleyen bir araçtır. Browserify, Webpack, Rollup veya diğerleri gibi araçlar kullanmış olabilirsiniz.
Genellikle bir giriş dosyasıyla başlar ve oradan o giriş dosyası için gereken tüm kodu bir araya getirir.

Bir paketleyicinin iki ana aşaması vardır:
- Bağımlılık çözümü
- Paketleme
Bir giriş noktasından ( app.js
yukarıdaki gibi) başlayarak , bağımlılık çözümlemesinin amacı, kodunuzun tüm bağımlılıklarını (çalışması gereken diğer kod parçalarını) aramak ve bir grafik (bağımlılık grafiği olarak adlandırılır) oluşturmaktır.
Bu yapıldıktan sonra, bağımlılık grafiğinizi uygulamanın kullanabileceği tek bir dosyaya paketleyebilir veya dönüştürebilirsiniz.
Kodumuza bazı ithalatlarla başlayalım (nedenini daha sonra açıklayacağım).
Bağımlılık Çözümü
Yapmamız gereken ilk şey, bağımlılık çözümleme aşamasında bir modülü nasıl temsil etmek istediğimizi düşünmektir.
Modül Gösterimi
Dört şeye ihtiyacımız olacak:
- Dosyanın adı ve tanımlayıcısı
- Dosyanın nereden geldiği (dosya sisteminde)
- Dosyadaki kod
- Dosyanın ihtiyacı olan bağımlılıklar
Grafik yapısı, her dosyadaki bağımlılıkları özyinelemeli olarak kontrol ederek oluşturulur.
JavaScript'te, böyle bir veri kümesini temsil etmenin en kolay yolu bir nesne olacaktır.
createModuleObject
Yukarıdaki işleve bakıldığında , dikkate değer kısım, çağrılan bir işlev çağrısıdır detective
.
Dedektif bir kütüphanedir “ derin iç içe nasıl) (olursa olsun gerektirecek tüm aramaları bulmak ” ve biz kendi AST geçişi yapıyor önleyebilirsiniz yolu deneyerek!
Dikkat edilmesi gereken bir şey (ve bu neredeyse tüm modül paketleyicilerinde aynıdır) şudur:
const libName = 'lodash'const lib = require(libName)
Onu bulamayacaktır (çünkü bu, kodu çalıştırmak anlamına gelir).
Peki bu işlevi bir modülün yolundan çalıştırmak ne verir?

Sıradaki ne? Bağımlılık çözümü.
Tamam, henüz değil. İlk olarak, modül haritası denen bir şeyden bahsetmek istiyorum.
Modül Haritası
Düğümdeki modülleri içe aktardığınızda, göreli içe aktarmalar yapabilirsiniz require('./utils')
. Öyleyse kodunuz bunu çağırdığında, paketleyici ./utils
, her şey paketlendiğinde doğru dosyanın ne olduğunu nasıl bilir ?
Modül haritasının çözdüğü sorun budur.
Modül nesnemiz, id
'gerçeğin kaynağımız' olacak benzersiz bir anahtara sahiptir. Bu nedenle, bağımlılık çözümümüzü yaparken, her modül için, gerekli olanların isimlerinin yanı sıra kimliklerinin bir listesini tutacağız. Bu şekilde, çalışma zamanında doğru modülü alabiliriz.
Bu aynı zamanda tüm modülleri iç içe olmayan bir nesnede, id'yi anahtar olarak kullanarak saklayabileceğimiz anlamına gelir.

Bağımlılık Çözümü
Tamam, yani getModules
işlevde makul bir miktar devam ediyor . Ana amacı, kök / giriş modülünden başlamak ve bağımlılıkları özyinelemeli olarak aramak ve çözmektir.
'Bağımlılıkları çöz' ile ne demek istiyorum? Node'da the adında bir şey vardır require.resolve
ve bu, Node'un ihtiyaç duyduğunuz dosyanın nerede olduğunu nasıl çözdüğünü gösterir. Bunun nedeni, nispeten veya bir node_modules
klasörden içe aktarabilmemizdir .
Şansımıza, bizim için resolve
bu algoritmayı uygulayan adında bir npm modülü var . Sadece bağımlılık ve temel URL argümanlarını iletmemiz gerekiyor ve bu bizim için tüm zor işi yapacak.
Projedeki her modülün her bağımlılığı için bu çözümü gerçekleştirmemiz gerekiyor.
Daha map
önce bahsettiğim adlı modül haritasını da oluşturuyoruz .
Fonksiyonun sonunda, modules
projemizdeki her modül / bağımlılık için modül nesnelerini içerecek isimli bir dizi kalıyor .
Artık buna sahip olduğumuza göre, son adıma geçebiliriz: paketleme!
Paketleme
Tarayıcıda, modül (tür) diye bir şey yoktur. Ancak bu, gerekli işlevi olmadığı ve hayır olduğu anlamına gelir module.exports
. Dolayısıyla, tüm bağımlılıklarımıza sahip olsak da, şu anda onları modül olarak kullanmanın bir yolu yok.
Modül Fabrika İşlevi
Fabrika işlevine girin.
Bir fabrika işlevi, bir nesne döndüren bir işlevdir (bu bir yapıcı değildir). Nesne yönelimli programlamanın bir modelidir ve kullanımlarından biri kapsülleme ve bağımlılık enjeksiyonu yapmaktır.
Kulağa hoş gelmek?
Bir fabrika işlevini kullanarak, hem kendi require
işlevimizi hem de module.exports
paket kodumuzda kullanılabilecek nesneyi enjekte edebilir ve modüle kendi kapsamını verebiliriz.
Paketleme
Aşağıda, paketleme için kullanılan paketleme işlevi verilmiştir.
Bunların çoğu sadece JavaScript şablon hazır değerleridir, bu yüzden ne yaptığını tartışalım.
İlk önce modulesSource
. Burada, modüllerin her birini gözden geçirip onları bir dizi kaynağa dönüştürüyoruz.
Öyleyse, bir modül nesnesi için çıktı nasıldır?

Şimdi okumak biraz zor, ancak kaynağın kapsüllendiğini görebilirsiniz. Daha önce de bahsettiğim gibi fabrika işlevini sağlıyor modules
ve require
kullanıyoruz.
Bağımlılık çözümü sırasında oluşturduğumuz modül haritasını da dahil ediyoruz.
Fonksiyonun ardından, tüm bağımlılıkların büyük bir nesnesini oluşturmak için bunların hepsini birleştiriyoruz.
Bir sonraki kod dizisi bir IIFE'dir, yani bu kodu tarayıcıda (veya başka herhangi bir yerde) çalıştırdığınızda, işlev hemen çalışacaktır. IIFE, kapsamı kapsüllemek için başka bir modeldir ve burada kullanılır, bu nedenle global kapsamı bizim require
ve modüllerimizle kirletmeyiz .
Gördüğünüz gibi, iki gerekli işlevi tanımlıyoruz require
ve localRequire
.
Require, bir modül nesnesinin kimliğini kabul eder, ancak elbette kaynak kodu kimlikler kullanılarak yazılmaz. Bunun yerine, localRequire
modüllerin gerektirdiği herhangi bir argümanı almak ve bunları doğru kimliğe dönüştürmek için diğer işlevi kullanıyoruz . Bu, bu modül haritalarını kullanıyor.
Bundan sonra module object
, modülün doldurabileceği bir tanımlıyoruz ve her iki işlevi de fabrikaya geçiriyoruz, ardından geri dönüyoruz module.exports
.
Son olarak, require(0)
girdi dosyamız olan 0 kimliğine sahip modülü gerektirmeye çağırıyoruz .
Ve bu kadar! Modül paketleyicimiz% 100 tamamlandı!
Tebrikler! ?
Şimdi çalışan bir modül toplayıcımız var.
Bu muhtemelen üretimde kullanılmamalıdır, çünkü birçok özelliği eksiktir (döngüsel bağımlılıkları yönetmek, her dosyanın yalnızca bir kez ayrıştırıldığından emin olmak, es-modülleri vb.), Ancak bu size nasıl olacağına dair iyi bir fikir vermiştir. modül paketleyicileri aslında çalışır.
Aslında, kaynak kodun tamamını kaldırırsanız, bu yaklaşık 60 satırda çalışır.
Okuduğunuz için teşekkürler ve umarım basit modül paketleyicimizin işleyişine bir göz atmışsınızdır. Yaptıysanız, alkışladığınızdan emin olun? ve Paylaş.
Bu makale ilk olarak blogumda yayınlandı.//Github.com/adamisntdead/wbpck-bundler kaynağına göz atın