19. Vue.js istemcisinde yapılan iyileştirmeler
19.1. Introduction
[vuejs-21] projesini geliştirme sunucusuyla test edeceğiz. Bu nedenle, sunucunun CORS başlıklarını göndermesi gerekecek. Bu nedenle, vergi hesaplama sunucusunun 14. sürümündeki [config.json] dosyasının bu başlıkları desteklemesi gerekir:

[vuejs-21] projesi, başlangıçta [vuejs-20] projesinin kopyalanmasıyla oluşturulmuştur. Daha sonra [3] olarak değiştirilmiştir.
Yeni dosyalar ortaya çıkar:
- [session.js]: mevcut oturumla ilgili bilgileri içerecek olan [session] nesnesini dışa aktarır;
- [pluginSession]: önceki [session] nesnesini, görünümlerin [$session] özelliğinde kullanılabilir hale getirir;
- [NotFound.vue]: Kullanıcı, mevcut olmayan bir URL nesnesini manuel olarak talep ettiğinde görüntülenen yeni bir görünüm;
Aşağıdaki dosyalar değiştirilecektir:
- [main.js]: mevcut oturumu başlatacak ve kullanıcı URL'i manuel olarak girdiğinde oturumu geri yükleyecektir;
- [router.js]: Kullanıcı tarafından girilen URL durumlarını işlemek için denetimler eklenir;
- [store.js]: Yeni bir değişiklik eklenir;
- [config.js]: yeni bir yapılandırma eklenmiştir;
- uygulamanın işleyişi sırasında önemli anlarda mevcut oturumu kaydetmek amacıyla çeşitli görünümler eklenmiştir. Bu oturum, kullanıcı URL kodlarını manuel olarak her girdiğinde geri yüklenir;
19.2. [Vuex] mağazası
[./store] komut dosyası şu şekilde gelişir:
// Vuex eklentisi
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
// Vuex deposu
const store = new Vuex.Store({
state: {
// simülasyon tablosu
simulations: [],
// son simülasyon numarası
idSimulation: 0
},
mutations: {
// indeks numaralı satırın silinmesi
deleteSimulation(state, index) {
...
},
// simülasyon ekleme
addSimulation(state, simulation) {
...
},
// durum temizleme
clear(state) {
// simülasyon kalmadı
state.simulations = [];
// simülasyon numaralandırması 0'dan yeniden başlıyor
state.idSimulation = 0;
}
}
});
// [store] nesnesinin dışa aktarımı
export default store;
- 24-29. satırlar: [clear] değişikliği, kaydedilmiş simülasyon listesini siler ve son simülasyon numarasını 0'a sıfırlar.
19.3. Oturum
Bir oturumun gerekli olmasının nedeni, kullanıcının tarayıcının adres çubuğuna bir URL yazdığında, [main.js] komut dosyasının yeniden çalıştırılmasıdır. Oysa bu komut dosyası şu talimatı içerir:
// Vuex deposu
import store from './store'
Bu komut, aşağıdaki [./store] dosyasını içe aktarır:
// Vuex eklentisi
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
// Vuex deposu
const store = new Vuex.Store({
state: {
// simülasyon tablosu
simulations: [],
// son simülasyon numarası
idSimulation: 0
},
mutations: {
...
}
});
// [store] nesnesinin dışa aktarımı
export default store;
7-13. satırlarda, boş bir simülasyon tablosunun içe aktarıldığı görülmektedir. Dolayısıyla, kullanıcı tarayıcının adres çubuğuna URL yazmadan önce simülasyonlar varsa, sonrasında bunlar kaybolur. Buradaki fikir şudur:
- kullanıcı URL kodunu manuel olarak girdiğinde, saklamak istediğimiz bilgileri depolayacak bir oturum kullanmak;
- uygulamanın önemli anlarında bu bilgileri kaydetmek;
- URL manuel olarak girildiğinde her zaman çalıştırılan [main.js]'te bu bilgileri geri yüklemek;
[./session] komut dosyası şu şekildedir:
// Vuex mağazasını içe aktarıyoruz
import store from './store'
// yapılandırma içe aktarılıyor
import config from './config';
// [session] nesnesi
const session = {
// oturum başlatıldı
started: false,
// kimlik doğrulama
authenticated: false,
// kaydetme saati
saveTime: "",
// katman [métier]
métier: null,
// Vuex durumu
state: null,
// oturumun bir dizede kaydedilmesi jSON
save() {
// oturuma bazı özellikler ekleniyor
this.saveTime = Date.now();
this.state = store.state;
// jSON'e dönüştürülür
const json = JSON.stringify(this);
// tarayıcıda saklanıyor
localStorage.setItem("session", json);
// eslint-disable-next-line no-console
console.log("session save", json);
},
// oturumun geri yüklenmesi
restore() {
// tarayıcıdan jSON oturumunu alıyoruz
const json = localStorage.getItem("session")
// eğer bir şey alınmışsa
if (json) {
// oturumun tüm anahtarları geri yüklenir
const restore = JSON.parse(json);
for (var key in restore) {
if (restore.hasOwnProperty(key)) {
this[key] = restore[key];
}
}
// oturumun başlangıcından itibaren belirli bir süre boyunca hiçbir işlem yapılmamışsa, sıfırdan başlanır
let durée = Date.now() - this.saveTime;
if (durée > config.duréeSession) {
// oturumu boşaltırız - oturum yine de kaydedilir
session.clear();
} else {
// Vuex deposu yeniden oluşturulur
store.replaceState(JSON.parse(JSON.stringify(this.state)));
}
}
// eslint-disable-next-line no-console
console.log("session restore", this);
},
// oturumu temizliyoruz
clear() {
// eslint-disable-next-line no-console
console.log("session clear");
// oturumun belirli alanları sıfırlanıyor
this.authenticated = false;
this.saveTime = "";
this.started = false;
if (this.métier) {
// [taxAdminData] alanını sıfırlıyoruz
this.métier.taxAdminData = null;
}
// Vuex deposu da temizlenir
store.commit("clear");
// yeni oturum kaydediliyor
this.save();
},
}
// [session] nesnesinin dışa aktarılması
export default session;
Açıklamalar
- 2. satır: oturum, [Vuex] depolama alanını da kapsayacaktır (simülasyon listesi, yapılan son simülasyonun numarası);
- 7-17. satırlar: oturum tarafından saklanan bilgiler:
- [started]: Sunucu ile jSON oturumu başlatıldı mı, başlatılmadı mı;
- [authenticated]: Kullanıcının kimlik doğrulaması yapıp yapmadığı;
- [saveTime]: son yedeklemenin milisaniye cinsinden tarihi;
- [métier]: [métier] katmanına ilişkin bir referans. Bu referans, vergi hesaplamasına olanak tanıyan [taxAdminData] verisini içerir;
- [state]: [Vuex] deposunun durumu (simülasyon listesi, yapılan son simülasyonun numarası);
- 20-30. satırlar: [save] yöntemi, oturumu uygulamayı çalıştıran tarayıcıda yerel olarak kaydeder;
- 22. satır: Kaydetme saati kaydedilir;
- satır 23: [Vuex] mağazasından [state] alınır;
- 25. satır: oturumun jSON dizesi oluşturulur;
- 27. satır: Bu dize, [session] anahtarıyla ilişkilendirilerek tarayıcıda yerel olarak saklanır;
- 33-57. satırlar: [restore] yöntemi, tarayıcıdaki yerel yedekten bir oturumu geri yüklemeye olanak tanır;
- 35. satır: Yerel jSON yedeklemesi alınır;
- 37. satır: Bir şey yüklendiyse;
- satır 39-44: [session] nesnesi yeniden oluşturulur;
- satır 46: son yedeklemeden bu yana geçen süre hesaplanır;
- satır 47-50: bu süre, yapılandırma ile belirlenen [config.duréeSession] değerinden büyükse, oturum sıfırlanır (satır 49) ve bu sırada yedeklenir;
- satır 52: aksi takdirde, [Vuex] deposunun [state] özniteliği yeniden oluşturulur;
- satır 60-75: [clear] yöntemi oturumu sıfırlar;
- satır 64-70: oturum özellikleri başlangıç değerlerine sıfırlanır;
- 72. satır: [Vuex] depolama alanı da sıfırlanır;
- 74. satır: yeni oturum kaydedilir;
19.4. [config] yapılandırma dosyası
[./config] dosyası şu şekilde değişir:
// [axios] kütüphanesi kullanılıyor
const axios = require('axios');
// istek zaman aşımı HTTP
axios.defaults.timeout = 2000;
...
// yapılandırmanın dışa aktarılması
export default {
// [axios] nesnesi
axios: axios,
// oturumun maksimum hareketsizlik süresi: 5 dakika = 300 saniye = 300000 milisaniye
duréeSession: 300000
}
- 12. satır: Uygulama oturumunu, bir web oturumunu yönettiğimiz gibi yöneteceğiz. Burada maksimum 5 dakikalık bir hareketsizlik süresi belirliyoruz;
19.5. [pluginSession] eklentisi
Daha önce birçok kez yapıldığı gibi, [pluginSession] eklentisi, görünümlerin [this.$session] özelliği aracılığıyla oturuma erişmesine olanak sağlayacaktır:
export default {
install(Vue, session) {
// görünüm sınıfına [$session] özelliğini ekler
Object.defineProperty(Vue.prototype, '$session', {
// Vue.$session referanslandığında, ikinci parametre [session] olarak ayarlanır
get: () => session,
})
}
}
19.6. Ana komut dosyası [main]
Ana komut dosyası [./main.js] şu şekilde güncellenmiştir:
// başlangıç günlüğü
// eslint-disable-next-line no-console
console.log("main started");
// ithalatlar
import Vue from 'vue'
...
// katman örneklendirme [métier]
import Métier from './couches/Métier';
const métier = new Métier();
// eklenti [métier]
import pluginMétier from './plugins/pluginMétier'
Vue.use(pluginMétier, métier)
// Vuex depolama
import store from './store'
// oturum
import session from './session';
import pluginSession from './plugins/pluginSession'
Vue.use(pluginSession, session)
// yeniden başlatmadan önce oturum geri yüklenir
session.restore();
// [métier] katmanı geri yükleniyor
if (session.métier && session.métier.taxAdminData) {
métier.setTaxAdminData(session.métier.taxAdminData);
}
// UI'in başlatılması
new Vue({
el: '#uygulama,
// yönlendirici
router: router,
// Vuex panosu
store: store,
// ana ekran
render: h => h(Main),
})
// son kayıt
// eslint-disable-next-line no-console
console.log("main terminated, session=", session);
- 19. satır: oturum içe aktarılır;
- 20. satır: eklentisi içe aktarılır;
- 21. satır: [pluginSession] eklentisi, [Vue]'e entegre edilir. Bu komuttan sonra tüm görünümler, [$session] özniteliğinde oturuma sahip olur;
- 27. satır: oturum geri yüklenir. 11. satırda içe aktarılan oturum, son yedeklemesinin içeriğiyle başlatılır;
- 16. satırdan sonra, görünümler 12. satırda başlatılan [$métier] özelliğine sahip olur. Bu özellik, vergiyi hesaplamaya olanak tanıyan [taxAdminData] bilgisini içermez;
- 30-32. satırlar: Az önce yapılan geri yükleme işlemi [session.métier.taxAdminData] özelliğini geri yüklediyse, görünümlerin [$métier] özelliği bu değerle başlatılır;
19.7. [router] yönlendirme dosyası
[./router] yönlendirme dosyası şu şekilde değişir:
// 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 rotaları
const routes = [
// kimlik doğrulama
{ path: '/', name: 'authentification', component: Authentification },
{ path: '/authentification', name: 'authentification', 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 yol mu?
if (to.meta.authenticated && !session.authenticated) {
next({
// kimlik doğrulamaya geçiliyor
name: 'authentification',
})
// olay döngüsüne geri dönüş
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 sonraki görünümü
next();
})
// yönlendiricinin dışa aktarımı
export default router
Açıklamalar
- 16-38. satırlar: Bazı rotalar ek bilgilerle zenginleştirilmiştir;
- 19. satır: [Authentification] manzarasına giden yeni bir rota oluşturulmuştur;
- 21-24. satırlar: [CalculImpot] görünümüne giden yol artık [meta] özelliğine sahiptir (bu ad zorunludur). Bu nesnenin içeriği herhangi bir şey olabilir ve geliştirici tarafından belirlenir;
- 23. satır: [meta] nesnesine [authenticated] özelliğini ekliyoruz (bu ad herhangi bir şey olabilir). Bu, bizim için [CalculImpot] görünümüne gitmek için kullanıcının kimlik doğrulamasından geçmesi gerektiği anlamına gelir;
- 26-29. satırlar: [ListeSimulations] görünümüne giden yol için de aynısını yapıyoruz. Burada da kullanıcının kimlik doğrulaması yapılmış olması gerekir;
- [meta.authenticated] özelliği, [CalculImpot, ListeSimulations] görünümlerinin URL adreslerini manuel olarak giren bir kullanıcının, kimlik doğrulaması yapılmamışsa bu adreslere erişemeyeceğini kontrol etmemizi sağlayacaktır;
- 51-76. satırlar: [beforeEach] yöntemi, bir görünüm yönlendirilmeden önce yürütülür. Bu, doğrulamaları yapmak için uygun bir zamandır;
- [to]: Hiçbir işlem yapılmazsa bir sonraki yönlendirme;
- [from]: en son görüntülenen rota;
- [next]: görüntülenecek bir sonraki rotayı değiştirmeye yarayan işlev;
- 55. satır: Bir sonraki rotanın kullanıcının kimlik doğrulamasını gerektirip gerektirmediğine bakılır;
- satır 56-59: eğer öyleyse ve kullanıcı kimliği doğrulanmamışsa, bir sonraki rota [Authentification] görünümüne değiştirilir;
- satır 64-73: satır 30-32'deki [finSession] rotasının özel durumu işlenir. Bu rotanın ilişkili bir görünümü yoktur;
- satır 66: oturum, başlangıç değerine sıfırlanır;
- satır 68-70: [Authentification] görünümünü bir sonraki görünüm olarak programlıyoruz;
- satır 75: Yukarıdaki iki durumdan biri geçerli değilse, yönlendirme dosyasında belirtilen rotaya geçilir;
- satır 35-37: Kullanıcı tarafından girilen rota bilinen hiçbir rotaya uymuyorsa, [NotFound] görünümü hazırlanır. Bu görünüm 8. satırda içe aktarılır. Rotalar, rota dosyasında belirtilen sırayla kontrol edilir. Dolayısıyla, 36. satıra gelinmişse, bu, istenen rotanın 18-33. satırlardaki rotalardan hiçbiri olmadığı anlamına gelir;
19.8. [NotFound] görünümü
Kullanıcı tarafından girilen rota bilinen hiçbir rotaya uymuyorsa [NotFound] görünümü görüntülenir:

Görünümün kodu şöyledir:
<!-- HTML görünüm tanımı -->
<template>
<!-- sayfa düzeni -->
<Layout :left="true" :right="true">
<!-- sağ sütundaki uyarı -->
<template slot="right">
<!-- sarı arka planlı mesaj -->
<b-alert show variant="danger" align="center">
<h4>Cette page n'existe pas</h4>
</b-alert>
</template>
<!-- sol sütundaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
</template>
<script>
// ithalatlar
import Layout from "./Layout";
import Menu from "./Menu";
export default {
// bileşenler
components: {
Layout,
Menu
},
// bileşenin dahili durumu
data() {
return {
// gezinme menüsü seçenekleri
options: [
{
text: "Authentification",
path: "/"
}
]
};
},
// yaşam döngüsü
created() {
// eslint-disable-next-line
console.log("NotFound created");
// hangi menü seçeneklerinin sunulacağına bakıyoruz
if (this.$session.authenticated && this.$métier.taxAdminData) {
// kullanıcı simülasyonlar yapabilir
Array.prototype.push.apply(this.options, [
{
text: "Calcul de l'impôt",
path: "/calcul-impot"
},
{
text: "Liste des simulations",
path: "/liste-des-simulations"
}
]);
}
}
};
</script>
Açıklamalar
- 4. satır: yönlendirilmiş görünümlerin iki sütununu kullanır;
- 6-11. satırlar: bir hata mesajı;
- 13. satır: gezinme menüsü sol sütunu kaplıyor;
- 31-36. satırlar: menünün varsayılan seçenekleri;
- satır 40-57: görünüm oluşturulduğunda yürütülen kod;
- 44. satır: Kullanıcının simülasyon yapıp yapamayacağına bakılır;
- 45-55. satırlar: eğer yapabiliyorsa, gezinme menüsüne iki seçenek eklenir; bu seçenekler için oturum açılmış olması ve [métier] katmanının çalışır durumda olması gerekir (46-55. satırlar);
19.9. [Authentification] görünümü
[Authentification] görünümü şu şekilde değişir:
<!-- görünümün HTML tanımı -->
<template>
<Layout :left="false" :right="true">
...
</Layout>
</template>
<!-- görünümün dinamikleri -->
<script>
import Layout from "./Layout";
export default {
// bileşenin durumu
data() {
return {
// kullanıcı
user: "",
// şifresi
password: "",
// hata mesajının görüntülenmesini kontrol eder
showError: false,
// hata mesajı
message: ""
};
},
// kullanılan bileşenler
components: {
Layout
},
// hesaplanan özellikler
computed: {
// geçerli girdiler
valid() {
return this.user && this.password && this.$session.started;
}
},
// 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.$session.authenticated = false;
// sunucuda engelleyici kimlik doğrulama
const response = await this.$dao.authentifierUtilisateur(
this.user,
this.password
);
// yükleme bitti
this.$emit("loading", false);
// sunucunun 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.$session.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 askıya alındı
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 {
// oturum güncellenir
this.$session.métier = this.$métier;
// oturum kaydediliyor
this.$session.save();
}
}
},
// yaşam döngüsü: bileşen az önce oluşturuldu
created() {
// eslint-disable-next-line
console.log("Authentification created");
// kullanıcı simülasyon yapabilir mi?
if (
this.$session.started &&
this.$session.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.$session.started) {
// bekleme başlar
this.$emit("loading", true);
// sunucu ile oturum başlatılır - 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.$session.started = true;
})
// hata durumunda
.catch(error => {
// hata, [Main] görünümüne iletilir
this.$emit("error", error);
})
// her durumda
.finally(() => {
// oturum kaydedilir
this.$session.save();
});
}
}
};
</script>
Açıklamalar
- [Vue.js] istemci sürümüne eklenen oturumu kullanan komutlar sarı renkle vurgulanmıştır;
- 97. ve 148. satırlar: [login, created] yöntemlerinin sonunda, bu yöntemlerde gerçekleştirilen HTTP sorgularının sonucu ne olursa olsun oturum kaydedilir (her iki durumda da [finally] cümlesi);
- 102-150. satırlardaki [created] yöntemi, [Authentification] görünümü her oluşturulduğunda yürütülür. Görünümün URL kodunu kullanıcı girmişse, oturum ne yapılması gerektiğini bize gösterecektir;
- 106-115. satırlar: jSON oturumu başlatılmışsa, kullanıcı kimliği doğrulanmışsa ve [this.$métier.taxAdminData] verisi başlatılmışsa, kullanıcı doğrudan vergi hesaplama formuna (112. satır) gidebilir;
- satır 117: [created] yöntemi, önceki sürümde sunucu ile bir jSON oturumu başlatmak için kullanılıyordu. Bu aşama, daha önce gerçekleştirilmişse gereksizdir;
- satır 42-66: kimlik doğrulama yöntemi;
- satır 66: kimlik doğrulama başarılı olursa, bu durum oturuma kaydedilir;
- 67-92. satırlar: vergi idaresi verileri için sunucuya yapılan istek ([taxAdminData]);
- 95. satır: Bu aşamanın sonunda, işlemin başarılı olup olmadığına bakılmaksızın oturumun [métier] özelliği güncellenir;
19.10. [CalculImpot] görünümü
[CalculImpot] görünümünün kodu şu şekilde değişir:
<!-- HTML görünümünün tanımı -->
<template>
...
</template>
<script>
// ithalatlar
import FormCalculImpot from "./FormCalculImpot";
import Menu from "./Menu";
import Layout from "./Layout";
export default {
// iç 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
};
},
// kullanılan bileşenler
components: {
Layout,
FormCalculImpot,
Menu
},
// olay yönetim yöntemleri
methods: {
// vergi hesaplama sonucu
handleResultatObtenu(résultat) {
// sonuç, HTML dizisi olarak oluşturulur
...
// bir simülasyon +
this.$store.commit("addSimulation", résultat);
// oturum kaydediliyor
this.$session.save();
}
},
// yaşam döngüsü
created() {
// eslint-disable-next-line
console.log("CalculImpot created");
}
};
</script>
Açıklamalar
- 45. satır: Hesaplanan simülasyon, [Vuex] depolama alanına eklenir. Bu, depolama alanının [state] özelliğini içeren oturumu etkiler. Bu nedenle oturum kaydedilir (47. satır);
- 51. satır: Günlüklerde görünüm oluşturma işlemlerini takip etmek için [created] yöntemi oluşturulur;
19.11. [ListeSimulations] görünümü
[ListeSimulations] görünümü şu şekilde gelişir:
<!-- HTML görünümünün tanımı -->
<template>
...
</div>
</template>
<script>
// ithalatlar
import Layout from "./Layout";
import Menu from "./Menu";
export default {
// bileşenler
components: {
Layout,
Menu
},
// iç durum
data() {
...
},
// hesaplanan iç durum
computed: {
// Vuex deposundan alınan simülasyon listesi
simulations() {
return this.$store.state.simulations;
}
},
// yöntemler
methods: {
supprimerSimulation(index) {
// eslint-disable-next-line
console.log("supprimerSimulation", index);
// [index] numaralı simülasyonun silinmesi
this.$store.commit("deleteSimulation", index);
// oturum kaydediliyor
this.$session.save();
}
},
// yaşam döngüsü
created() {
// eslint-disable-next-line
console.log("ListeSimulations created");
}
};
</script>
Açıklamalar
- 36. satır: 34. satırdaki bir simülasyonun silinmesinden sonra, bu durum değişikliğini yansıtmak için oturum kaydedilir;
- 40-43. satırlar: Görünümlerin oluşturulmasını takip etmeye devam ediyoruz;
19.12. Projenin çalıştırılması

