Menggunakan Scala.js Dengan NPM Dan Browserify
Diterbitkan: 2022-03-11Jika Anda menggunakan Scala.js, kompiler bahasa Scala ke JavaScript, Anda mungkin menemukan manajemen ketergantungan standar Scala.js terlalu membatasi di dunia JavaScript modern. Scala.js mengelola dependensi dengan WebJars, sementara pengembang JavaScript mengelola dependensi menggunakan NPM. Karena dependensi yang dihasilkan oleh NPM adalah sisi server, biasanya diperlukan langkah tambahan menggunakan Browserify atau Webpack untuk menghasilkan kode browser.
Dalam posting ini, saya akan menjelaskan bagaimana mengintegrasikan Scala.js dengan kebanyakan modul JavaScript yang tersedia di NPM. Anda dapat memeriksa repositori GitHub ini untuk contoh kerja dari teknik yang dijelaskan di sini. Menggunakan contoh ini dan dengan membaca posting ini, Anda akan dapat mengumpulkan perpustakaan JavaScript Anda menggunakan NPM, membuat bundel menggunakan Browserify, dan menggunakan hasilnya dalam proyek Scala.js Anda sendiri. Semua ini bahkan tanpa menginstal Node.js, karena semuanya dikelola oleh SBT.
Mengelola Dependensi untuk Scala.js
Saat ini, menulis aplikasi dalam bahasa yang dikompilasi ke JavaScript menjadi praktik yang sangat umum. Semakin banyak orang yang pindah ke bahasa JavaScript yang diperluas, seperti CoffeeScript atau TypeScript, atau transpiler seperti Babel, yang memungkinkan Anda menggunakan ES6 hari ini. Pada saat yang sama, Google Web Toolkit (kompiler dari Java ke JavaScript) banyak digunakan untuk aplikasi perusahaan. Untuk alasan ini, sebagai pengembang Scala saya tidak menganggap lagi menggunakan Scala.js sebagai pilihan yang aneh. Kompilernya cepat, kode yang dihasilkan efisien, dan secara keseluruhan itu hanya cara untuk menggunakan bahasa yang sama baik di front-end maupun back-end.
Konon, menggunakan alat Scala di dunia JavaScript belum 100 persen alami. Terkadang Anda harus mengisi celah dari ekosistem JavaScript ke ekosistem Scala. Scala.js sebagai bahasa memiliki interoperabilitas yang sangat baik dengan JavaScript. Karena Scala.js adalah kompiler untuk bahasa Scala ke JavaScript, sangat mudah untuk menghubungkan kode Scala ke kode JavaScript yang ada. Yang terpenting, Scala.js memberi Anda kemampuan untuk membuat antarmuka yang diketik (atau fasad) untuk mengakses pustaka JavaScript yang tidak diketik (mirip dengan apa yang Anda lakukan dengan TypeScript). Untuk pengembang yang terbiasa dengan bahasa yang sangat diketik seperti Java, Scala atau bahkan Haskell, JavaScript terlalu longgar diketik. Jika Anda adalah pengembang seperti itu, mungkin alasan utamanya adalah karena Anda mungkin ingin menggunakan Scala.js adalah untuk mendapatkan bahasa yang diketik (sangat) di atas bahasa yang tidak diketik.
Masalah dalam rantai alat Scala.js standar, yang didasarkan pada SBT dan masih dibiarkan terbuka, adalah: Bagaimana cara menyertakan dependensi, seperti pustaka JavaScript tambahan, dalam proyek Anda? SBT menstandarisasi pada WebJars, jadi Anda seharusnya menggunakan WebJars untuk mengelola dependensi Anda. Sayangnya, menurut pengalaman saya, itu terbukti tidak memadai.
Masalah dengan WebJars
Seperti yang disebutkan, cara Scala.js standar untuk mengambil dependensi JavaScript didasarkan pada WebJars. Scala, bagaimanapun, adalah bahasa JVM. Scala.js menggunakan SBT untuk membangun program terutama, dan akhirnya SBT sangat baik dalam mengelola dependensi JAR.
Untuk alasan ini, format WebJar didefinisikan persis untuk mengimpor dependensi JavaScript di dunia JVM. WebJar adalah file JAR termasuk aset web, di mana file JAR sederhana hanya menyertakan kelas Java yang dikompilasi. Jadi, ide dari Scala.js adalah Anda harus mengimpor dependensi JavaScript Anda hanya dengan menambahkan dependensi WebJar, dengan cara yang sama Scala menambahkan dependensi JAR.
Ide Bagus, Kecuali Itu Tidak Berhasil
Masalah terbesar dengan Webjars adalah bahwa versi arbitrer pada perpustakaan JavaScript acak jarang tersedia sebagai WebJar. Pada saat yang sama, sebagian besar pustaka JavaScript tersedia sebagai modul NPM. Namun, seharusnya ada jembatan antara NPM dan WebJars dengan pembuat paket npm-to-webjar
. Jadi saya mencoba mengimpor perpustakaan, tersedia sebagai modul NPM. Dalam kasus saya, itu adalah VoxelJS, perpustakaan untuk membangun dunia seperti Minecraft di halaman web. Saya mencoba meminta perpustakaan sebagai WebJar, tetapi jembatan gagal hanya karena tidak ada bidang lisensi di deskriptor.
Anda mungkin juga menghadapi pengalaman yang membuat frustrasi ini karena alasan lain dengan perpustakaan lain. Sederhananya, sepertinya Anda tidak dapat mengakses setiap perpustakaan di alam liar sebagai WebJar. Persyaratan Anda harus menggunakan WebJars untuk mengakses perpustakaan JavaScript tampaknya terlalu membatasi.
Masukkan NPM dan Browserify
Seperti yang telah saya tunjukkan, format pengemasan standar untuk sebagian besar pustaka JavaScript adalah Node Package Manager, atau NPM, yang disertakan dalam versi Node.js apa pun. Dengan menggunakan NPM, Anda dapat dengan mudah mengakses hampir semua library JavaScript yang tersedia.
Perhatikan bahwa NPM adalah manajer paket Node . Node adalah implementasi sisi server dari mesin JavaScript V8, dan menginstal paket untuk digunakan di sisi server oleh Node.js. Seperti, NPM tidak berguna untuk browser. Namun, kegunaan NPM telah diperpanjang beberapa saat untuk bekerja dengan aplikasi browser, berkat alat Browserify di mana-mana, yang tentu saja juga didistribusikan sebagai paket NPM.
Browserify adalah, sederhananya, sebuah packager untuk browser. Ini akan mengumpulkan modul NPM yang menghasilkan "bundel" yang dapat digunakan dalam aplikasi browser. Banyak pengembang JavaScript bekerja dengan cara ini - mereka mengelola paket dengan NPM, dan kemudian Browserify mereka untuk digunakan dalam aplikasi web. Ingatlah bahwa ada alat lain yang bekerja dengan cara yang sama, seperti Webpack.
Mengisi Kesenjangan dari SBT ke NPM
Untuk alasan yang baru saja saya jelaskan, yang saya inginkan adalah cara untuk menginstal dependensi dari web dengan NPM, memanggil Browserify untuk mengumpulkan dependensi untuk browser, dan kemudian menggunakannya dengan Scala.js. Tugasnya ternyata sedikit lebih rumit dari yang saya harapkan, tetapi masih mungkin. Memang, saya melakukan pekerjaan itu dan saya menggambarkannya di sini.
Untuk kesederhanaan, saya memilih Browserify juga karena saya menemukan itu mungkin untuk menjalankannya dalam SBT. Saya belum mencoba dengan Webpack, meskipun saya rasa mungkin juga. Untungnya, saya tidak harus memulai dalam ruang hampa. Ada beberapa bagian yang sudah ada:
- SBT sudah mendukung NPM. Plugin
sbt-web
, yang dikembangkan untuk Play Framework, dapat menginstal dependensi NPM. - SBT mendukung eksekusi JavaScript. Anda dapat menjalankan alat Node tanpa menginstal Node itu sendiri, berkat plugin
sbt-jsengine
. - Scala.js dapat menggunakan bundel yang dihasilkan. Di Scala.js, ada fungsi penggabungan untuk disertakan dalam pustaka JavaScript arbitrer aplikasi Anda.
Dengan menggunakan fitur ini, saya membuat tugas SBT yang dapat mengunduh dependensi NPM dan kemudian memanggil Browserify, menghasilkan file bundle.js
. Saya mencoba mengintegrasikan prosedur dalam rantai kompilasi, dan saya dapat menjalankan semuanya secara otomatis, tetapi harus memproses bundling di setiap kompilasi terlalu lambat. Juga, Anda tidak mengubah dependensi sepanjang waktu; karenanya, masuk akal Anda harus membuat bundel secara manual sesekali ketika Anda mengubah dependensi.
Jadi, solusi saya adalah membangun subproyek. Subproyek ini mengunduh dan mengemas pustaka JavaScript dengan NPM dan Browserify. Kemudian, saya menambahkan perintah bundle
untuk melakukan pengumpulan dependensi. Bundel yang dihasilkan ditambahkan ke sumber daya untuk digunakan dalam aplikasi Scala.js.
Anda seharusnya menjalankan "bundel" ini secara manual setiap kali Anda mengubah dependensi JavaScript Anda. Seperti disebutkan, itu tidak otomatis dalam rantai kompilasi.
Cara Menggunakan Bundel
Jika Anda ingin menggunakan contoh saya, lakukan hal berikut: pertama, checkout repositori dengan perintah Git biasa.
git clone https://github.com/sciabarra/scalajs-browserify/
Kemudian, salin folder bundle
di proyek Scala.js Anda. Ini adalah subproyek untuk bundling. Untuk terhubung ke proyek utama, Anda harus menambahkan baris berikut di file build.sbt
Anda:
val bundle = project.in(file("bundle")) jsDependencies += ProvidedJS / "bundle.js" addCommandAlias("bundle", "bundle/bundle")
Juga, Anda harus menambahkan baris berikut ke file project/plugins.sbt
Anda:
addSbtPlugin("com.typesafe.sbt" % "sbt-web" % "1.1.1") addSbtPlugin("com.typesafe.sbt" % "sbt-js-engine" % "1.1.3")
Setelah selesai, Anda memiliki perintah baru, bundle
, yang dapat Anda gunakan untuk mengumpulkan dependensi Anda. Ini akan menghasilkan file bundle.js
di bawah folder src/main/resources
Anda.
Bagaimana Paket Termasuk dalam Aplikasi Scala.js Anda?
Perintah bundle
yang baru saja dijelaskan mengumpulkan dependensi dengan NPM dan kemudian membuat bundle.js
. Saat Anda menjalankan perintah fastOptJS
atau fullOptJS
, ScalaJS akan membuat myproject-jsdeps.js
, termasuk semua sumber daya yang Anda tentukan sebagai dependensi JavaScript, demikian juga bundle.js
Anda. Untuk menyertakan dependensi yang dibundel dalam aplikasi Anda, Anda seharusnya menggunakan penyertaan berikut:

