17. [nuxt-20] örneği: [vuejs-22] örneğinin uyarlanması
17.1. Présentation
Burada, SPA türündeki [vue.js] uygulamasını oluşturan [vuejs-22] örneğini, [nuxt] SSR bağlamına taşımayı öneriyoruz. [vuejs-22], vergi hesaplama sunucusunun bir istemci uygulamasıydı ve aşağıdaki görünümleri içeriyordu:
İlk görünüm, kimlik doğrulama görünümüdür:

İkinci ekran, vergi hesaplama ekranıdır:

Üçüncü ekran, kullanıcı tarafından yapılan simülasyonların listesini gösteren ekrandır:

Yukarıdaki ekran, 1 numaralı simülasyonun silinebileceğini göstermektedir. Bunun üzerine aşağıdaki görünüm elde edilir:

Şimdi son simülasyonu silersek, aşağıdaki yeni ekran karşımıza çıkar:

[vuejs-22] uygulamasını kademeli olarak [nuxt-20] uygulamasına taşıyacağız. [vuejs-22] kodlarını tekrar açıklamayacağız. Okuyucunun |Introduction au framework VUE.JS par l’exemple| belgesini tekrar gözden geçirmesi önerilir. Farklı adımlar, bir [vuejs] uygulaması ile bir [nuxt] uygulaması arasındaki farkları göstermelidir.
17.2. 1. adım
[nuxt-20] projesi, başlangıçta [nuxt-12] projesinin kopyalanmasıyla elde edilir. Bu proje, aslında iyi bir başlangıç noktasıdır:
- vergi hesaplama sunucusuyla iletişim kurabiliyor;
- sunucunun gönderdiği hataları doğru bir şekilde yönetir;
- [nuxt] istemcisi ve sunucusu, bir [nuxt] oturumu aracılığıyla iletişim kurabiliyor;
Dolayısıyla iyi bir başlangıç altyapısına sahibiz. Asıl işimiz şu değişiklikleri yapmak olmalı:
- sayfaları değiştirmek. [vuejs-22] projesindeki sayfaları alıp yeni ortama uyarlayacağız;
- mağaza yönetimi. Ek bilgiler eklenmeli (simülasyon listesi) ve bazı bilgiler ise gereksiz hale gelebilir;
- [nuxt] istemci ve sunucu yönlendirme yönetimi;
Bu nedenle öncelikle, [nuxt-12] projesini kopyalayarak [nuxt-20] projesini oluşturacağız:

Ardından, artık gereksiz hale gelen sayfaları ve bileşenleri siliyoruz [2]:
- [components/navigation] bileşeni kaybolur;
- [layout/default] düzeni kaybolur;
- [index, authentification, get-admindata, fin-session] sayfaları silinir;
Ardından, [nuxt-20]'e [vuejs-22] ve [3]'ten öğeler eklenir:
- [vuejs-22] uygulamasındaki üç [Authentification, CalculImpot, ListeSimulations] sayfası, [pages] klasörüne taşınır;
- [vuejs-22] uygulamasının [FormCalculImpot, Menu, Layout] bileşenleri, [components] klasörüne taşınır;
- [vuejs-22] uygulamasında [layout] olarak kullanılan [vuejs-22] sayfasının [Main] sayfası, [layouts] klasörüne taşınır;
[4] adlı entegre öğelerin adları şu şekilde değiştirilir:

- [layouts]'te, [Main], [nuxt] uygulamasının düzeninin varsayılan adı olduğu için [default] haline geldi;
- [pages] dosyasında, [Authentification] sayfası [index] olarak değiştirilmiştir; çünkü [Authentification], [vuejs-22] uygulamasında bu rolü üstleniyordu;
Bu noktada, ilk hataları görmek için projeyi derleyebiliriz. [nuxt-12] örneğindeki [nuxt.config] dosyasını, artık [nuxt-20]'i çalıştıracak şekilde değiştiriyoruz:
export default {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Introduction à [nuxt.js]',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: 'ssr routing loading asyncdata middleware plugins store'
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},
/*
** Customize the progress-bar color
*/
loading: false,
/*
** Global CSS
*/
css: [],
/*
** Plugins to load before mounting the App
*/
plugins: [
{ src: '@/plugins/client/plgSession', mode: 'client' },
{ src: '@/plugins/server/plgSession', mode: 'server' },
{ src: '@/plugins/client/plgDao', mode: 'client' },
{ src: '@/plugins/server/plgDao', mode: 'server' },
{ src: '@/plugins/client/plgEventBus', mode: 'client' }
],
/*
** Nuxt.js dev-modules
*/
buildModules: [
// Belge: https://github.com/nuxt-community/eslint-module
'@nuxtjs/eslint-module'
],
/*
** Nuxt.js modules
*/
modules: [
// Belge: https://bootstrap-vue.js.org
'bootstrap-vue/nuxt',
// Belge: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
// https://www.npmjs.com/package/cookie-universal-nuxt
'cookie-universal-nuxt'
],
/*
** Axios module configuration
** See https://axios.nuxtjs.org/options
*/
axios: {},
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
},
// kaynak kod dizini
srcDir: 'nuxt-20',
// yönlendirici
router: {
// uygulamanın URL kökü
base: '/nuxt-20/',
// yönlendirme ara yazılımı
middleware: ['routing']
},
// sunucu
server: {
// hizmet bağlantı noktası, varsayılan olarak 3000
port: 81,
// dinlenen ağ adresleri, varsayılan olarak localhost: 127.0.0.1
// 0.0.0.0 = makinenin tüm ağ adresleri
host: 'localhost'
},
// ortam
env: {
// axios yapılandırması
timeout: 2000,
withCredentials: true,
baseURL: 'http://localhost/php7/scripts-web/impots/version-14',
// oturum çerezi yapılandırması [nuxt]
maxAge: 60 * 5
}
}
Ardından projenin [build] derlemesini yapıyoruz:

Bildirilen hatalar şunlardır:
- 1. satırdaki hata, var olmayan bir resme atıfta bulunulduğunu gösterir. Bu resim, [vuejs-22] komutunda alınacaktır;
- 2. satırdaki hata, [./FormCalculImpot] bileşeninin mevcut olmadığını göstermektedir. Nitekim bu bileşen artık [@/components/form-calcul-impot]'te yer almaktadır;
- [3-5] satırlarındaki hatalar, [./Layout] bileşeninin mevcut olmadığını göstermektedir. Nitekim bu bileşen artık [@/components/layout] içinde yer almaktadır;
- [6-7] satırlarındaki hatalar, [./Menu] bileşeninin mevcut olmadığını göstermektedir. Nitekim, bu bileşen artık [@/components/menu] olarak adlandırılmaktadır;
[assets/logo.jpg] adlı resim, [nuxt-20] projesine eklenir:

Ayrıca, tüm sayfalarda bileşenlerin yollarını düzelteceğiz. [calcul-impot] sayfasını örnek olarak alalım:
<!-- HTML görünüm tanımı -->
<template>
<div>
<Layout :left="true" :right="true">
<!-- sağdaki vergi hesaplama formu -->
<FormCalculImpot slot="right" @resultatObtenu="handleResultatObtenu" />
<!-- sol taraftaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
<!-- formun altındaki vergi hesaplama sonuçlarının görüntülenme alanı -->
<b-row v-if="résultatObtenu" class="mt-3">
<!-- boş üç sütunlu alan -->
<b-col sm="3" />
<!-- dokuz sütunlu alan -->
<b-col sm="9">
<b-alert show variant="success">
<span v-html="résultat"></span>
</b-alert>
</b-col>
</b-row>
</div>
</template>
<script>
// ithalatlar
import FormCalculImpot from './FormCalculImpot'
import Menu from './Menu'
import Layout from './Layout'
export default {
// kullanılan bileşenler
components: {
Layout,
FormCalculImpot,
Menu
},
26-28. satırlardaki üç [import] şu şekilde değiştirilecek:
// ithalatlar
import FormCalculImpot from '@/components/form-calcul-impot'
import Menu from '@/components/menu'
import Layout from '@/components/layout'
Böylece tüm bileşenler, düzenler ve sayfalardaki [import] değerlerini kontrol eder ve gerekirse düzeltiriz. Bu düzeltmeler yapıldıktan sonra, yeni bir [build] denemesi yapabiliriz. Normalde artık hata kalmaz.
Ardından şu komutu çalıştırmayı deneyebiliriz:

Hatalar ortaya çıkıyor:
Görüldüğü üzere, [dev] komutu, [eslint] modülüyle birleştiğinde, sözdizimi açısından [build] komutundan daha katıdır. Burada, [!=] karşılaştırma operatörünün, daha katı bir operatör olan (aynı zamanda işlenenlerin türünü de kontrol eden) [!==] olarak yazılması istenmektedir. Bu hatalar [index.vue] sayfasında ortaya çıkmaktadır.
Yukarıdaki hataları düzelttikten sonra projenin yürütülmesini yeniden başlatıyoruz. Ardından, [eslint] modülünden bir uyarı alıyoruz:

Bu hatayı, [eslint] ve [2] modüllerinden [Quick fix] ile düzeltin.
Projenin yürütülmesini yeniden başlatıyoruz. Artık derleme hatası yok. Ardından bir tarayıcıyla URL ve [http://localhost:81/nuxt-20/] dosyalarını çağırıyoruz. Bir yürütme hatası alıyoruz:

Hata, [index.vue] ve [2] dosyalarında bulunuyor. [1] hatası, [vuejs-22] dosyasında [dao] katmanı [this.$dao]'te mevcutken, altyapısını benimsediğimiz [nuxt-12]'te ise bu katman [this.$dao()] işlevinde mevcut olması nedeniyle ortaya çıkmıştır.
Hata, [index] sayfasının yaşam döngüsündeki [created] işlevinde yer almaktadır:

Şimdilik, yaşam döngüsü fonksiyonu [created]'in çalıştırılmaması için [created]'i [created2] olarak yeniden adlandırıyoruz.
Değişikliği kaydedip [index] sayfasını tarayıcıda yeniden yüklüyoruz. Bu sefer doğru:

17.3. 2. adım
[vuejs-22] projesinin sayfaları aşağıdaki enjekte edilmiş öğeleri kullanıyordu:
- $dao: [vue.js] istemcisinin [dao] katmanı için;
- $session: tarayıcının [localStorage]'inde depolanan bir oturum için;
Kopyaladığımız [nuxt-12] proje altyapısında bu öğeler artık mevcut değildir:
- artık iki [dao] katmanı bulunmaktadır; biri [nuxt] istemcisi, diğeri ise [nuxt] sunucusu içindir. Her ikisi de [$dao] adlı bir enjeksiyon fonksiyonu aracılığıyla kullanılabilir. Bu, uygulama sayfalarında [this.$dao]'in [this.$dao()] ile değiştirilmesi gerektiği anlamına gelir;
- [nuxt-20] uygulaması tarafından yönetilen [nuxt] oturumu, oturum çerezi kavramının bulunmadığı[vuejs-22] uygulamasındaki [$session] nesnesiyle artık hiçbir ilgisi yoktur; bu uygulamada oturum çerezi kavramı mevcut değildi. Bununla birlikte, her ikisi de benzer bir işlevselliğe sahiptir: kullanıcının eylemleri boyunca kalıcı bilgileri depolamak. [nuxt] oturumu, bilgileri doğrudan oturumda değil, depoda saklar. Uygulamanın sayfalarında, oturumda bilgi depolamak söz konusu olduğunda [this.$session], oturumun kendisiyle işlem yapmak söz konusu olduğunda ise [this.$session()] ile değiştirilmelidir;
- depolamadaki bir P özelliğinin durumunu öğrenmek için [this.$store.state.P] yazılmalıdır;
- deposunun P özelliğini değiştirmek için [this.$store.commit(‘replace’, {P:value}] yazılmalıdır;
Bu değişiklikleri [index] sayfasında yapıyoruz:
<!-- HTML görünüm tanımı -->
<template>
<Layout :left="false" :right="true">
<template slot="right">
<!-- HTML formu - değerler [authentifier-utilisateur] eylemi ile gönderilir -->
<b-form @submit.prevent="login">
<!-- başlık -->
<b-alert show variant="primary">
<h4>Bienvenue. Veuillez vous authentifier pour vous connecter</h4>
</b-alert>
<!-- 1. satır -->
<b-form-group label="Nom d'utilisateur" label-for="user" label-cols="3">
<!-- kullanıcı giriş alanı -->
<b-col cols="6">
<b-form-input id="user" v-model="user" type="text" placeholder="Nom d'utilisateur" />
</b-col>
</b-form-group>
<!-- 2. satır -->
<b-form-group label="Mot de passe" label-for="password" label-cols="3">
<!-- şifre giriş alanı -->
<b-col cols="6">
<b-input id="password" v-model="password" type="password" placeholder="Mot de passe" />
</b-col>
</b-form-group>
<!-- 3. satır -->
<b-alert v-if="showError" show variant="danger" class="mt-3">L'erreur suivante s'est produite : {{ message }}</b-alert>
<!-- 3. satırda [submit] tipi düğme -->
<b-row>
<b-col cols="2">
<b-button :disabled="!valid" variant="primary" type="submit">Valider</b-button>
</b-col>
</b-row>
</b-form>
</template>
</Layout>
</template>
<!-- görünüm dinamikleri -->
<script>
/* eslint-disable no-console */
import Layout from '@/components/layout'
export default {
// kullanılan bileşenler
components: {
Layout
},
// bileşen durumu
data() {
return {
// kullanıcı
user: '',
// şifresi
password: '',
// hata mesajının görüntülenmesini kontrol eder
showError: false,
// hata mesajı
message: ''
}
},
// hesaplanan özellikler
computed: {
// geçerli girdiler
valid() {
return this.user && this.password && this.$store.state.started
}
},
// yaşam döngüsü: bileşen yeni oluşturuldu
mounted() {
// eslint-disable-next-line
console.log("Authentification mounted");
// kullanıcı simülasyon yapabilir mi?
if (this.$store.state.started && this.$store.state.authenticated && this.$métier.taxAdminData) {
// o halde kullanıcı simülasyon yapabilir
this.$router.push({ name: 'calculImpot' })
// olay döngüsüne geri dön
return
}
// jSON oturumu daha önce başlatılmışsa, yeniden başlatılmaz
if (!this.$store.state.started) {
// bekleme başlıyor
this.$emit('loading', true)
// sunucu ile oturum başlatılıyor - asenkron istek
// [dao] katmanındaki yöntemler tarafından döndürülen söz kullanılır
this.$dao()
// bir oturum başlatılıyor jSON
.initSession()
// yanıt alındı
.then((response) => {
// bekleme sona erdi
this.$emit('loading', false)
// yanıtın analizi
if (response.état !== 700) {
// hata görüntüleniyor
this.message = response.réponse
this.showError = true
// olay döngüsüne geri dönülüyor
return
}
// oturum başlatıldı
this.$store.commit('replace', { started: true })
console.log('[authentification], session=', this.$session())
})
// hata durumunda
.catch((error) => {
// hata, [Main] görünümüne iletilir
this.$emit('error', error)
})
// her durumda
.finally(() => {
// oturum kaydedilir
this.$session().save()
})
}
},
// olay yöneticileri
methods: {
// ----------- kimlik doğrulama
async login() {
try {
// bekleme başlangıcı
this.$emit('loading', true)
// henüz kimlik doğrulaması yapılmadı
this.$store.commit('replace', { authenticated: false })
// sunucuda engelleyici kimlik doğrulama
const response = await this.$dao().authentifierUtilisateur(this.user, this.password)
// yükleme bitti
this.$emit('loading', false)
// sunucu yanıtı analiz ediliyor
if (response.état !== 200) {
// hata görüntüleniyor
this.message = response.réponse
this.showError = true
// olay döngüsüne geri dönüş
return
}
// hata yok
this.showError = false
// kimlik doğrulaması yapıldı
this.$store.commit('replace', { authenticated: true })
// --------- şimdi vergi idaresinden veriler isteniyor
// başlangıçta veri yok
this.$métier.setTaxAdminData(null)
// bekleme başladı
this.$emit('loading', true)
// sunucuya yapılan istek bloke edildi
const response2 = await this.$dao().getAdminData()
// yükleme tamamlandı
this.$emit('loading', false)
// yanıtın analizi
if (response2.état !== 1000) {
// hata görüntüleniyor
this.message = response2.réponse
this.showError = true
// olay döngüsüne geri dönüş
return
}
// hata yok
this.showError = false
// alınan veri [métier] katmanında saklanıyor
this.$métier.setTaxAdminData(response2.réponse)
// vergi hesaplamasına geçilebilir
this.$router.push({ name: 'calculImpot' })
} catch (error) {
// hata ana bileşene iletilir
this.$emit('error', error)
} finally {
// değişiklik kaydedilir
this.$store.commit('replace', { métier: this.$métier })
// oturum kaydediliyor
this.$session().save()
}
}
}
}
</script>
Aşağıdaki noktalara dikkat edelim:
- 69. satır: [created2] işlevi, [mounted] olarak yeniden adlandırılmıştır; bunun amacı, [nuxt] sunucusunun bu işlevi çalıştırmamasıdır (bu sunucu ne [beforeMount] ne de [mounted] işlevlerini çalıştırır). Sadece [nuxt] istemcisi bunu çalıştıracaktır, tıpkı [vuejs-22] örneğinde olduğu gibi;
- 73. satır: Şu anda mevcut olmayan [this.$métier]'e atıfta bulunuluyor;
- 75. satır: Bu yöntemi [nuxt] uygulamasında hiç kullanmadık. [nuxt] bağlamında çalışıp çalışmadığını kontrol etmek gerekecek;
- satır 112, 172: [vuejs-22]'te proje oturumu bu şekilde kaydedilmişti. [nuxt-20] projesinde ise [save] yöntemine geçerli bağlamın aktarılması gerekir. Bilindiği üzere, [nuxt] sayfasında, [context] nesnesi [this.$nuxt.context]'te mevcuttur;
Bu nedenle 112. ve 172. satırlar şu şekilde yeniden yazılır:
this.$session().save(this.$nuxt.context)
Bu kodun optimize edilmediğine dikkat edilmelidir. [this.$session()] işlevini birkaç kez kullanmak yerine, şu şekilde yazmak daha uygun olur:
const session=this.$session()
ve ardından [session] değişkenini kullanmak daha uygun olur. Aynı mantık, [this.$dao()] işlevi için de geçerlidir.
Bu düzeltmeleri yaptıktan sonra, URL ve [http://localhost:81/nuxt-20/]'i bir tarayıcıyla yeniden yükleyebiliriz. Hala daha önce gördüğümüzle aynı sayfayı elde ediyoruz:

Tarayıcının günlüklerine bir göz atalım:

[1] günlüğü, [nuxt] istemcisi tarafından oluşturulan son günlüktür. [2]'te, [started] özelliğinin [vrai]'e ait olduğunu görüyoruz; bu, [mounted] işlevinin vergi hesaplama sunucusuyla jSON oturumunu başlatmayı başardığı anlamına gelir. Ayrıca, deponun ya silinmesi ya da yeniden adlandırılması gereken özellikleri olduğu da görülüyor. Hatırlayalım ki, [nuxt-12] örneğindeki depoyu kullanıyoruz.
Şimdi, vergi hesaplama sunucusu çalışmıyorken URL ve [http://localhost:81/nuxt-20/] oturumlarını yeniden talep edelim. Öncelikle, [nuxt] oturum çerezini silmeye özen gösterelim:

Yukarıdaki ekran görüntüsü Chrome'dan alınmıştır. Bu işlem tamamlandıktan sonra, URL ve [http://localhost:81/nuxt-20/] çerezleri şu sonucu verir:

Hata, [vuejs-22] projesi tarafından doğru şekilde yönetildi. Hata, [nuxt-20] projesi tarafından da doğru şekilde yönetilmeye devam ediyor.
17.4. 3. adım
Artık kimlik doğrulama sayfasına sahip olduğumuza göre, kullanıcı [Valider] düğmesine tıkladığında yürütülen koda bakmamız gerekiyor:
// olay yöneticileri
methods: {
// ----------- kimlik doğrulama
async login() {
try {
// bekleme başlıyor
this.$emit('loading', true)
// henüz kimlik doğrulaması yapılmadı
this.$store.commit('replace', { authenticated: false })
// sunucuda engelleyici kimlik doğrulama
const response = await this.$dao().authentifierUtilisateur(this.user, this.password)
// yükleme bitti
this.$emit('loading', false)
// sunucu yanıtı analiz ediliyor
if (response.état !== 200) {
// hata görüntüleniyor
this.message = response.réponse
this.showError = true
// olay döngüsüne geri dönüş
return
}
// hata yok
this.showError = false
// kimlik doğrulaması yapıldı
this.$store.commit('replace', { authenticated: true })
// --------- şimdi vergi idaresinden veriler isteniyor
// başlangıçta veri yok
this.$métier.setTaxAdminData(null)
// bekleme başladı
this.$emit('loading', true)
// sunucuya yapılan istek bloke edildi
const response2 = await this.$dao().getAdminData()
// yükleme tamamlandı
this.$emit('loading', false)
// yanıtın analizi
if (response2.état !== 1000) {
// hata görüntüleniyor
this.message = response2.réponse
this.showError = true
// olay döngüsüne geri dönüş
return
}
// hata yok
this.showError = false
// alınan veri [métier] katmanında saklanıyor
this.$métier.setTaxAdminData(response2.réponse)
// vergi hesaplamasına geçilebilir
this.$router.push({ name: 'calculImpot' })
} catch (error) {
// hata ana bileşene iletilir
this.$emit('error', error)
} finally {
// değişiklik kaydedilir
this.$store.commit('replace', { métier: this.$métier })
// oturum kaydediliyor
this.$session().save(this.$nuxt.context)
}
}
}
Buradaki temel sorun, [this.$métier] verisinin eksikliği gibi görünüyor. Bunu gidermek için şunları yapacağız:
- [vuejs-22] örneğindeki [Métier] sınıfını ekleyeceğiz. Bunu [api] klasörüne yerleştireceğiz;
- [nuxt] istemcisinin bağlamına, bu sınıfa erişim sağlayacak bir [$métier] işlevi ekleyeceğiz;
Öncelikle, [Métier] sınıfını [api] klasörüne kopyalayalım:

[Métier] sınıfı projeye eklendikten sonra, [nuxt] istemcisi için yeni bir eklenti oluşturulur. [pluginMétier] adlı bu eklenti, [$métier] işlevini ekleyecek ve bu işlev [Métier] sınıfına erişim sağlayacaktır:
/* eslint-disable no-console */
// [métier] katmanına erişim noktası oluşturuluyor
import Métier from '@/api/client/Métier'
export default (context, inject) => {
// [métier] katmanının örneklenmesi
const métier = new Métier()
// [$métier] işlevinin bağlama enjeksiyonu
inject('métier', () => métier)
// günlük
console.log('[fonction client $métier créée]')
}
Bu işlem tamamlandıktan sonra, [index] sayfasını düzeltebiliriz:
// yaşam döngüsü: bileşen az önce oluşturuldu
mounted() {
// eslint-disable-next-line
console.log("Authentification mounted");
// kullanıcı simülasyon yapabilir mi?
if (this.$store.state.started && this.$store.state.authenticated && this.$métier().taxAdminData) {
// o halde kullanıcı simülasyon yapabilir
this.$router.push({ name: 'calcul-impot' })
// olay döngüsüne geri dön
return
}
// jSON oturumu daha önce başlatılmışsa, yeniden başlatılmaz
...
},
// olay işleyicileri
methods: {
// ----------- kimlik doğrulama
async login() {
try {
// bekleme başlangıcı
this.$emit('loading', true)
// henüz kimlik doğrulaması yapılmadı
this.$store.commit('replace', { authenticated: false })
// sunucuda engelleyici kimlik doğrulama
const response = await this.$dao().authentifierUtilisateur(this.user, this.password)
// yükleme bitti
this.$emit('loading', false)
// sunucu yanıtı analizi
if (response.état !== 200) {
// hatanın görüntülenmesi
this.message = response.réponse
this.showError = true
// olay döngüsüne geri dönüş
return
}
// hata yok
this.showError = false
// kimlik doğrulaması yapıldı
this.$store.commit('replace', { authenticated: true })
// --------- şimdi vergi idaresinden veriler isteniyor
// başlangıçta veri yok
this.$métier().setTaxAdminData(null)
// bekleme başladı
this.$emit('loading', true)
// sunucuya yapılan istek bloke edildi
const response2 = await this.$dao().getAdminData()
// yükleme tamamlandı
this.$emit('loading', false)
// yanıtın analizi
if (response2.état !== 1000) {
// hata görüntüleniyor
this.message = response2.réponse
this.showError = true
// olay döngüsüne geri dönüş
return
}
// hata yok
this.showError = false
// alınan veri [métier] katmanında saklanır
this.$métier().setTaxAdminData(response2.réponse)
// vergi hesaplamasına geçilebilir
this.$router.push({ name: 'calcul-impot' })
} catch (error) {
// hata ana bileşene iletilir
this.$emit('error', error)
} finally {
// değişiklik kaydedilir
this.$store.commit('replace', { métier: this.$métier() })
// oturum kaydediliyor
this.$session().save(this.$nuxt.context)
}
}
}
- 43., 61. ve 69. satırlarda, [this.$métier], [this.$métier()] ile değiştirilmiştir;
- 8. ve 63. satırlar: [vuejs-22] projesindeki [CalculImpot] sayfası, [nuxt-20] projesinde [calcul-impot] sayfası haline getirildi;
Bu düzeltmeler yapıldıktan sonra, kimlik doğrulama sayfasını onaylamayı deneyebiliriz:

Elde edilen sayfa şöyledir:

Vergi hesaplama sayfasını başarıyla elde ettik. Şimdi loglara bakalım:

[2]'te, oturum açma bilgilerinin doğru şekilde kaydedildiğini görüyoruz. [3-4]'te, [Métier] sınıfı aracılığıyla vergi hesaplamasına olanak tanıyan [taxAdminData] verisinin alındığını görüyoruz.
17.5. 4. adım
Elde ettiğimiz [calcul-impot] sayfasını inceleyelim:
<!-- HTML görünümünün tanımı -->
<template>
<div>
<Layout :left="true" :right="true">
<!-- sağdaki vergi hesaplama formu -->
<FormCalculImpot slot="right" @resultatObtenu="handleResultatObtenu" />
<!-- sol taraftaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
<!-- formun altında vergi hesaplama sonuçlarının görüntülendiği alan -->
<b-row v-if="résultatObtenu" class="mt-3">
<!-- boş üç sütunlu alan -->
<b-col sm="3" />
<!-- dokuz sütunlu alan -->
<b-col sm="9">
<b-alert show variant="success">
<span v-html="résultat"></span>
</b-alert>
</b-col>
</b-row>
</div>
</template>
<script>
// ithalatlar
import FormCalculImpot from '@/components/form-calcul-impot'
import Menu from '@/components/menu'
import Layout from '@/components/layout'
export default {
// kullanılan bileşenler
components: {
Layout,
FormCalculImpot,
Menu
},
// dahili durum
data() {
return {
// menü seçenekleri
options: [
{
text: 'Liste des simulations',
path: '/liste-des-simulations'
},
{
text: 'Fin de session',
path: '/fin-session'
}
],
// vergi hesaplama sonucu
résultat: '',
résultatObtenu: false
}
},
// yaşam döngüsü
created() {
// eslint-disable-next-line
console.log("CalculImpot created");
},
// olay yönetimi yöntemleri
methods: {
// vergi hesaplama sonucu
handleResultatObtenu(résultat) {
// sonuç bir dize olarak oluşturulur HTML
const impôt = "Montant de l'impôt : " + résultat.impôt + ' euro(s)'
const décôte = 'Décôte : ' + résultat.décôte + ' euro(s)'
const réduction = 'Réduction : ' + résultat.réduction + ' euro(s)'
const surcôte = 'Surcôte : ' + résultat.surcôte + ' euro(s)'
const taux = "Taux d'imposition : " + résultat.taux
this.résultat = impôt + '<br/>' + décôte + '<br/>' + réduction + '<br/>' + surcôte + '<br/>' + taux
// sonucun görüntülenmesi
this.résultatObtenu = true
// ---- deponun güncellenmesi [Vuex]
// bir simülasyon daha
this.$store.commit('addSimulation', résultat)
// oturum kaydediliyor
this.$session.save()
}
}
}
</script>
- 44. ve 48. satırlar: gezinme menüsündeki bağlantılar doğrudur. [/fin-session] sayfası mevcut değildir. [vuejs-22] projesi bu sorunu yönlendirme ile çözmüştü. [nuxt-20] projesinde de aynısını yapacağız;
- 76. satır: Şu anda mevcut olmayan [addSimulation] numaralı bir mutasyona atıfta bulunuluyor. Bunu oluşturacağız;
- 78. satır: [index] sayfasında olduğu gibi, [this.$session().save(this.$nuxt.context)] yazılmalıdır;
[store/index] depolama alanını değiştirelim. [nuxt-12] projesinden devralınan bu depolama alanı, şu anda şu şekildedir:
/* eslint-disable no-console */
// mağazanın durumu
export const state = () => ({
// jSON oturumu başlatıldı
jsonSessionStarted: false,
// kullanıcı kimliği doğrulandı
userAuthenticated: false,
// oturum çerezi PHP
phpSessionCookie: '',
// adminData
adminData: ''
})
// depo değişiklikleri
export const mutations = {
// durumun değiştirilmesi
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// depolama alanının sıfırlanması
reset() {
this.commit('replace', { jsonSessionStarted: false, userAuthenticated: false, phpSessionCookie: '', adminData: '' })
}
}
// depolama işlemleri
export const actions = {
nuxtServerInit(store, context) {
// bu kodu kim çalıştırıyor?
console.log('nuxtServerInit, client=', process.client, 'serveur=', process.server, 'env=', context.env)
// oturum başlatma
initStore(store, context)
}
}
function initStore(store, context) {
// başlatılacak depo
// oturum alınır
const session = context.app.$session()
// oturum daha önce başlatılmış mı?
if (!session.value.initStoreDone) {
// yeni bir depo başlatılıyor
console.log("nuxtServerInit, initialisation d'un nouveau store")
// depolama alanı oturuma ekleniyor
session.value.store = store.state
// depolama alanı artık başlatıldı
session.value.initStoreDone = true
} else {
console.log("nuxtServerInit, reprise d'un store existant")
// depolama alanı, oturumdaki depolama alanıyla güncelleniyor
store.commit('replace', session.value.store)
}
// oturum kaydediliyor
session.save(context)
// günlük
console.log('initStore terminé, store=', store.state)
}
- 3-27. satırlar: [vuejs-22] uygulamasının durumunu ve değişikliklerini alacağız (bkz. [3] belgesi):
// depolama alanı durumu
export const state = () => ({
// jSON oturumu başlatıldı
started: false,
// kullanıcı kimliği doğrulandı
authenticated: false,
// oturum çerezi PHP
phpSessionCookie: '',
// simülasyon listesi
simulations: [],
// son simülasyon numarası
idSimulation: 0,
// katman [métier]
métier: null
})
// perde değişiklikleri
export const mutations = {
// durumun değiştirilmesi
replace(state, newState) {
for (const attr in newState) {
state[attr] = newState[attr]
}
},
// depolamanın sıfırlanması
reset() {
this.commit('replace', { started: false, authenticated: false, phpSessionCookie: '', idSimulation: 0, simulations: [], métier: null })
},
// indeks numaralı satırın silinmesi
deleteSimulation(state, index) {
// eslint-disable-next-line no-console
console.log('mutation deleteSimulation')
// [index] numaralı satır siliniyor
state.simulations.splice(index, 1)
console.log('store simulations', state.simulations)
},
// bir simülasyon ekleniyor
addSimulation(state, simulation) {
// eslint-disable-next-line no-console
console.log('mutation addSimulation')
// simülasyon numarası
state.idSimulation++
simulation.id = state.idSimulation
// simülasyon simülasyon tablosuna ekleniyor
state.simulations.push(simulation)
}
}
- 4. ve 6. satırlar: Daha önce kullanılmış olan özellikleri ekliyoruz;
- 8. satır: PHP oturum çerezini koruyoruz. Bu, istemci ile [nuxt] sunucusunun, vergi hesaplama sunucusuyla aynı PHP oturumuna sahip olması için hayati önem taşır;
- 10. satır: Kullanıcı tarafından yapılan simülasyonların listesi;
- 12. satır: kullanıcı tarafından yapılan son simülasyonun numarası;
- 14. satır: [métier] katmanı;
- satır 30-47: [vuejs-22] projesinin depolama alanında bulunan ve uygulama sayfaları tarafından referans verilen değişiklikler. [vuejs-22] projesinde, simülasyon listesini boşaltan [clear] adlı bir değişiklik vardı. Bu mutasyon eklenmez çünkü halihazırda mevcut olan [reset] mutasyonu bu işi halletmelidir;
- 26-28. satırlar: [reset] mutasyonu, state'in yeni içeriğini dikkate alacak şekilde değiştirilmiştir;
[calcul-impot] sayfası, aşağıdaki [form-calcul-impot] bileşenini kullanır:
<!-- HTML görünümünün tanımlanması -->
<template>
<!-- HTML formu -->
<b-form @submit.prevent="calculerImpot" class="mb-3">
<!-- mavi arka planlı 12 sütunlu mesaj -->
<b-row>
<b-col sm="12">
<b-alert show variant="primary">
<h4>Remplissez le formulaire ci-dessous puis validez-le</h4>
</b-alert>
</b-col>
</b-row>
<!-- form öğeleri -->
<!-- ilk satır -->
<b-form-group label="Etes-vous marié(e) ou pacsé(e) ?">
<!-- 5 sütunlu radyo düğmeleri-->
<b-col sm="5">
<b-form-radio v-model="marié" value="oui">Oui</b-form-radio>
<b-form-radio v-model="marié" value="non">Non</b-form-radio>
</b-col>
</b-form-group>
<!-- ikinci satır -->
<b-form-group label="Nombre d'enfants à charge" label-for="enfants">
<b-form-input id="enfants" v-model="enfants" :state="enfantsValide" type="text" placeholder="Indiquez votre nombre d'enfants"></b-form-input>
<!-- olası hata mesajı -->
<b-form-invalid-feedback :state="enfantsValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
</b-form-group>
<!-- üçüncü satır -->
<b-form-group label="Salaire annuel net imposable" label-for="salaire" description="Arrondissez à l'euro inférieur">
<b-form-input id="salaire" v-model="salaire" :state="salaireValide" type="text" placeholder="Salaire annuel"></b-form-input>
<!-- olası hata mesajı -->
<b-form-invalid-feedback :state="salaireValide">Vous devez saisir un nombre positif ou nul</b-form-invalid-feedback>
</b-form-group>
<!-- dördüncü satır, düğme [submit] -->
<b-col sm="3">
<b-button :disabled="formInvalide" type="submit" variant="primary">Valider</b-button>
</b-col>
</b-form>
</template>
<!-- komut dosyası -->
<script>
export default {
// iç durum
data() {
return {
// evli mi değil mi
marié: 'non',
// çocuk sayısı
enfants: '',
// yıllık maaş
salaire: ''
}
},
// hesaplanan iç durum
computed: {
// formun onaylanması
formInvalide() {
return (
// maaş geçersiz
!this.salaire.match(/^\s*\d+\s*$/) ||
// veya geçersiz çocuk sayısı
!this.enfants.match(/^\s*\d+\s*$/) ||
// veya vergi verileri alınamadı
!this.$métier.taxAdminData
)
},
// maaş doğrulaması
salaireValide() {
// sayısal olmalı ve 0'dan büyük olmalı
return Boolean(this.salaire.match(/^\s*\d+\s*$/) || this.salaire.match(/^\s*$/))
},
// çocuk bilgilerinin doğrulanması
enfantsValide() {
// sayısal olmalı ve 0'dan büyük olmalı
return Boolean(this.enfants.match(/^\s*\d+\s*$/) || this.enfants.match(/^\s*$/))
}
},
// yaşam döngüsü
created() {
// günlük
// eslint-disable-next-line
console.log("FormCalculImpot created");
},
// olay yöneticisi
methods: {
calculerImpot() {
// vergi, [métier] katmanı kullanılarak hesaplanır
const résultat = this.$métier.calculerImpot(this.marié, Number(this.enfants), Number(this.salaire))
// eslint-disable-next-line
console.log("résultat=", résultat);
// sonuç tamamlanır
résultat.marié = this.marié
résultat.enfants = this.enfants
résultat.salaire = this.salaire
// [resultatObtenu] olayını tetiklenir
this.$emit('resultatObtenu', résultat)
}
}
}
</script>
- 65. ve 89. satırlar: [this.$métier] referansı, [this.$métier()] olarak değiştirilmelidir;
Bu düzeltmeler yapıldıktan sonra bir simülasyon denemesi yapılabilir:

Aşağıdaki yanıt alınır:

Günlük dosyalarına bakıldığında:

- [9-10]'te, ilk simülasyonun [store]'te yer aldığı görülüyor;
- [5]'te ise son simülasyon numarasının artırıldığını görüyoruz;
17.6. 5. adım
Simülasyonu gerçekleştirdiğimize göre, [Liste des simulations] bağlantısına tıklayalım. Aşağıdaki sayfa karşımıza çıkar:

[nuxt] istemcisinin yönlendirmesi doğru şekilde gerçekleştirildi. [liste-des-simulations] sayfasının koduna bakalım:
<!-- HTML görünümünün tanımı -->
<template>
<div>
<!-- sayfa düzeni -->
<Layout :left="true" :right="true">
<!-- sağ sütundaki simülasyonlar -->
<template slot="right">
<template v-if="simulations.length == 0">
<!-- simülasyon yok -->
<b-alert show variant="primary">
<h4>Votre liste de simulations est vide</h4>
</b-alert>
</template>
<template v-if="simulations.length != 0">
<!-- simülasyonlar var -->
<b-alert show variant="primary">
<h4>Liste de vos simulations</h4>
</b-alert>
<!-- simülasyon tablosu -->
<b-table :items="simulations" :fields="fields" striped hover responsive>
<template v-slot:cell(action)="data">
<b-button @click="supprimerSimulation(data.index)" variant="link">Supprimer</b-button>
</template>
</b-table>
</template>
</template>
<!-- sol sütundaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
</div>
</template>
<script>
// içe aktarmalar
import Layout from '@/components/layout'
import Menu from '@/components/menu'
export default {
// bileşenler
components: {
Layout,
Menu
},
// iç durum
data() {
return {
// gezinti menüsü seçenekleri
options: [
{
text: "Calcul de l'impôt",
path: '/calcul-impot'
},
{
text: 'Fin de session',
path: '/fin-session'
}
],
// HTML tablosunun parametreleri
fields: [
{ label: '#', anahtar: 'id' },
{ label: 'Marié', key: 'marié' },
{ label: "Nombre d'enfants", key: 'enfants' },
{ label: 'Salaire', key: 'salaire' },
{ label: 'Impôt', key: 'impôt' },
{ label: 'Décôte', key: 'décôte' },
{ label: 'Réduction', key: 'réduction' },
{ label: 'Surcôte', key: 'surcôte' },
{ label: '', key: 'action' }
]
}
},
// hesaplanan dahili durum
computed: {
// Vuex deposundan alınan simülasyon listesi
simulations() {
return this.$store.state.simulations
}
},
// yaşam döngüsü
created() {
// eslint-disable-next-line
console.log("ListeSimulations created");
},
// yöntemler
methods: {
supprimerSimulation(index) {
// eslint-disable-next-line
console.log("supprimerSimulation", index);
// simülasyonun kaldırılması n° [index]
this.$store.commit('deleteSimulation', index)
// oturum kaydediliyor
this.$session.save()
}
}
}
</script>
- 47-56. satırlar: gezinme menüsündeki hedefler doğru;
- 75. satır: mağaza doğru şekilde referanslandırılmıştır;
- 89. satır: bir önceki adımda entegre ettiğimiz [deleteSimulation] mutasyonu kullanılıyor;
- 91. satır: Bu satır, [this.$session().save(this.$nuxt.context)] olarak yeniden yazılmalıdır;
Gerekli değişiklikleri yaptıktan sonra, görüntülenen simülasyonu silmeye çalışıyoruz:

Böylece şu sayfa karşımıza çıkar:

Günlük dosyalarına bakalım:

- [6]'te, simülasyon tablosunun boş olduğu görülüyor;
Şimdi vergi hesaplama formuna geri dönelim:

Şu sayfa karşımıza çıkıyor:

Yani yönlendirme işlemi başarılı oldu.
17.7. 6. adım
Şimdi gezinme menüsündeki [Fin de session] gezinme seçeneğini yönetmemiz gerekiyor:
// menü seçenekleri
options: [
{
text: 'Liste des simulations',
path: '/liste-des-simulations'
},
{
text: 'Fin de session',
path: '/fin-session'
}
]
- 9. satırda, [/fin-session] sayfası mevcut değil. [vuejs-22] projesi, bu durumu [router.js] dosyasındaki yönlendirme kurallarıyla yönetiyordu:
// ithalatlar
import Vue from 'vue'
import VueRouter from 'vue-router'
// görünümler
import Authentification from './views/Authentification'
import CalculImpot from './views/CalculImpot'
import ListeSimulations from './views/ListeSimulations'
import NotFound from './views/NotFound'
// oturum
import session from './session'
// yönlendirme eklentisi
Vue.use(VueRouter)
// uygulama yolları
const routes = [
// kimlik doğrulama
{ path: '/', name: 'authentification', component: Authentification },
{ path: '/authentification', name: 'authentification2', component: Authentification },
// vergi hesaplaması
{
path: '/calcul-impot', name: 'calculImpot', component: CalculImpot,
meta: { authenticated: true }
},
// simülasyon listesi
{
path: '/liste-des-simulations', name: 'listeSimulations', component: ListeSimulations,
meta: { authenticated: true }
},
// oturum sonlandırma
{
path: '/fin-session', name: 'finSession'
},
// bilinmeyen sayfa
{
path: '*', name: 'notFound', component: NotFound,
},
]
// yönlendirici
const router = new VueRouter({
// yollar
routes,
// URL görüntüleme modu
mode: 'history',
// uygulamanın temel URL'i
base: '/client-vuejs-impot/'
})
// yolların doğrulanması
router.beforeEach((to, from, next) => {
// eslint-disable-next-line no-console
console.log("router to=", to, "from=", from);
// kimliği doğrulanmış kullanıcılara özel rota mı?
if (to.meta.authenticated && !session.authenticated) {
next({
// kimlik doğrulamaya geçiliyor
name: 'authentification',
})
// olay döngüsüne geri dönülüyor
return;
}
// oturum sonu özel durumu
if (to.name === "finSession") {
// oturum temizleniyor
session.clear();
// [authentification] görünümüne geçiliyor
next({
name: 'authentification',
})
// olay döngüsüne geri dönülür
return;
}
// diğer durumlar - normal yönlendirme görünümüne geç
next();
})
// yönlendiricinin dışa aktarımı
export default router
- 64-76. satırlar, [/fin-session] yoluna giden özel durumu yönetiyordu;
- 66. satır: geçerli oturum boşaltılır;
- 68-70. satırlar: [authentification] görünümü görüntülenir;
[nuxt] istemcisinin yönlendirme dosyasında da benzer bir işlem yapmaya çalışacağız:

[client/routing.js] komut dosyası şu şekilde olur:
/* eslint-disable no-console */
export default function(context) {
// bu kodu kim çalıştırıyor?
console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
// tarayıcıdaki PHP oturum çerezinin yönetimi
// Tarayıcıdaki PHP oturum çerezi, Nuxt oturumunda bulunanla aynı olmalıdır
// [fin-session] eylemi, yeni bir PHP çerezi alır (hem sunucu hem de Nuxt istemcisi)
// Eğer çerezi alan sunucuysa, istemci bunu tarayıcıya iletmelidir
// sunucuyla kendi iletişimi için PHP
// burada bir istemci yönlendirmesi söz konusudur
// oturum çerezini alıyoruz PHP
const phpSessionCookie = context.store.state.phpSessionCookie
if (phpSessionCookie) {
// varsa, PHP oturum çerezini tarayıcıya atanır
document.cookie = phpSessionCookie
}
// Nereye gidiyoruz?
const to = context.route.path
if (to === '/fin-session') {
// oturumu temizliyoruz
const session = context.app.$session()
session.reset(context)
// ana sayfaya yönlendiriliyor
context.redirect({ name: 'index' })
}
}
- Mevcut koda [19-27] satırlarını ekledik;
- 20. satır: mevcut yolun hedefinden [path] alınır;
- 21. satır: [/fin-session] olup olmadığına bakılır. Eğer öyleyse:
- 23-24. satırlar: oturum sıfırlanır;
- satır 26: [nuxt] istemcisi ana sayfaya yönlendirilir;
Oturumun [session.reset(context)] yöntemi (satır 24) şöyledir:
// oturumu sıfırla
reset(context) {
console.log('nuxt-session reset')
// depolama alanını sıfırla
context.store.commit('reset')
// yeni depoyu oturuma kaydediyoruz ve oturumu kaydediyoruz
this.save(context)
}
[context.store.commit('reset')] yöntemi (satır 5) şöyledir:
// depolama alanını sıfırla
reset() {
this.commit('replace', { started: false, authenticated: false, phpSessionCookie: '', idSimulation: 0, simulations: [], métier: null })
}
Şimdi [Fin de session] bağlantısı kullanıldığında, ana sayfa aşağıdaki günlük kayıtlarıyla görüntülenir:

- [3]'te, artık oturum açılmamış olduğu görülüyor;
- [4]'te, jSON oturumunun başlatıldığı görülüyor;
- [6]'te, [métier] katmanı artık depoda mevcut değildir ([this.$métier()]'in bulunduğu sayfalarda hâlâ mevcuttur);
- [5, 7]'te artık simülasyon kalmamıştır;
Bir oturumun sona ermesi sırasında neler olduğunu iyi anlamak gerekir:
- [nuxt] oturumu sıfırlanır: mağazanın [started] özelliği [false] olarak değişir;
- [index] sayfasına yönlendirme yapılır;
- [index] sayfasındaki [mounted] yöntemi yürütülür. Bu yöntem, vergi hesaplama sunucusuyla yeni bir jSON oturumu başlatır. İşlem başarılı olursa, deponun [started] özelliği [true] olarak güncellenir;
17.8. 7. adım
Bu aşamada, [nuxt-20] uygulaması, [vuejs-22] uygulamasının tüm işlevlerine sahiptir. Taşıma işlemi tamamlanmış gibi görünüyor.
[nuxt] yaklaşımıyla biraz daha ileri gideceğiz. [index] sayfasındaki [mounted] yöntemi sorun yaratıyor. Bu yöntem, bir arama motorunun sonunu beklemeyeceği bir asenkron işlem başlatıyor. Bu durumda, asenkron işlemi bir [asyncData] işlevine yerleştirmek gerektiği bilinmektedir; çünkü o zaman, işlemi yürüten [nuxt] sunucusu, sayfayı arama motoruna teslim etmeden önce işlemin tamamlanmasını bekler.
Burada, [index] sayfası için [nuxt-12] uygulamasında yazılmış olan [asyncData] işlevinden yararlanıyoruz:
export default {
name: 'InitSession',
// kullanılan bileşenler
components: {
Layout,
Navigation
},
// asenkron veriler
async asyncData(context) {
// günlük
console.log('[index asyncData started]')
// sayfa daha önce istenmişse işlem iki kez yapılmaz
if (process.server && context.store.state.jsonSessionStarted) {
console.log('[index asyncData canceled]')
return { result: '[succès]' }
}
try {
// bir oturum başlatılır jSON
const dao = context.app.$dao()
const response = await dao.initSession()
// günlük
console.log('[index asyncData response=]', response)
// sonraki istekler için PHP oturum çerezini alınır
const phpSessionCookie = dao.getPhpSessionCookie()
// PHP oturum çerezini [nuxt] oturumunda saklanıyor
context.store.commit('replace', { phpSessionCookie })
// Bir hata mı oluştu?
if (response.état !== 700) {
// hata response.réponse'te bulunuyor
throw new Error(response.réponse)
}
// jSON oturumunun başladığı belirtiliyor
context.store.commit('replace', { jsonSessionStarted: true })
// sonuç veriliyor
return { result: '[succès]' }
} catch (e) {
// günlüğe kaydediliyor
console.log('[index asyncData error=]', e)
// jSON oturumunun başlamadığı kaydedilir
context.store.commit('replace', { jsonSessionStarted: false })
// hata bildiriliyor
return { result: '[échec]', showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// depolama alanı kaydedilir
const session = context.app.$session()
session.save(context)
// günlük
console.log('[index asyncData finished]')
}
},
// yaşam döngüsü
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
console.log('[index beforeMount]')
},
mounted() {
console.log('[index mounted]')
// sadece istemci
if (this.showErrorLoading) {
console.log('[index mounted, showErrorLoading=true]')
this.$eventBus().$emit('errorLoading', true, this.errorLoadingMessage)
}
}
- 13., 33. ve 40. satırlar: [jsonSessionStarted] özelliğini [started] olarak değiştirmeliyiz;
- 13. satır: [nuxt-12] uygulamasında, yalnızca [nuxt] sunucusu [index] sayfasını ve [asyncData] işlevini çalıştırıyordu. [nuxt] istemcisi, [index] sayfasını ancak [nuxt] sunucusundan aldıktan sonra çalıştırıyordu ve bu durumda [asyncData] işlevini çalıştırmıyordu. [nuxt-20]'te durum farklıdır: [Fin de session] bağlantısı, [index] sayfasını [nuxt] istemcisi ortamında görüntüler. Bunun üzerine [asyncData] işlevi çalıştırılacaktır. Ancak, bu şekilde [index] sayfasına ulaşıldığında, bu arada [nuxt] oturumu sıfırlanmış olacaktır ve deponun [started] özelliği [false] değerine sahip olacaktır; dolayısıyla 13. satırdaki koşul mutlaka yanlış olacaktır. Dolayısıyla [process.server]'i olduğu gibi bırakabiliriz; böylece [nuxt] istemcisi bu testi yapmayacaktır;
- 15., 35. ve 42. satırlar: [result] özelliği, [index] sayfasının [data] özelliklerine eklenir. [nuxt-20]'te bu özellik kullanılmayacaktır ve işlevin döndürdüğü sonuçtan bunu kaldıracağız;
- 61-67. satırlar: [mounted] yöntemi, [nuxt] istemcisinin hata mesajını görüntülemesini sağlayan yöntem olduğu için korunmalıdır. Ancak hatanın işlenme şekli değiştirilecektir;
Mevcut [index] sayfasında, eski [mounted] fonksiyonunun yerine yukarıdaki [asyncData] fonksiyonunu entegre ediyoruz ve yeni bir [mounted] fonksiyonu ekliyoruz. [nuxt-20] örneğindeki [index] sayfasının kodu şu şekilde olur:
...
<!-- görünüm dinamiği -->
<script>
/* eslint-disable no-console */
import Layout from '@/components/layout'
export default {
// kullanılan bileşenler
components: {
Layout
},
// bileşen durumu
data() {
return {
// kullanıcı
user: '',
// şifresi
password: '',
// hata gösterimi
showError: false
}
},
// hesaplanan özellikler
computed: {
// geçerli girdiler
valid() {
return this.user && this.password && this.$store.state.started
}
},
// asenkron veriler
async asyncData(context) {
// günlük
console.log('[index asyncData started]')
// sayfa daha önce istenmişse işlem iki kez yapılmaz
if (process.server && context.store.state.started) {
console.log('[index asyncData canceled]')
return
}
try {
// bir oturum başlatılıyor jSON
const dao = context.app.$dao()
const response = await dao.initSession()
// günlük
console.log('[index asyncData response=]', response)
// sonraki istekler için PHP oturum çerezini alınır
const phpSessionCookie = dao.getPhpSessionCookie()
// PHP oturum çerezini [nuxt] oturumunda saklanıyor
context.store.commit('replace', { phpSessionCookie })
// Bir hata mı oluştu?
if (response.état !== 700) {
// hata response.réponse'te bulunuyor
throw new Error(response.réponse)
}
// jSON oturumunun başladığı belirtilmiştir
context.store.commit('replace', { started: true })
// sonuç yok
return
} catch (e) {
// günlük
console.log('[index asyncData error=]', e.message)
// jSON oturumunun başlamadığı kaydedilir
context.store.commit('replace', { started: false })
// hata bildiriliyor
return { showErrorLoading: true, errorLoadingMessage: e.message }
} finally {
// depolama alanı kaydediliyor
const session = context.app.$session()
session.save(context)
// günlük
console.log('[index asyncData finished]')
}
},
// yaşam döngüsü
beforeCreate() {
console.log('[index beforeCreate]')
},
created() {
console.log('[index created]')
},
beforeMount() {
// sadece istemci
console.log('[index beforeMount]')
// olası hatanın yönetimi
if (this.showErrorLoading) {
// günlük
console.log('[index beforeMount, showErrorLoading=true]')
// hata ana bileşene iletilir [default]
this.$emit('error', new Error(this.errorLoadingMessage))
}
},
mounted() {
console.log('[index mounted]')
},
// olay yöneticileri
methods: {
// ----------- kimlik doğrulama
async login() {
...
}
</script>
- 58. ve 65. satırlar: [asyncData] işlevi artık burada [result] özelliğini kullanılmayan hale getirmiyor;
- 81. satır: [nuxt] istemcisinin [beforeMount] yöntemi. Bu yöntem, [asyncData]'in olası hatasını yönetmek için [mounted] yöntemine tercih edilmiştir;
- satır 85: [errorLoading] özelliğinin ayarlanıp ayarlanmadığı kontrol edilir. Bu özellik yalnızca [asyncData] işlevi tarafından ayarlanabilir;
- 85-90. satırlar: [asyncData] işlevi bir hata bildirmişse, bu hata [error] olayı aracılığıyla [default] sayfasına aktarılır. Az önce değiştirdiğimiz eski [created] işlevi, olası hataları bu şekilde yönetiyordu;
Birkaç test yapalım.
Öncelikle, varsa [nuxt] oturum çerezini ve PHP oturum çerezini siliyoruz. Ardından, vergi hesaplama sunucusu çalışır durumda değilken [http://localhost:81/nuxt-20/] sayfasını çağırıyoruz. Aşağıdaki sayfayı görüyoruz:

Vergi hesaplama sunucusunu başlattıktan sonra aynı sayfayı yeniden yüklüyoruz:

Günlük dosyalarına bakalım:

- [2-3]'te, jSON oturumunun başlatıldığını görüyoruz;
- [4]'te, [nuxt] sunucusunun vergi hesaplama sunucusuyla yaptığı iletişim sırasında aldığı PHP oturum çerezi görülüyor. [nuxt] istemcisi bundan sonra bu çerezi kullanacaktır;
Şimdi kimliğimizi doğrulayalım:

Aşağıdaki sayfayı görüyoruz:

[1]'te bir hata mesajı aldık. Bu, tarayıcının bir önceki adımda [nuxt] sunucusu tarafından başlatılan PHP oturumu için doğru çerezi göndermediği anlamına gelir. [nuxt-12]'te, PHP oturum çerezinin [nuxt] sunucusundan [nuxt] istemcisine aktarımı, [middleware/client/routing] betiğinin [nuxt] istemcisi yönlendirmesinde gerçekleştiriliyordu :
/* eslint-disable no-console */
export default function(context) { // bu kodu kim çalıştırıyor?
console.log('[middleware client], process.server', process.server, ', process.client=', process.client)
// tarayıcıdaki PHP oturum çerezinin yönetimi
// tarayıcıdaki PHP oturum çerezi, nuxt oturumunda bulunanla aynı olmalıdır
// [fin-session] eylemi, yeni bir PHP çerezi alır (hem sunucu hem de Nuxt istemcisi)
// Eğer çerezi alan sunucuysa, istemci bunu tarayıcıya iletmelidir
// sunucuyla kendi iletişimi için PHP
// burada bir istemci yönlendirmesi söz konusudur
// oturum çerezini alıyoruz PHP
const phpSessionCookie = context.store.state.phpSessionCookie
if (phpSessionCookie) {
// varsa, PHP oturum çerezini tarayıcıya atanır
document.cookie = phpSessionCookie
}
// Nereye gidiyoruz?
const to = context.route.path
if (to === '/fin-session') {
// oturumu temizlenir
const session = context.app.$session()
session.reset(context)
// ana sayfaya yönlendirilir
context.redirect({ name: 'index' })
}
}
[nuxt] istemcisinin, [nuxt] sunucusundaki PHP oturum çerezini almasını sağlayan satırlar 13-17'dir.
Buradaki sorun, [Valider] düğmesine tıklandığında [nuxt] istemcisine yönlendirme yapılmamasıdır. Dolayısıyla, bu istemcinin yönlendirme işlevi çağrılmamaktadır. Bu sorunu, [index] sayfasının kimlik doğrulama yönteminin başına 12-17. satırları kopyalayarak çözebiliriz:
// olay yöneticileri
methods: {
// ----------- kimlik doğrulama
async login() {
// depolama alanında PHP oturum çerezini alıyoruz
const phpSessionCookie = this.$store.state.phpSessionCookie
if (phpSessionCookie) {
// varsa, PHP oturum çerezi tarayıcıya atanır
document.cookie = phpSessionCookie
}
try {
// bekleme başlıyor
this.$emit('loading', true)
// henüz kimlik doğrulaması yapılmamıştır
5-10. satırlarda, [nuxt] sunucusu tarafından başlatılan PHP oturum çerezini çerez tablosundan alıyoruz. Bu değişiklik yapıldıktan sonra, vergi hesaplama sayfasını başarıyla alıyoruz; bu da kimlik doğrulamanın başarılı olduğu anlamına geliyor.
17.9. 8. adım
[nuxt] felsefesine uygun olarak çalışan işlevsel bir uygulamamız var. [nuxt-13] uygulamasında yaptığımız gibi, şimdi de [nuxt] sunucusundaki gezinme yapısına odaklanacağız. Daha önce de belirtildiği gibi, kullanıcının uygulamanın URL adreslerini elle yazması gerekmez. Kullanıcıya sunulan ve o sırada SPA modunda çalışan [nuxt] istemcisi tarafından yürütülen bağlantıları kullanması beklenir. Bununla birlikte, [nuxt] sunucusundaki gezinme işleminin uygulamayı her zaman kararlı bir durumda bırakmasını sağlayacağız.
[nuxt-13] için yapılan çalışmadan (bkz. bağlantı paragrafı), şunları yapmamız gerektiğini biliyoruz:
- [midleware/routing] komut dosyasını değiştirmek;
- bir [middleware/server/routing] komut dosyası eklemek;

[middleware/routing] komut dosyası şu şekilde değiştirilir:
/* eslint-disable no-console */
// sunucu ve istemci middleware'lerini içe aktarıyoruz
import serverRouting from './server/routing'
import clientRouting from './client/routing'
export default function(context) {
// Bu kodu kim çalıştırıyor?
console.log('[middleware], process.server', process.server, ', process.client=', process.client)
if (process.server) {
// sunucu yönlendirmesi
serverRouting(context)
} else {
// müşteri yönlendirmesi
clientRouting(context)
}
}
- 4. satır: [nuxt] sunucusunun yönlendirme komut dosyası içe aktarılır;
- 10-12. satırlar: kodu çalıştıran sunucu [nuxt] ise, bu sunucunun yönlendirme işlevi kullanılır;
[middleware/server/routing] komut dosyası şöyledir:
/* eslint-disable no-console */
export default function(context) {
// bu kodu kim çalıştırıyor?
console.log('[middleware server], process.server', process.server, ', process.client=', process.client)
// buradan ve oradan bazı bilgiler topluyoruz
const store = context.store
// nereden geliyoruz?
const from = store.state.from || 'nowhere'
// Nereye gidiyoruz?
let to = context.route.name
// [name] özniteliği olmayan /fin-session özel durumu
if (context.route.path === '/fin-session') {
to = 'fin-session'
}
// olası yönlendirme
let redirection = ''
// yönlendirme yönetimi tamamlandı
let done = false
// [nuxt] sunucusundan bir yönlendirmenin içinde miyiz?
if (store.state.serverRedirection) {
// yapılacak bir şey yok
done = true
}
// Bu bir sayfa yenileme mi?
if (!done && from === to) {
// yapılacak bir şey yok
done = true
}
// [nuxt] sunucusunun gezinme kontrolü
// müşterinin gezinme menüsüne uyarlanıyor
// önce oturum sonu durumunu işliyoruz
if (!done && store.state.started && store.state.authenticated && to === 'fin-session') {
// oturumu temizlenir
const session = context.app.$session()
session.reset(context)
// ana sayfaya yönlendirme yapılır
redirection = 'index'
// işlem tamamlandı
done = true
}
// PHP oturumu başlatılmamışsa
if (!done && !store.state.started && to !== 'index') {
// [index] sayfasına yönlendirme
redirection = 'index'
// iş tamamlandı
done = true
}
// kullanıcının kimliği doğrulanmamışsa
if (!done && store.state.started && !store.state.authenticated && to !== 'index') {
redirection = 'index'
// işlem tamamlandı
done = true
}
// [adminData] alınamadığı durum
if (!done && store.state.started && store.state.authenticated && !store.state.métier.taxAdminData && to !== 'index') {
// [index]'e yönlendirme
redirection = 'index'
// iş tamamlandı
done = true
}
// [adminData] elde edildiğinde
if (
!done &&
store.state.started &&
store.state.authenticated &&
store.state.métier.taxAdminData &&
to !== 'calcul-impot' &&
to !== 'liste-des-simulations'
) {
// aynı sayfada kalınıyor
redirection = from
// iş tamamlandı
done = true
}
// normalde tüm kontroller yapıldı ---------------------
// yönlendirme mi?
if (redirection) {
// yönlendirme mağazada kaydedildi
store.commit('replace', { serverRedirection: true })
} else {
// yönlendirme yok
store.commit('replace', { serverRedirection: false, from: to })
}
// depolama alanı oturuma kaydediliyor [nuxt]
const session = context.app.$session()
session.value.store = store.state
session.save(context)
// sunucudan olası yönlendirme yapılır [nuxt]
if (redirection) {
context.redirect({ name: redirection })
}
}
- Bu komut dosyasında, [nuxt-13] uygulamasının [nuxt] sunucusunun yönlendirmesinde daha önce geliştirilen ve kullanılan fikirleri tekrar kullanıyoruz;
- uygulamanın depolama alanına iki özellik ekliyoruz:
- [from]: en son görüntülenen sayfanın adı. [nuxt] istemcisinin bu bilgiye sahip olduğu biliniyor, ancak [nuxt] sunucusu bu bilgiye sahip değil. [nuxt] sunucusundaki her yönlendirmede, görüntülenecek sayfanın adını depoya kaydederek bu bilgiyi sunucuya ekleyeceğiz. Aynı işlemi, [nuxt] istemcisinin her yönlendirmesinde de yapacağız. Böylece, [nuxt] sunucusunun bir sonraki yönlendirmesinde, bu sunucu depoda uygulama tarafından en son görüntülenen sayfanın adını bulacaktır;
- [serverRedirection]: Bir yönlendirme hedefi [nuxt] sunucusu tarafından reddedildiğinde, bu sunucu bir yönlendirme işlemi gerçekleştirecektir. Ardından depoda, [nuxt] sunucusunun bir sonraki hedefinin bir yönlendirme sayfası olduğunu belirtecektir. Bu yönlendirme, [nuxt] sunucusunun yönlendiricisinin yeniden çalıştırılmasına neden olacaktır. Bu sunucu, mevcut hedefin bir yönlendirmeden geldiğini tespit ederse, işlemi devam ettirecektir;
- 6-11. satırlar: yönlendirme için gerekli bilgiler alınır;
- 13-16. satırlar: [/fin-session] hedefi, [fin-session] adlı bir sayfayla ilişkilendirilmemiştir. Dolayısıyla bir adı yoktur. Ona bir ad verilir;
- 19. satır: Olası bir yönlendirmenin hedefi;
- 21. satır: Yönlendirme testleri tamamlandığında [done=true];
- 23-27. satırlar: daha önce de belirtildiği gibi, devam eden yönlendirme bir yönlendirmeden kaynaklanıyorsa, yapılacak bir şey yoktur. Nitekim, önceki yönlendirme sırasında yönlendirici, istemci tarayıcısını yönlendirmek gerektiğine karar vermiştir. Bu kararı yeniden gözden geçirmeye gerek yoktur;
- satır 29-33: Sayfanın yeniden yüklenmesi söz konusuysa, işlem devam etmesine izin verilir. Bu, tüm uygulamalar için geçerli bir kural değildir: Her sayfa için yeniden yüklemenin etkilerini ayrı ayrı incelemek gerekir. Bu durumda, [index, calcul-impot, liste-des-simulations] sayfalarının yeniden yüklenmesinin istenmeyen sonuçlara yol açmadığı görülmektedir;
- 35-85. satırlar: [nuxt] sunucusunun yönlendirmesi, [nuxt] istemcisinin yönlendirmesini devralır. Bir sayfadayken, [nuxt] sunucusunun yönlendirmesi, o sayfadayken [nuxt] istemcisi tarafından sunulan gezinme menüsünü yansıtmalıdır;
- 38-47. satırlar: Öncelikle, mevcut bir sayfaya karşılık gelmeyen [fin-session] hedef durumu ele alınır. Koşullar sağlanmışsa (oturum başlatılmış, kullanıcı kimliği doğrulanmış), oturum temizlenir ve kullanıcı [index] sayfasına yönlendirilir;
- 49-55. satırlar: Vergi hesaplama sunucusuyla jSON oturumu başlatılmamışsa, tek olası hedef [index] sayfasıdır;
- satır 57-62: jSON oturumu başlatılmışsa ve kullanıcı kimliği doğrulanmamışsa ve kimlik doğrulama sayfasını talep etmemişse, kullanıcı kimlik doğrulama sayfasına, yani [index] sayfasına yönlendirilir;
- 64-70. satırlar: Kullanıcı kimliği doğrulanmışsa ancak [adminData] verisi alınmamışsa, kimlik doğrulama sayfasına yönlendirilir. Kimlik doğrulama iki şey yapar: kullanıcıyı doğrular ve kimlik doğrulama başarılı olursa [adminData] verisini de ister. Bu veri alınmamışsa kimlik doğrulama işlemi yeniden başlatılmalıdır;
- 72-85. satırlar: [adminData] verisi elde edildiğinde, olası tek hedefler [calcul-impot] ve [liste-des-simulations]'tir. Aksi takdirde yönlendirme reddedilir;
- satır 88-95: yönlendirme olup olmayacağına göre depo güncellenir;
- 94. satır: Yönlendirme yoktur. Dolayısıyla mevcut [to], bir sonraki yönlendirmede [from] olacaktır;
- satır 96-99: depodaki bilgiler, [nuxt] oturum çerezine kaydedilir;
- satır 100-103: Yönlendirme yapılması gerekiyorsa, bu işlem gerçekleştirilir;
Testleri yapmak için, vergi hesaplama sunucusundaki [nuxt] oturum çerezini ve PHP oturum çerezini silerek temiz bir durumdan başlamaya özen gösterilmelidir:

[nuxt] sunucusunun yönlendirme işlevini test etmek için, her sayfada olası tüm URL ve [/, /calcul-impot, /liste-des-simulations] değerlerini deneyin. Her seferinde uygulama tutarlı bir durumda kalmalıdır.
17.10. 9. adım
- adım, [nuxt-20] uygulamasının dağıtımını içerir. Bu adım, [nuxt] sunucusunu çalıştırmak için [node.js] ortamı sunan bir barındırma hizmeti gerektirir. Bende böyle bir barındırma hizmeti yok. Okuyucu, bağlantı paragrafında açıklanan prosedürleri izleyerek [nuxt-20] uygulamasını geliştirme makinesine dağıtabilir ve HTTPS protokolüyle güvenliğini sağlayabilir.
17.11. Conclusion
[vuejs-22] uygulamasının [nuxt-20] uygulamasına taşınması artık tamamlanmıştır. Bu taşıma işlemiyle ilgili birkaç noktayı hatırlayalım:
- [vuejs-22] sayfaları korunmuştur;
- [vuejs-22] sayfalarında bulunan asenkron işlemler, [asyncData] işlevine taşınmıştır;
- [nuxt-20]'te iki varlık yönetilmesi gerekti: [nuxt] istemcisi ve [nuxt] sunucusu. Bu son varlık, [vuejs-22]'te mevcut değildi. İki varlık arasında tutarlılığı sağlamak için [nuxt] oturumuna ihtiyacımız oldu;
- [nuxt] sunucusunun yönlendirmesini yönetmemiz gerekti;
Uygulamada, [vue.js] mimarisini oluşturup daha sonra bunu [nuxt] ortamına taşımaktansa, doğrudan [nuxt] mimarisiyle başlamak şüphesiz daha tercih edilebilir bir seçenektir.