Pernahkah Anda berpikir tentang menggunakan set teknologi paling sederhana yang ada saat mengembangkan proyek web Anda berikutnya? Jika demikian, maka materi, terjemahan yang kami terbitkan hari ini, ditulis khusus untuk Anda.
Kerangka kerja JavaScript ada untuk membantu kami membangun aplikasi dengan kemampuan serupa menggunakan pendekatan generik. Namun, banyak aplikasi tidak membutuhkan semua kekuatan yang disediakan kerangka kerja. Menggunakan kerangka kerja dalam proyek kecil atau menengah, yang memiliki persyaratan spesifik tertentu, mungkin merupakan pemborosan waktu dan energi yang tidak perlu.

Pada artikel ini, kita akan berbicara tentang penggunaan teknologi modern dalam pengembangan aplikasi web yang kapabilitasnya tidak dibatasi oleh kapabilitas kerangka kerja. Omong-omong, jika Anda membutuhkannya, maka Anda, menggunakan teknologi yang dijelaskan di sini, dapat membuat kerangka kerja Anda yang sangat terspesialisasi. JavaScript murni dan teknologi web dasar lainnya memberi pengembang kemampuan untuk melakukan apa yang mereka butuhkan tanpa membatasi diri pada ruang lingkup alat yang mereka gunakan.
Ulasan
Sebelum kita memulai bisnis, mari kita bahas alat yang kita butuhkan.
β Arsitektur Aplikasi
Untuk memastikan kecepatan pemuatan aplikasi dan kegunaan yang tinggi, kami akan menggunakan pola desain berikut:
- Arsitektur Aplikasi Shell.
- Pola PRPL (Push, Render, Pre-cache, memuat Malas).
β Sistem pembangunan proyek
Dalam proyek kami, kami membutuhkan sistem perakitan berkualitas tinggi yang disesuaikan dengan kebutuhan kami. Di sini kita akan menggunakan Webpack, menyajikan persyaratan berikut untuk sistem membangun proyek:
- Dukungan untuk ES6 dan kemampuan impor sumber daya dinamis.
- Dukungan untuk SASS dan CSS.
- Konfigurasi mode pengembangan yang terpisah dan karya nyata aplikasi.
- Kemampuan untuk mengkonfigurasi pekerja layanan secara otomatis.
β Fitur JavaScript Canggih
Kami akan menggunakan set minimum fitur JavaScript modern yang memungkinkan kami untuk mengembangkan apa yang kami butuhkan. Berikut adalah fitur yang dimaksud:
- Modul
- Berbagai cara untuk membuat objek (objek literal, kelas).
- Impor sumber daya yang dinamis.
- Fungsi panah.
- Templat literal.
Sekarang kami memiliki gagasan umum tentang apa yang kami butuhkan, kami siap untuk mulai mengembangkan proyek kami.
Arsitektur aplikasi
Munculnya Aplikasi Web Progresif (PWA) telah berkontribusi pada kedatangan solusi arsitektur baru dalam pengembangan web. Ini memungkinkan aplikasi web memuat dan menampilkan lebih cepat. Kombinasi arsitektur App Shell dan pola PRPL dapat menyebabkan aplikasi web menjadi cepat dan responsif, mirip dengan aplikasi biasa.
β Apa itu App Shell dan PRPL?
App Shell adalah pola arsitektur yang digunakan untuk mengembangkan PWA, bila digunakan, jumlah minimum sumber daya kritis untuk operasi situs dikirim ke browser pengguna saat situs dimuat. Komposisi bahan-bahan ini biasanya mencakup semua sumber daya yang diperlukan untuk tampilan pertama aplikasi. Sumber daya semacam itu juga dapat di-cache menggunakan pekerja layanan.
Singkatan PRPL diuraikan sebagai berikut:
- Push - mengirim sumber daya penting ke klien untuk rute sumber (khususnya, menggunakan HTTP / 2).
- Render - menampilkan rute asli.
- Pra-cache - cache dari rute atau sumber daya yang tersisa sebelumnya.
- Lazy load - "lazy" memuat bagian-bagian aplikasi yang diperlukan (khususnya, atas permintaan pengguna).
β Implementasi App Shell dan PRPL dalam kode
Pola App Shepp dan PRPL dibagikan. Ini memungkinkan Anda untuk menerapkan pendekatan lanjutan dalam pengembangan proyek web. Begini pola aplikasi Shell dalam kode:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <style> html { box-sizing: border-box; } *, *:after, *:before { box-sizing: inherit; } body { margin: 0; padding: 0; font: 18px 'Oxygen', Helvetica; background: #ececec; } header { height: 60px; background: #512DA8; color: #fff; display: flex; align-items: center; padding: 0 40px; box-shadow: 1px 2px 6px 0px #777; } h1 { margin: 0; } .banner { text-decoration: none; color: #fff; cursor: pointer; } main { display: flex; justify-content: center; height: calc(100vh - 140px); padding: 20px 40px; overflow-y: auto; } button { background: #512DA8; border: 2px solid #512DA8; cursor: pointer; box-shadow: 1px 1px 3px 0px #777; color: #fff; padding: 10px 15px; border-radius: 20px; } .button { display: flex; justify-content: center; } button:hover { box-shadow: none; } footer { height: 40px; background: #2d3850; color: #fff; display: flex; align-items: center; padding: 40px; } </style> <title>Vanilla Todos PWA</title> </head> <body> <body> <header> <h3><font color="#3AC1EF">β<a class="banner"> Vanilla Todos PWA </a></font></h3> </header> <main id="app"></main> <footer> <span>Β© 2019 Anurag Majumdar - Vanilla Todos SPA</span> </footer> <script async src="<%= htmlWebpackPlugin.files.chunks.main.entry %>"></script> <noscript> This site uses JavaScript. Please enable JavaScript in your browser. </noscript> </body> </body> </html>
Setelah mempelajari kode ini, Anda dapat memahami bahwa template App Shell menyediakan untuk pembuatan "shell" aplikasi, yang merupakan "kerangka" yang berisi minimal markup. Mari kita analisis kode ini (selanjutnya, fragmen kode yang akan kita rujuk selama analisis ditandai dengan komentar, seperti
<!-- β1 -->
).
- Fragmen No. 1. Gaya paling penting dibangun ke dalam markup, dan tidak disajikan sebagai file terpisah. Ini dilakukan agar kode CSS akan diproses langsung saat memuat halaman HTML.
- Fragmen No. 2. Ini adalah "shell" dari aplikasi tersebut. Area-area ini nantinya akan dikendalikan oleh kode JavaScript. Ini terutama berlaku untuk apa yang akan ada di tag
main
dengan app
pengenal ( <main id="app"></main>
). - Fragmen No. 3. Di sini skrip ikut bermain. Atribut
async
memungkinkan Anda untuk tidak memblokir parser selama pemuatan skrip.
"Kerangka" aplikasi di atas mengimplementasikan langkah-langkah Push dan Render dari pola PRPL. Ini terjadi ketika browser mem-parsing kode HTML untuk membentuk representasi visual dari halaman. Pada saat yang sama, browser dengan cepat menemukan sumber daya yang penting untuk output halaman. Selain itu, skrip disajikan di sini (fragmen No. 3), yang bertanggung jawab untuk menampilkan rute asli dengan memanipulasi DOM (pada langkah Render).
Namun, jika kami tidak menggunakan pekerja layanan untuk menembolok "shell" dari aplikasi, maka kami tidak akan mendapatkan peningkatan kinerja, misalnya, saat memuat ulang halaman.
Kode di bawah ini menunjukkan pekerja layanan caching kerangka dan semua sumber daya statis aplikasi.
var staticAssetsCacheName = 'todo-assets-v3'; var dynamicCacheName = 'todo-dynamic-v3';
Mari kita menganalisis kode ini.
- Fragmen No. 1. Menangani acara
install
pekerja layanan membantu cache sumber daya statis. Di sini Anda dapat menyimpan sumber daya "kerangka" aplikasi (CSS, JavaScript, gambar, dan sebagainya) untuk rute pertama (sesuai dengan konten "kerangka"). Selain itu, Anda dapat mengunduh sumber daya aplikasi lain, membuatnya dapat berfungsi tanpa koneksi Internet. Caching sumber daya, selain caching kerangka, sesuai dengan langkah Pra-cache dari pola PRPL. - Fragmen No. 2. Memproses acara
activate
cache yang tidak digunakan. - Fragmen No. 3. Baris sumber kode ini memuat dari cache jika ada di sana. Jika tidak, permintaan jaringan dibuat. Selain itu, jika permintaan jaringan dibuat untuk menerima sumber daya, ini berarti bahwa sumber daya ini belum di-cache. Sumber daya semacam itu ditempatkan dalam cache terpisah yang baru. Script ini membantu cache data aplikasi dinamis.
Sampai saat ini, kami telah membahas sebagian besar solusi arsitektur yang akan digunakan dalam aplikasi kami. Satu-satunya hal yang belum kita bicarakan adalah langkah memuat Malas dari pola PRPL. Kami akan kembali lagi nanti, tetapi untuk saat ini kami akan berurusan dengan sistem perakitan proyek.
Sistem pembangunan proyek
Arsitektur yang baik saja, tanpa sistem pembangunan proyek yang layak, tidak cukup untuk membuat aplikasi yang berkualitas. Di sinilah Webpack sangat berguna. Ada alat lain untuk membangun proyek (bundler), misalnya - Parcel dan Rollup. Tetapi apa yang akan kami terapkan berdasarkan Webpack juga bisa dilakukan dengan menggunakan cara lain.
Di sini kita berbicara tentang bagaimana fitur yang kami minati terkait dengan plugin untuk Webpack. Ini akan memungkinkan Anda untuk dengan cepat memahami esensi sistem build kami. Pemilihan plug-in untuk bundler dan konfigurasi yang tepat adalah langkah paling penting menuju sistem pembangunan proyek berkualitas tinggi. Setelah menguasai prinsip-prinsip ini, Anda akan dapat menggunakannya di masa depan saat mengerjakan aplikasi Anda sendiri.
Tidak mudah untuk menyempurnakan alat seperti Webpack dari awal. Dalam kasus seperti itu, sangat membantu untuk mendapatkan bantuan yang baik. Panduan ini, dengan mana bagian yang sesuai dari bahan ini ditulis, adalah artikel
ini . Jika Anda memiliki masalah dengan Webpack - hubungi dia. Sekarang mari kita ingat dan terapkan persyaratan untuk sistem perakitan proyek yang telah kita bicarakan sejak awal.
βMendukung ES6 dan kemampuan impor sumber daya dinamis
Untuk mengimplementasikan fitur-fitur ini, kami membutuhkan Babel, transporter populer yang memungkinkan Anda untuk mengubah kode yang ditulis menggunakan fitur ES6 menjadi kode yang dapat dijalankan di lingkungan ES5. Untuk membuat Babel bekerja dengan Webpack, kita dapat menggunakan paket-paket berikut:
@babel/core
@babel/plugin-syntax-dynamic-import
@babel/preset-env
babel-core
babel-loader
babel-preset-env
Berikut ini contoh file
.babelrc
untuk digunakan dengan Webpack:
{ "presets": ["@babel/preset-env"], "plugins": ["@babel/plugin-syntax-dynamic-import"] }
Saat mengkonfigurasi Babel, baris
presets
dari file ini digunakan untuk mengkonfigurasi Babel untuk mengkompilasi ES6 ke ES5, dan baris
plugins
sehingga impor dinamis dapat digunakan di Webpack.
Ini adalah bagaimana Babel digunakan dengan Webpack (di sini adalah potongan dari file pengaturan Webpack -
webpack.config.js
):
module.exports = { entry: {
Bagian
rules
file ini menjelaskan cara menggunakan
babel-loader
untuk menyesuaikan proses transpilation. Sisa dari file ini dihilangkan demi singkatnya.
βSASS dan dukungan CSS
Untuk memberikan dukungan untuk sistem perakitan proyek SASS dan CSS kami, kami membutuhkan plugin berikut:
sass-loader
css-loader
style-loader
MiniCssExtractPlugin
Berikut tampilan file pengaturan Webpack di mana data tentang plugin ini dimasukkan:
module.exports = { entry: {
Loader terdaftar di bagian
rules
. Karena kami menggunakan plugin untuk mengekstrak gaya CSS, entri yang sesuai dimasukkan di bagian
plugins
.
β Pengaturan terpisah mode pengembangan dan kerja nyata aplikasi
Ini adalah bagian yang sangat penting dari proses pembuatan aplikasi. Semua orang tahu bahwa ketika membuat aplikasi, beberapa pengaturan digunakan untuk membangun versi yang digunakan selama pengembangan, sementara yang lain digunakan untuk versi produksinya. Berikut adalah daftar paket yang berguna di sini:
clean-webpack-plugin
: untuk membersihkan isi folder dist
.compression-webpack-plugin
: untuk mengompresi isi folder dist
.copy-webpack-plugin
: untuk menyalin sumber daya statis, misalnya, file, dari folder dengan data sumber aplikasi ke folder dist
.html-webpack-plugin
: untuk membuat file index.html
di folder dist
.webpack-md5-hash
: untuk hashing file aplikasi dalam folder dist
.webpack-dev-server
: untuk memulai server lokal yang digunakan selama pengembangan.
Beginilah
webpack.config.js
file
webpack.config.js
dihasilkan:
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const WebpackMd5Hash = require('webpack-md5-hash'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); module.exports = (env, argv) => ({ entry: { main: './src/main.js' }, devtool: argv.mode === 'production' ? false : 'source-map', output: { path: path.resolve(__dirname, 'dist'), chunkFilename: argv.mode === 'production' ? 'chunks/[name].[chunkhash].js' : 'chunks/[name].js', filename: argv.mode === 'production' ? '[name].[chunkhash].js' : '[name].js' }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, { test: /\.scss$/, use: [ 'style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ] } ] }, plugins: [ new CleanWebpackPlugin('dist', {}), new MiniCssExtractPlugin({ filename: argv.mode === 'production' ? '[name].[contenthash].css' : '[name].css' }), new HtmlWebpackPlugin({ inject: false, hash: true, template: './index.html', filename: 'index.html' }), new WebpackMd5Hash(), new CopyWebpackPlugin([
Seluruh konfigurasi Webpack disajikan sebagai fungsi yang mengambil dua argumen. Argumen
argv
digunakan di sini, yang mewakili argumen yang diteruskan ke fungsi ini ketika perintah
webpack
atau
webpack-dev-server
webpack
. Berikut ini uraian perintah-perintah ini dalam file proyek
package.json
:
"scripts": { "build": "webpack --mode production && node build-sw", "serve": "webpack-dev-server --mode=development --hot", },
Akibatnya, jika kita menjalankan perintah
npm run build
, versi produksi aplikasi akan dibuat. Jika Anda menjalankan perintah
npm run serve
, server pengembangan akan mulai, mendukung proses pengerjaan aplikasi.
Bagian
plugins
dan
devServer
file di atas menunjukkan cara mengkonfigurasi plugins dan server pengembangan.
Di bagian yang dimulai dengan
new CopyWebpackPlugin
, Anda menentukan sumber daya yang ingin Anda salin dari bahan sumber aplikasi.
Etting Mengatur pekerja layanan
Kita semua tahu bahwa kompilasi daftar file secara manual, misalnya, yang dimaksudkan untuk caching, adalah tugas yang agak membosankan. Oleh karena itu, di sini kita akan menggunakan skrip perakitan pekerja layanan khusus yang menemukan file dalam folder
dist
dan menambahkannya sebagai konten cache dalam templat pekerja layanan. Setelah itu, file pekerja layanan akan ditulis ke folder
dist
. Konsep-konsep yang kita bicarakan ketika melamar pekerja layanan tidak berubah. Berikut ini adalah kode skrip
build-sw.js
:
const glob = require('glob'); const fs = require('fs'); const dest = 'dist/sw.js'; const staticAssetsCacheName = 'todo-assets-v1'; const dynamicCacheName = 'todo-dynamic-v1';
Mari kita menganalisis kode ini.
- Fragmen No. 1. Di sini daftar file dari folder
dist
ditempatkan di array staticAssetsCacheFiles
. - Fragmen No. 2. Ini adalah templat pekerja layanan yang kita bicarakan. Saat membuat kode selesai, variabel digunakan. Ini membuat templat menjadi universal, memungkinkan Anda untuk menggunakannya di masa mendatang, selama pengembangan proyek. Kami juga memerlukan templat karena kami menambahkan informasi tentang isi folder
dist
ke dalamnya, yang dapat berubah seiring waktu. Untuk ini, stringFileCachesArray
konstan stringFileCachesArray
. - Fragmen No. 3. Di sini, kode pekerja layanan yang baru dibuat disimpan dalam konstanta
serviceWorkerScript
ditulis ke file yang terletak di dist/sw.js
Untuk menjalankan skrip ini, gunakan perintah
node build-sw
. Itu perlu dieksekusi setelah perintah
webpack --mode production
.
Script untuk membangun pekerja layanan yang disajikan di sini sangat menyederhanakan tugas mengatur file caching. Perlu dicatat bahwa skrip ini telah menemukan aplikasi dalam proyek nyata.
Jika Anda ingin menggunakan perpustakaan khusus yang dirancang untuk memecahkan masalah bekerja aplikasi web progresif offline, lihat
Workbox . Ini memiliki fitur yang dapat disesuaikan sangat menarik.
β Gambaran umum paket yang digunakan dalam proyek
Ini adalah file
package.json
dari proyek kami, di mana Anda dapat menemukan informasi tentang paket-paket yang digunakan dalam proyek ini:
{ "name": "vanilla-todos-pwa", "version": "1.0.0", "description": "A simple todo application using ES6 and Webpack", "main": "src/main.js", "scripts": { "build": "webpack --mode production && node build-sw", "serve": "webpack-dev-server --mode=development --hot" }, "keywords": [], "author": "Anurag Majumdar", "license": "MIT", "devDependencies": { "@babel/core": "^7.2.2", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/preset-env": "^7.2.3", "autoprefixer": "^9.4.5", "babel-core": "^6.26.3", "babel-loader": "^8.0.4", "babel-preset-env": "^1.7.0", "clean-webpack-plugin": "^1.0.0", "compression-webpack-plugin": "^2.0.0", "copy-webpack-plugin": "^4.6.0", "css-loader": "^2.1.0", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.5.0", "node-sass": "^4.11.0", "sass-loader": "^7.1.0", "style-loader": "^0.23.1", "terser": "^3.14.1", "webpack": "^4.28.4", "webpack-cli": "^3.2.1", "webpack-dev-server": "^3.1.14", "webpack-md5-hash": "0.0.6" } }
Jika kita berbicara tentang dukungan proyek-proyek seperti itu, perlu dicatat bahwa alat dalam ekosistem Webpack sering diperbarui. Sering terjadi bahwa plugin yang ada diganti dengan yang baru. Oleh karena itu, penting, ketika memutuskan apakah akan menggunakan yang baru dan bukan beberapa paket, untuk fokus bukan pada paket itu sendiri, tetapi pada fitur yang harus mereka terapkan. Sebenarnya, itulah sebabnya kami berbicara di atas tentang peran yang dimainkan paket ini atau itu.
JavaScript fitur modern
Selama pengembangan aplikasi web, programmer memiliki pilihan - apakah akan menulis implementasi sendiri dari fitur-fitur seperti deteksi perubahan, perutean, penyimpanan data, atau menggunakan paket yang ada.
Sekarang kita akan berbicara tentang set minimum teknologi yang diperlukan untuk memastikan operasi proyek kami. Jika perlu, rangkaian teknologi ini dapat diperluas menggunakan kerangka kerja atau paket yang ada.
β Modul
Kami akan menggunakan kemampuan ES6 untuk mengimpor dan mengekspor modul, mengingat setiap file sebagai modul ES6. Fitur ini sering ditemukan dalam kerangka kerja populer seperti Angular dan React, sangat nyaman untuk menggunakannya. Berkat konfigurasi Webpack yang kami miliki, kami dapat menggunakan ekspresi sumber daya impor dan ekspor. Berikut tampilannya di file
app.js
:
import { appTemplate } from './app.template'; import { AppModel } from './app.model'; export const AppComponent = {
βBerbagai cara untuk membuat objek
Pembuatan komponen adalah bagian penting dari proses pengembangan aplikasi kami. Di sini sangat mungkin untuk menggunakan beberapa alat modern, seperti komponen web, tetapi agar tidak menyulitkan proyek, kami akan menggunakan objek JavaScript biasa, yang dapat dibuat baik menggunakan objek literal atau menggunakan sintaks kelas yang muncul dalam standar ES6 .
Keunikan menggunakan kelas untuk membuat objek adalah bahwa setelah deskripsi kelas, Anda perlu membuat instance objek berdasarkan, dan kemudian mengekspor objek ini. Untuk menyederhanakan banyak hal bahkan lebih kuat, kita akan menggunakan di sini objek biasa yang dibuat menggunakan objek literal. Berikut adalah kode untuk file
app.js
mana Anda dapat melihat aplikasi mereka.
import { appTemplate } from './app.template'; import { AppModel } from './app.model'; export const AppComponent = { init() { this.appElement = document.querySelector('#app'); this.initEvents(); this.render(); }, initEvents() { this.appElement.addEventListener('click', event => { if (event.target.className === 'btn-todo') { import( './todo/todo.module') .then(lazyModule => { lazyModule.TodoModule.init(); }) .catch(error => 'An error occurred while loading Module'); } }); document.querySelector('.banner').addEventListener('click', event => { event.preventDefault(); this.render(); }); }, render() { this.appElement.innerHTML = appTemplate(AppModel); } };
Di sini kita membentuk dan mengekspor komponen
AppComponent
, yang dapat langsung Anda gunakan di bagian lain aplikasi.
Anda bisa menggunakan kelas ES6 atau komponen web dengan sangat baik dalam situasi seperti itu, mengembangkan proyek dengan gaya yang lebih dekat dengan deklaratif daripada yang digunakan di sini. Di sini, agar tidak menyulitkan proyek pelatihan, pendekatan imperatif digunakan.
Import Impor sumber daya dinamis
Ingat bahwa, berbicara tentang pola PRPL, kita belum menemukan bagian yang diwakili oleh huruf L (pemuatan Malas)? Impor sumber daya yang dinamis adalah hal yang membantu kami mengatur pemuatan komponen atau modul yang malas. Karena kami menggunakan arsitektur App Shell dan pola PRPL untuk menyimpan "kerangka" aplikasi dan sumber dayanya, proses impor dinamis memuat sumber daya dari cache, bukan dari jaringan.
Harap perhatikan bahwa jika kami hanya menggunakan arsitektur App Shell, maka sumber daya aplikasi yang tersisa, yaitu isi folder
chunks
, tidak akan di-cache.
Contoh impor sumber daya dinamis dapat dilihat pada fragmen kode di atas komponen
AppComponent
, khususnya, di mana peristiwa klik tombol dikonfigurasikan (kita berbicara tentang metode objek
initEvents()
). Yaitu, jika pengguna aplikasi mengklik tombol
btn-todo
, modul
btn-todo
akan dimuat. Modul ini adalah file JavaScript biasa yang berisi sekumpulan komponen yang direpresentasikan sebagai objek.
β Fungsi panah dan templat literal
Fungsi panah sangat berguna ketika Anda membutuhkan
this
dalam fungsi tersebut untuk menunjukkan konteks di mana fungsi tersebut dinyatakan. Selain itu, fungsi panah memungkinkan Anda menulis kode yang lebih ringkas daripada menggunakan fungsi konvensional. Berikut ini contoh fungsi tersebut:
export const appTemplate = model => ` <section class="app"> <h3><font color="#3AC1EF">β ${model.title} </font></h3> <section class="button"> <button class="btn-todo"> Todo Module </button> </section> </section> `;
Fungsi
appTemplate
mengambil model (parameter
model
) dan mengembalikan string HTML yang berisi data yang diambil dari model. . , - .
, , .
reduce()
HTML-:
const WorkModel = [ { id: 1, src: '', alt: '', designation: '', period: '', description: '' }, { id: 2, src: '', alt: '', designation: '', period: '', description: '' },
, . . , , .
:
model
. β , reduce()
, .model.reduce
, HTML-, , . , , , β .
. , , β .
Todo-, . .
.
. , , , .
-
-, , -, . , , . , JavaScript -, .
, .
-. Lighthouse.
Ringkasan
, , JavaScript, . , , , .
Pembaca yang budiman! -, ?
