Ekspresikan middleware untuk melayani Aplikasi Halaman Tunggal dengan url pushState dan peningkatan kinerja
![Gitter](https://badges.gitter.im/Gabung Obrolan.svg)
Linux: Windows: Umum:
Serve-SPA berperilaku seperti middleware express.static. Namun, jika url pushState diminta, Serve-SPA tidak mengembalikan 404 melainkan menyajikan halaman SPA yang cocok.
Asumsikan Anda memiliki SPA yang sangat sederhana yang disajikan dari folder ini:
spa
|-- index.html
|-- app.js
Pada awalnya, pengunjung biasanya memuat SPA Anda melalui url dasar http://localhost:3000/
. Dengan demikian index.html disajikan dan app.js juga dimuat. Selanjutnya pengunjung menavigasi SPA Anda yang memperbarui url menggunakan pushState dan misalnya tiba di http://localhost:3000/profile/me
. Pengunjung sekarang mungkin menandai halaman ini dan membukanya lagi keesokan harinya. express.static akan mengirimkan 404 untuk url ini karena folder yang disajikan tidak berisi folder "profil" yang berisi file "saya". Namun, Serve-SPA mengenali http://localhost:3000/profile/me
sebagai url pushState dan mencari struktur folder untuk halaman yang paling cocok dengan url yang diberikan, yaitu http://localhost:3000/
.
Yang perlu Anda lakukan untuk mengaktifkan dukungan url pushState adalah mengganti nama file index.html
Anda menjadi index.htmlt
(dengan a t ). Yaitu:
spa
|-- index.htmlt <-- Just renamed and pushState urls are supported
|-- app.js
Beginilah cara SPA biasa (dalam hal ini menggunakan Angular.js) dimuat:
Waktu hingga pengguna melihat halaman meningkat secara signifikan dengan dua permintaan AJAX "list.html" yang merupakan template HTML dan "proyek" yang merupakan data JSON yang digunakan untuk mengisi halaman.
Dengan Serve-SPA Anda dapat dengan mudah memasukkan template/data ke dalam index.htmlt
sehingga panggilan AJAX dilewati dan halaman segera dirender:
Serve-SPA menghadirkan kekuatan templat lodash ke file index.htmlt
Anda. Contoh SPA reguler di atas menggunakan halaman html ini:
<!doctype html >
< html ng-app =" project " >
< head >
< title > Pure Angular.js SPA </ title >
< script src =" /bower_components/angular/angular.min.js " > </ script >
< script src =" /bower_components/angular-resource/angular-resource.min.js " > </ script >
< script src =" /bower_components/angular-route/angular-route.min.js " > </ script >
< link rel =" stylesheet " href =" /bower_components/bootstrap/dist/css/bootstrap.min.css " >
< script src =" /scripts/app.js " > </ script >
< base href =" / " >
</ head >
< body >
< div class =" container " >
< h1 > JavaScript Projects </ h1 >
< div ng-view > </ div >
</ div >
</ body >
</ html >
Jika pengunjung meminta http://localhost:3000/
SPA ini memerlukan template list.html
untuk dirender. Untuk melewati panggilan AJAX yang diperlukan, templat harus disisipkan. Namun, karena url pushState lain tidak memerlukan templat ini, maka templat ini hanya boleh dimasukkan jika http://localhost:3000/
diminta. Hal ini dapat dicapai dengan penambahan berikut pada index.htmlt
:
<body>
+ <% if (req.path === '/') { %>
+ <script type="text/ng-template" id="partials/list.html">
+ <%= require('fs').readFileSync('app/partials/list.html') %>
+ </script>
+ <% } %>
<div class="container">
<h1>JavaScript Projects</h1>
<div ng-view></div>
</div>
</body>
Ada beberapa cara (misalnya menggunakan compose.js
) untuk mengimplementasikan ini dengan cara yang lebih bersih dan tidak menghalangi, tetapi Anda mengerti maksudnya.
Jika Anda sudah menyajikan SPA dengan middleware express.static, Anda dapat menyajikannya dengan Serve-SPA:
// Just replace:
app . use ( express . static ( appDir ) ) ;
// with:
serveSpa ( app , appDir ) ;
Kemudian Anda mengganti nama file index.html
Anda menjadi index.htmlt
.
app
|-- blog
| |-- img
| | |-- ...
| |
- | |-- index.html
+ | |-- index.htmlt
| |-- blog.css
| |-- blog.js
|
- |-- index.html
+ |-- index.htmlt
|-- main.css
|-- main.js
Ini memberi Anda dukungan url pushState dan fungsionalitas templat untuk memasukkan templat HTML dan data JSON ke dalam HTML yang disajikan untuk setiap permintaan. Misalnya, jika Anda perlu mengambil data dari database, Anda dapat menambahkan file compose.js
untuk melakukannya:
app
|-- blog
| |-- img
| | |-- ...
| |
+ | |-- compose.js // May load latest article headlines from the database
| |-- index.htmlt // May inline article headline so the browser spares an AJAX call
| |-- blog.css
| |-- blog.js
|
|-- index.htmlt
|-- main.css
|-- main.js
BTW, Serve-SPA tidak membuat asumsi apa pun tentang bagaimana SPA Anda diterapkan di sisi klien. Implementasi apa pun harus dapat bekerja dengan perubahan yang perlu dilakukan di sisi server.
Lihat repo Demo Serve-SPA yang bertujuan untuk menyediakan versi SPA reguler dan bermigrasi untuk kerangka kerja SPA terkenal. Misalnya untuk melihat cara memigrasikan SPA berbasis Angular.js, buatlah perbedaan antara implementasi regulernya dan versi yang telah dibuat sebelumnya yang memanfaatkan Serve-SPA sepenuhnya.
Modul untuk node.js diinstal melalui npm:
npm install serve-spa --save
Serve-SPA bergantung pada versi serve-static yang didefinisikan secara longgar. Jika Anda ingin menginstal versi tertentu, harap instal serve-static terlebih dahulu.
var serveSpa = require ( 'serve-spa' ) ;
serveSpa ( appOrRouter , rootPath , options ) ;
appOrRouter
harus diambil dari var app = express();
atau var router = express.Router();
tergantung di mana Anda ingin memasang Serve-SPA. Menggunakan router memungkinkan Anda memasang SPA pada url tertentu. Misalnya SPA dipasang di http://localhost:3000/app/
jika Anda menggunakan app.use('/app', router);
.rootPath
adalah jalur sistem file ke sumber daya SPA. Parameternya identik dengan yang digunakan dengan app.use(express.static(rootPath))
. Selain itu, rootPath
dapat berupa larik jalur untuk menggunakan banyak akar.options
adalah objek yang mengizinkan atribut berikut:options.staticSettings
diteruskan ke middleware servis-statis yang digunakan secara internal. BTW, ini adalah opsi yang sama yang diteruskan ke express.static(rootPath, staticSettings)
. Lihat dokumentasi opsi serve-static untuk mengetahui detailnya.options.beforeAll
mengambil middleware yang dieksekusi sebelum template dirender. Gunakan untuk melakukan hal-hal umum yang diperlukan untuk semua templat. Jika Anda perlu melakukan sesuatu untuk templat tertentu, gunakan file composer.js sebagai gantinya.options.templateSettings
diteruskan ke _.template(template, templateSettings)
lodash. Lihat dokumentasi _.templateSettings
untuk detailnya.options.require
mengambil fungsi require tertentu untuk digunakan dalam rendering template. Jika opsi tidak diberikan, Serve-SPA menyediakan fungsi kebutuhannya sendiri saat merender template. Fungsi yang diperlukan ini mungkin memiliki hierarki pencarian jalur modul yang berbeda. Oleh karena itu, opsi ini memungkinkan untuk meneruskan fungsi kebutuhan lain yang memiliki hierarki pencarian jalur modul pilihan.Templatnya adalah file html yang mungkin berisi campuran JavaScript:
<!doctype html >
< html >
< head >
< title > Example template </ title >
</ head >
< body >
Today is: < %= (new Date()).toString() % > < br />
< ul >
< % _.forEach(['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], function (day, i) { % >
< li < %= (new Date()).getDay() === i ? ' style="color: red;"' : '' % > >
< %= day % >
</ li >
< % }); % >
</ ul >
</ body >
</ html >
Ini menghasilkan:
Today is: Tue Oct 20 2015 16:09:40 GMT+0200 (CEST) < br />
< ul >
< li > Sunday </ li >
< li > Monday </ li >
< li style =" color: red; " > Tuesday </ li >
< li > Wednesday </ li >
< li > Thursday </ li >
< li > Friday </ li >
< li > Saturday </ li >
</ ul >
Di dalam templat, variabel-variabel berikut tersedia:
_
: Perpustakaan lodashrequire
: Fungsi kebutuhan Noderequest
dan req
: Objek permintaan yang disediakan oleh Expressresponse
dan res
: Objek respons yang disediakan oleh Express Jika rendering gagal, Kesalahan diteruskan ke middleware penanganan kesalahan berikutnya. Anda dapat mendaftarkan middleware penanganan kesalahan Anda sendiri seperti ini:
// First Serve-SPA
serveSpa ( app , rootPath ) ;
// Then this middleware with the additional err parameter
app . use ( function ( err , req , res , next ) {
// If headers were already sent it gets complicated...
if ( res . headersSent ) {
return next ( err ) ; // ...just leave it to Express' default error handling middleware.
}
// Do your error handling:
console . error ( err ) ;
res . redirect ( '/guru-meditation/' ) ;
} ) ;
Masukkan file compose.js
ke dalam folder yang sama dengan index.htmlt
Anda dan file tersebut akan dieksekusi tepat sebelum template dirender. Hal ini memungkinkan misalnya mengambil data dari database untuk digunakan saat merender template.
compose.js
harus mengekspor middleware:
var db = require ( '../lib/db.js' ) ;
module . exports = function ( req , res , next ) {
db . loadData ( { for : 'me' } , function ( err , data ) {
if ( err ) {
return next ( err ) ;
}
req . data = data ; // You can access the data through req.data in the template now.
next ( ) ;
} ) ;
} ;
Middleware yang diekspor melalui compose.js
digunakan seperti middleware biasa. Dengan demikian penanganan error berfungsi seperti biasa.
Middleware dapat diteruskan melalui options.beforeAll
yang dijalankan untuk semua permintaan yang menghasilkan rendering template. Jika hanya file statis yang diminta, middleware ini tidak dijalankan.
Urutan eksekusi keseluruhan adalah sebagai berikut:
Middleware yang disediakan digunakan seperti middleware biasa. Dengan demikian penanganan error berfungsi seperti biasa.
Seperti express.static(...)
Serve-SPA hanya memproses permintaan GET dan HEAD. Secara default, untuk permintaan HEAD, middleware beforeAll dan composer.js tidak dijalankan atau template index.htmlt tidak dirender. Namun, mengeksekusi middleware dapat diaktifkan secara eksplisit dengan menambahkan callForHEAD = true
ke fungsi middleware:
function middlewareForGETandHEAD ( req , res , next ) {
res . set ( 'my-header' , 'yay' ) ;
if ( req . method === 'GET' ) {
db . load ( ... ) ;
}
}
middlewareForGETandHEAD . callForHEAD = true ;
README Serve-Static menjelaskan untuk menggunakan banyak akar seperti ini:
app . use ( serveStatic ( path . join ( __dirname , '/public-optimized' ) ) ) ;
app . use ( serveStatic ( path . join ( __dirname , '/public' ) ) ) ;
Namun, dengan Serve-SPA, penggunaan beberapa middleware seperti ini tidak mungkin lagi. Karena menangani url pushState dengan benar membuatnya lebih rumit.
Untuk memberikan dukungan yang sama pada banyak root, Serve-SPA mengambil serangkaian jalur root. Kode berikut ini setara dengan contoh Serve-Static di atas:
serveSpa ( app , [
path . join ( __dirname , '/public-optimized' ) ,
path . join ( __dirname , '/public' )
] ) ;
Url hanya diidentifikasi sebagai url pushState jika tidak ada sumber daya statis di semua jalur tertentu yang cocok dengan url tersebut. Dan url pushState diterapkan ke SPA yang paling cocok seperti biasanya – seolah-olah direktori tertentu digabungkan.
Untuk menyiapkan lingkungan pengembangan Anda untuk Serve-SPA:
cd
shell ke folder utama,npm install
,npm install gulp -g
jika Anda belum menginstal gulp secara global, dangulp dev
. (Atau jalankan node ./node_modules/.bin/gulp dev
jika Anda tidak ingin menginstal gulp secara global.) gulp dev
mengawasi semua file sumber dan jika Anda menyimpan beberapa perubahan, itu akan membuang kode dan menjalankan semua tes. Laporan cakupan pengujian dapat dilihat dari ./coverage/lcov-report/index.html
.
Jika Anda ingin men-debug pengujian, Anda harus menggunakan gulp test-without-coverage
untuk menjalankan semua pengujian tanpa mengaburkan kode dengan instrumentasi cakupan pengujian.
minimatch
yang dipatch seperti yang disarankan oleh Platform Keamanan Nodeexpress.Router()
di composer.jsexpress.Router()
Jika Anda belum pernah mendengar tentang lisensi ISC, lisensi ini secara fungsional setara dengan lisensi MIT.
Lihat file LISENSI untuk detailnya.