Testler sırasında aşağıdaki noktaları kontrol edin:
- kullanıcı, gezinme menüsündeki bağlantılar ve eylem düğmeleri/bağlantıları aracılığıyla uygulamayı "kullanıyorsa", uygulama çalışıyor mu;
- kullanıcı URL kodlarını manuel olarak girerse, uygulama çalışmaya devam ediyor. Özellikle aşağıdaki testi yapın:
- simülasyonlar yapın;
- [ListeSimulations] görünümüne geldikten sonra, görünümü yeniden yükleyin (F5). Önceki uygulamada ([vuejs-20]), bu durumda simülasyonlar kayboluyordu. Burada durum böyle değildir: daha önce yapılmış simülasyonlar aynen korunur;
- anlamak için loglara bakın:
- [main] komut dosyasının ne zaman çalıştırıldığını kontrol edin. Kullanıcının URL'i elle her girdiğinde komut dosyasının çalıştırıldığını görmelisiniz;
- görünümlerin ne zaman oluşturulduğunu. Bunların, görüntülenecekleri her seferinde oluşturulduğunu görmelisiniz;
- yönlendirmenin nasıl çalıştığını. Her yönlendirmeden önce, size şunları gösteren bir günlük kaydı oluşturulur:
- geldiğiniz yol;
- gideceğiniz yol;
19.13. Uygulamanın yerel bir sunucuya dağıtılması
Alıştırma olarak, |Déploiement sur un serveur local| paragrafını takip ederek [vuejs-21] projesini yerel Laragon sunucusuna dağıtın. Ardından test edin.
19.14. Mobil sürümün geliştirilmesi
Teorik olarak, Bootstrap kullanımı sayesinde akıllı telefon, tablet, dizüstü ve masaüstü bilgisayarlar gibi farklı cihazlarda kullanılabilir bir uygulamaya sahip olmamız gerekir. Bu cihazları birbirinden ayıran şey ekran boyutlarıdır.
[vuejs-21] sürümünü bir mobil cihazda test ettiğimizde, görünümlerin görüntülenmesinde bir kaos olduğu görülür. [vuejs-22] sürümü bu sorunu giderir. Değişikliklerin tamamı görünüm şablonlarında yapılmıştır. Bu değişiklikler temel olarak bir akıllı telefon ekranı için uygun bir görüntüleme ayarlamasından ibarettir. Bu ayar yapıldığında, Bootstrap sayesinde daha büyük ekranlarda görüntüleme sorunsuz bir şekilde gerçekleşir.