<script src="target/scala-2.11/myproject-jsdeps.js"></script> <script src="target/scala-2.11/myproject-fastopt.js"></script> <script src="target/scala-2.11/myproject-launcher.js"></script>
Bundel Anda sekarang tersedia sebagai bagian dari myproject-jsdeps.js
. Bundel sudah siap, dan kami telah menyelesaikan tugas kami (mengimpor dependensi dan mengekspornya ke browser). Langkah selanjutnya adalah menggunakan pustaka JavaScript, yang merupakan masalah yang berbeda, masalah pengkodean Scala.js. Untuk kelengkapannya, sekarang kita akan membahas cara menggunakan bundle di Scala.js dan membuat fasad untuk menggunakan library yang kita impor.
Menggunakan Pustaka JavaScript Generik di Aplikasi Scala.js Anda
Untuk rekap, kita baru saja melihat cara menggunakan NPM dan Browserify untuk membuat bundel, dan menyertakan bundel itu di Scala.js. Tapi bagaimana kita bisa menggunakan perpustakaan JavaScript generik?
Proses lengkapnya, yang akan kami jelaskan secara rinci di sisa posting, adalah:
- Pilih perpustakaan Anda dari NPM dan sertakan dalam
bundle/package.json
. - Muat mereka dengan
require
dalam file modul library, dibundle/lib.js
. - Tulis fasad Scala.js untuk menginterpretasikan objek
Bundle
di Scala.js. - Terakhir, buat kode aplikasi Anda menggunakan pustaka yang baru diketik.
Menambahkan Ketergantungan
Menggunakan NPM, Anda harus menyertakan dependensi Anda dalam file package.json
, yang merupakan standar.
Jadi, anggaplah Anda ingin menggunakan dua perpustakaan terkenal seperti jQuery dan Loadash. Contoh ini hanya untuk tujuan demonstrasi, karena sudah ada pembungkus yang sangat baik untuk jQuery yang tersedia sebagai dependensi untuk Scala.js dengan modul yang tepat, dan Lodash tidak berguna di dunia Scala. Meskipun demikian, saya pikir itu adalah contoh yang baik, tetapi anggap itu hanya sebagai contoh.
Jadi, buka situs web npmjs.com
dan temukan perpustakaan yang ingin Anda gunakan dan pilih versi juga. Misalkan Anda memilih jquery-browserify
versi 13.0.0 dan lodash
versi 4.3.0. Kemudian, perbarui blok dependencies
dari packages.json
Anda sebagai berikut:
"dependencies": { "browserify": "13.0.0", "jquery-browserify": "1.8.1", "lodash": "4.3.0" }
Selalu simpan browserify
, karena diperlukan untuk menghasilkan bundel. Anda tidak diharuskan untuk memasukkannya ke dalam bundel.
Perhatikan bahwa jika Anda telah menginstal NPM, Anda cukup mengetik dari direktori bundle
:
npm install --save jquery-browserify lodash
Itu juga akan memperbarui package.json
. Jika Anda tidak menginstal NPM, jangan khawatir. SBT akan menginstal versi Java dari Node.js dan NPM, mengunduh JAR yang diperlukan dan menjalankannya. Semua ini dikelola ketika Anda menjalankan perintah bundle
dari SBT.
Mengekspor Perpustakaan
Sekarang kita tahu cara mengunduh paket. Langkah selanjutnya adalah menginstruksikan Browserify untuk mengumpulkannya dalam satu bundel dan membuatnya tersedia untuk aplikasi lainnya.
Browserify adalah pengumpul require
, meniru perilaku Node.js untuk browser, yang berarti Anda harus memiliki suatu tempat yang require
untuk mengimpor perpustakaan Anda. Karena kita perlu mengekspor perpustakaan tersebut ke Scala.js, bundel juga menghasilkan objek JavaScript tingkat atas bernama Bundle
. Jadi, yang perlu Anda lakukan adalah mengedit lib.js
, yang mengekspor objek JavaScript, dan memerlukan semua pustaka Anda sebagai bidang objek ini.
Jika kita ingin mengekspor ke perpustakaan Scala.js jQuery dan Lodash, dalam kode ini berarti:
module.exports = { "jquery": require("jquery-browserify"), "lodash": require("lodash") }
Sekarang, jalankan saja bundle
perintah dan perpustakaan akan diunduh, dikumpulkan dan ditempatkan di bundel, siap digunakan di aplikasi Scala.js Anda.
Mengakses Bundel
Sangat jauh:
- Anda menginstal subproyek bundel di proyek Scala.js Anda dan mengonfigurasinya dengan benar.
- Untuk perpustakaan apa pun yang Anda inginkan, Anda menambahkannya di
package.json
. - Anda membutuhkannya di
lib.js
. - Anda menjalankan perintah
bundle
.
Akibatnya, Anda sekarang memiliki objek JavaScript tingkat atas Bundle
, menyediakan semua titik masuk untuk perpustakaan, tersedia sebagai bidang objek ini.
Sekarang Anda siap menggunakannya dengan Scala.js. Dalam kasus paling sederhana Anda dapat melakukan sesuatu seperti ini untuk mengakses perpustakaan:
@js.native object Bundle extends js.Object { def jquery : js.Any = js.native def lodash: js.Any = js.native }
Kode ini memungkinkan Anda mengakses perpustakaan dari Scala.js. Namun, Anda tidak harus melakukannya dengan Scala.js, karena perpustakaannya masih belum diketik. Sebagai gantinya, Anda harus menulis "fasad" yang diketik, atau pembungkus, sehingga Anda dapat menggunakan pustaka JavaScript yang tidak diketik dengan cara scalish yang diketik.
Saya tidak dapat memberi tahu Anda di sini cara menulis fasad karena itu tergantung pada pustaka JavaScript tertentu yang ingin Anda bungkus di Scala.js. Saya hanya akan menunjukkan contoh, untuk melengkapi pembahasan. Silakan periksa dokumentasi resmi Scala.js untuk detail lebih lanjut. Juga, Anda dapat berkonsultasi dengan daftar fasad yang tersedia dan membaca kode sumber untuk inspirasi.
Sejauh ini, kami membahas proses untuk perpustakaan yang sewenang-wenang dan masih belum dipetakan. Di sisa artikel saya mengacu pada perpustakaan dengan fasad yang sudah tersedia, jadi diskusi hanya sebagai contoh.
Membungkus API JavaScript di Scala.js
Saat Anda menggunakan require
dalam JavaScript, Anda mendapatkan objek yang bisa berupa banyak hal berbeda. Ini bisa menjadi fungsi atau objek. Bahkan bisa berupa string atau boolean, dalam hal ini require
dipanggil hanya untuk efek samping.
Dalam contoh yang saya lakukan, jquery
adalah fungsi, mengembalikan objek yang menyediakan metode tambahan. Penggunaan umum adalah jquery(<selector>).<method>
. Ini adalah penyederhanaan, karena jquery
juga memungkinkan penggunaan $.<method>
, tetapi dalam contoh yang disederhanakan ini saya tidak akan membahas semua kasus tersebut. Perhatikan secara umum, untuk pustaka JavaScript yang kompleks tidak semua API dapat dengan mudah dipetakan ke tipe Scala statis. Anda mungkin perlu menggunakan js.Dynamic
yang menyediakan antarmuka dinamis (tidak diketik) ke objek JavaScript.
Jadi, untuk menangkap kasus penggunaan yang lebih umum, saya mendefinisikan di objek Bundle
, jquery
:
def jquery : js.Function1[js.Any, Jquery] = js.native
Fungsi ini akan mengembalikan objek jQuery. Sebuah instance dari suatu sifat dalam kasus saya didefinisikan dengan satu metode (penyederhanaan, Anda dapat menambahkan Anda sendiri):
@js.native trait Jquery extends js.Object { def text(arg: js.Any): Jquery = js.native }
Untuk perpustakaan Lodash, kami memodelkan seluruh perpustakaan sebagai objek JavaScript karena ini adalah kumpulan fungsi yang dapat Anda panggil secara langsung:
def lodash: Lodash = js.native
Di mana sifat lodash
adalah sebagai berikut (juga penyederhanaan, Anda dapat menambahkan metode Anda di sini):
@js.native trait Lodash extends js.Object { def camelCase(arg: js.Any): String = js.native }
Dengan menggunakan definisi tersebut, kita sekarang akhirnya dapat menulis kode Scala menggunakan pustaka jQuery dan Lodash yang mendasarinya, keduanya dimuat dari NPM dan kemudian dijelajahi :
object Main extends JSApp { def main(): Unit = { import Bundle._ jquery("#title").text(lodash.camelCase("This is a test")) } }
Anda dapat memeriksa contoh lengkap di sini.
Kesimpulan
Saya seorang pengembang Scala, dan saya sangat senang ketika menemukan Scala.js karena saya dapat menggunakan bahasa yang sama untuk server dan klien. Karena Scala lebih mirip dengan JavaScript daripada Java, Scala.js cukup mudah dan alami di browser. Selain itu, Anda juga dapat menggunakan fitur-fitur canggih dari Scala sebagai koleksi perpustakaan, makro, dan IDE serta alat pembangunan yang canggih. Keuntungan utama lainnya adalah Anda dapat berbagi kode antara server dan klien. Ada banyak kasus di mana fitur ini berguna. Jika Anda menggunakan transpiler untuk Javascript seperti Coffeescript, Babel atau TypeScript, Anda tidak akan melihat terlalu banyak perbedaan saat menggunakan Scala.js, tetapi masih ada banyak keuntungan. Rahasianya adalah mengambil yang terbaik dari setiap dunia dan memastikan mereka bekerja sama dengan baik.