19.14.1. [Main] görünümü
[Main] görünümü şu şekilde değişiyor:
<!-- HTML görünümünün tanımı -->
<template>
<div class="container">
<b-card>
<!-- jumbotron -->
<b-jumbotron>
<b-row>
<b-col sm="4">
<img src="../assets/logo.jpg" alt="Cerisier en fleurs" />
</b-col>
<b-col sm="8">
<h1>Calculez votre impôt</h1>
</b-col>
</b-row>
</b-jumbotron>
....
</b-card>
</div>
</template>
Açıklamalar
- 8. satır: [cols=’4’] yazan yere [sm=’4’] yazılır. [sm], [small] anlamına gelir. Akıllı telefon ekranları bu kategoriye girer. Diğer kategoriler şunlardır: [xs=extra small, md=medium, lg=large, xl=extra large];
- 11. satır: aynı;
19.14.2. [Layout] görünümü
[Layout] görünümü şu şekilde değişir:
<!-- yönlendirilmiş görünümün mizanpajının HTML tanımı -->
<template>
<!-- satır -->
<div>
<b-row>
<!-- sol tarafta üç sütunlu alan -->
<b-col sm="3" v-if="left">
<slot name="left" />
</b-col>
<!-- sağdaki dokuz sütunlu alan -->
<b-col sm="9" v-if="right">
<slot name="right" />
</b-col>
</b-row>
</div>
</template>
19.14.3. [Authentification] görünümü
[Authentification] görünümü şu şekilde değişir:
<!-- 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" description="Tapez admin">
<!-- kullanıcı giriş alanı -->
<b-col sm="6">
<b-form-input type="text" id="user" placeholder="Nom d'utilisateur" v-model="user" />
</b-col>
</b-form-group>
<!-- 2. satır -->
<b-form-group label="Mot de passe" label-for="password" description="Tapez admin">
<!-- şifre giriş alanı -->
<b-col sm="6">
<b-input type="password" id="password" placeholder="Mot de passe" v-model="password" />
</b-col>
</b-form-group>
<!-- 3. satır -->
<b-alert
show
variant="danger"
v-if="showError"
class="mt-3"
>L'erreur suivante s'est produite : {{message}}</b-alert>
<!-- 3. satırda [submit] tipi düğme -->
<b-row>
<b-col sm="2">
<b-button variant="primary" type="submit" :disabled="!valid">Valider</b-button>
</b-col>
</b-row>
</b-form>
</template>
</Layout>
</template>
Açıklamalar
- 11. ve 19. satırlar: Giriş etiketinin sütun sayısını belirleyen [label-cols] özniteliği kaldırılmıştır. Bu özniteliğin olmaması durumunda, etiket giriş alanının üstünde yer alır. Bu, akıllı telefon ekranları için daha uygundur;
19.14.4. [CalculImpot] görünümü
[CalculImpot] görünümü şu şekilde değiştirilmiştir:
<!-- 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ı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>
19.14.5. [FormCalculImpot] görünümü
[FormCalculImpot] görünümü şu şekilde değişir:
<!-- HTML görünüm tanımı -->
<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
type="text"
id="enfants"
placeholder="Indiquez votre nombre d'enfants"
v-model="enfants"
:state="enfantsValide"
></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
type="text"
id="salaire"
placeholder="Salaire annuel"
v-model="salaire"
:state="salaireValide"
></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 type="submit" variant="primary" :disabled="formInvalide">Valider</b-button>
</b-col>
</b-form>
</template>
Açıklamalar
- 15, 23, 35. satırlar: [label-cols] özniteliği kaldırılmıştır;
Ayrıca, geçerlilik testleri de şu şekilde değiştirilmiştir:
...
// hesaplanan dahili durum
computed: {
// formun onaylanması
formInvalide() {
return (
// geçersiz maaş
!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ı >=0
return Boolean(
this.enfants.match(/^\s*\d+\s*$/) || this.enfants.match(/^\s*$/)
);
}
},
...
Yorumlar
- 19. satır: Hiçbir şey girilmediğinde, giriş geçerli kabul edilir. Bu, sayfa ilk açıldığında geçerli bir girişin olmasını sağlar. Önceki sürümde, giriş başlangıçta hatalı olarak görünüyordu;
- 26. satır: aynı şekilde;
- 5-14. satırlar: Onay düğmesi, her iki alana da bir şeyler girilmiş ve bu girdiler geçerli ise etkin hale gelir;
19.14.6. [Menu] görünümü
[Menu] görünümü şu şekilde değişir:
<!-- HTML görünümünün tanımı -->
<template>
<b-card class="mb-3">
<!-- dikey Bootstrap menüsü -->
<b-nav vertical>
<!-- menü seçenekleri -->
<b-nav-item
v-for="(option,index) of options"
:key="index"
:to="option.path"
exact
exact-active-class="active"
>{{option.text}}</b-nav-item>
</b-nav>
</b-card>
</template>
Açıklamalar
- 3. satır: Menüyü ince bir çerçeveyle çevrelemek için <b-card> etiketi eklenir. Bu, menünün akıllı telefonda daha iyi konumlandırılmasını sağlar;
19.14.7. [ListeSimulations] görünümü
[ListeSimulations] görünümü değişmeden kalır:
<!-- HTML görünüm 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 striped hover responsive :items="simulations" :fields="fields">
<template v-slot:cell(action)="data">
<b-button variant="link" @click="supprimerSimulation(data.index)">Supprimer</b-button>
</template>
</b-table>
</template>
</template>
<!-- sol sütundaki gezinme menüsü -->
<Menu slot="left" :options="options" />
</Layout>
</div>
</template>
Açıklamalar
- 20. satır: Tablonun ekran boyutuna göre uyarlanmasını sağlayan [responsive] özniteliğine dikkat edin:

- [2]'te ise küçük ekranlarda, tabloyu görüntülemek için yatay bir kaydırma çubuğu kullanılır;
19.14.8. [NotFound] görünümü
Değişmez.
19.14.9. Mobil cihazlardaki görünümler


Not: Mobil cihazlara daha uygun görünümler elde etmenin kesinlikle bir yolu vardır. Özellikle gezinme menüsünün iyileştirilebileceğini düşünüyorum, ancak başka noktalar da var. Bu belgenin birincil amacı bir mobil uygulama oluşturmak değildi. Öyle olsaydı, belki de Ionic |https://ionicframework.com/| gibi bir çerçeveye yönelirdik.