---
title: 'Web Görünümü Tabanlı Yaklaşım'
source: 'https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/web-gorunumu-tabanli-yaklasim'
course: 'Çapraz Platform Yaklaşımları'
language: tr
updated: '2026-10-21T05:02:50+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Web görünümü tabanlı yaklaşımın sayfa, kabuk ve ileti olmak üzere üç parçasını açıklar'
    - 'Sayfanın liste satırını çizen işlevin neden iki platformda ortak çıktığını ölçer'
    - 'Sayfanın kabuktan okuduğu güvenli alan payının paylaşımı ortaktan dallıya çevirdiğini gösterir'
    - 'Paylaşımın sayfa kodunu değil kabuğun verdiği girdiyi izlediğini ayırt eder'
---

# Web Görünümü Tabanlı Yaklaşım

Web görünümü tabanlı yaklaşım arayüzü tek bir sayfadan çizer ve sayfayı her platformda bir yerel kabuğun içine yerleştirir. Ders bu düzeneğin paylaşımını ölçer ve sayfanın kabuktan aldığı girdinin paylaşımı nasıl değiştirdiğini gösterir.

Önceki ders dört paylaşımı tanıttı ve bir özelliğin kodunun iki platformda nasıl durduğunu sınıflamayı öğretti. Bu ders ilk yaklaşımı o sınıflamadan geçirir: web görünümü tabanlı yaklaşım. Kursun sorusu değişmez — **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** — ve ders bu soruyu bir düzeneğe sorar.

Web görünümü, bir yerel uygulamanın içine yerleştirilen ve bir web sayfasını gösteren görünümdür. Uygulamanın arayüzü bu sayfadır: liste, form, düğme sayfanın içinde çizilir. Sayfayı çevreleyen yerel parçaya kabuk denir. Kabuk uygulamanın açılışını, pencereyi, uygulama mağazasına giden paketi ve sayfayla konuşan kanalı taşır. Bu düzeneğe melez uygulama da denir. Sayfa bir kez yazılır ve iki platformda yüklenir; kabuk her platformda ayrı yazılır ama ince tutulur.

Bu düzenek paylaşımın büyük kısmını sayfaya yığar. Sayfadaki her işlev iki platformda aynı işlevdir, çünkü iki kabuk da aynı dosyayı yükler. Dersin ölçüsü bunun nerede bozulduğunu bulmaktır.

## Sayfanın Kendi Kodu

Bir liste satırını çizen işlevle başlayalım. Satır bir başlık, bir sayı ve bir sıra taşır; işlev bunlardan sayfaya yazılacak bir satır üretir. Aynı sayfa bir de satıra dokunulduğunda kabuğa gönderilecek iletiyi kurar. İki işlev de yalnız girdilerine bakar, platformu okumaz.

**YA1.** Model, bu paylaşımın kuralını kurar; model web görünümünü ya da bir sayfayı çalıştırmaz. Modelde sayfa kodu iki platformda aynı işlev nesnesidir ve satır, bir başlık, bir sayı, bir sıra ve bir sabitlenmiş bayrağı olan düz bir nesnedir.

```javascript
// sharing.mjs — bir ozelligin kodunun platformlar arasinda nasil paylasildigini siniflandirir
export const PLATFORMS = ["ios", "android"];

export class Sharing {
  constructor(label, impl, samples) {
    this.label = label;
    this.total = samples.length;
    this.missing = PLATFORMS.filter((p) => typeof impl[p] !== "function");
    const present = PLATFORMS.filter((p) => !this.missing.includes(p));
    this.code = new Set(present.map((p) => impl[p])).size;
    if (this.missing.length > 0) {
      this.differ = null;
      this.verdict = "tek";
      return;
    }
    this.differ = samples.filter((s) => {
      const outs = PLATFORMS.map((p) => JSON.stringify(impl[p](s, p)));
      return new Set(outs).size > 1;
    }).length;
    if (this.code > 1) this.verdict = "ikiz";
    else this.verdict = this.differ === 0 ? "ortak" : "dalli";
  }

  line() {
    const detail = this.differ === null
      ? `eksik=${this.missing.join(",")}`
      : `fark=${this.differ}/${this.total}`;
    console.log(`${this.label}: kod=${this.code} | ${detail} -> ${this.verdict}`);
  }
}

export function countSharing(items) {
  for (const name of ["ortak", "dalli", "ikiz", "tek"]) {
    console.log(`${name}: ${items.filter((s) => s.verdict === name).length}`);
  }
}
```
Aşağıdaki ölçüm iki özelliği dört satırla sınar.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

function renderRow(row) {
  return `<li>${row.title} (${row.count})</li>`;
}

function encodeMessage(row) {
  return JSON.stringify({ type: "open", id: row.index });
}

const rows = [
  { index: 0, title: "Kahve", count: 2, pinned: false },
  { index: 1, title: "Haftalik plan", count: 7, pinned: true },
  { index: 2, title: "Market listesi", count: 0, pinned: false },
  { index: 3, title: "Yillik abonelik", count: 12, pinned: false },
];

const features = [
  new Sharing("satir-cizimi", { ios: renderRow, android: renderRow }, rows),
  new Sharing("ileti-bicimi", { ios: encodeMessage, android: encodeMessage }, rows),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
satir-cizimi: kod=1 | fark=0/4 -> ortak
ileti-bicimi: kod=1 | fark=0/4 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0
```

İki özellik de ortak çıktı. `kod=1` iki kabuğun aynı işlevi çalıştırdığını, `fark=0/4` dört satırın ikisinde de aynı çizildiğini söyler. Bu, yaklaşımın vaadinin en saf halidir: satırın biçimi bir yerde yazılır, bir yerde sınanır ve iki platforma birlikte gider. Bir hata düzeltmesi iki platformda aynı anda yürürlüğe girer.

Sayfa, kabuğa şöyle ulaşır. Aşağıdaki parça sayfanın üç işlevini gösterir; kabuk iletiyi alan ve güvenli alan payını sayfaya yazan yerel kodu taşır.

```html
<!-- Platform kodu: sayfa -->
<ul id="rows"></ul>
<script>
  function showRows(rows) {
    const items = rows.map((r) => {
      const item = document.createElement("li");
      item.textContent = `${r.title} (${r.count})`;
      return item;
    });
    document.getElementById("rows").replaceChildren(...items);
  }
  function setInsets(top, bottom) {
    document.body.style.paddingTop = `${top}px`;
    document.body.style.paddingBottom = `${bottom}px`;
  }
  function openRow(id) {
    const message = JSON.stringify({ type: "open", id: id });
    (window.webkit?.messageHandlers.host ?? window.Host).postMessage(message);
  }
</script>
```

Bu parça ölçülmez; model sayfanın kendisini çalıştırmaz. Satır başlığı sunucudan gelen veridir ve `textContent` onu işaretleme olarak değil, metin olarak yazar. Köprüye bağlı bir sayfada bu ayrım önemlidir: işaretleme olarak yorumlanan bir başlık sayfada betik çalıştırabilir ve köprü üzerinden kabuğa ileti gönderebilir. Parçanın son satırı ise ders açısından önemli bir ayrıntıyı gösterir: iletiyi kabuğa ulaştırmanın yolu iki platformda farklıdır ve sayfa bunu kendi içinde ayırır. Bu ayrım iletinin içeriğini değil, ulaştırma kanalını ilgilendirir; `ileti-bicimi` ortak kalır, çünkü kuralın baktığı şey ileti metnidir. Kanalın ayrımı ise ölçüm dışında kalır ve bakım bedeli olarak düzyazıda söylenir.

Kabuğun iOS tarafındaki payı sayfaya yazan parçası şöyle görünür.

```swift
// Platform kodu: HostViewController
import WebKit

@MainActor
func pushInsets(to webView: WKWebView) {
    let insets = webView.safeAreaInsets
    webView.evaluateJavaScript("setInsets(\(insets.top), \(insets.bottom))")
}
```

Bu parça da ölçülmez. Gösterdiği şey kabuğun sayfaya sayı verdiğidir; sayfa bu sayıyla ne yapacağını kendisi belirler, burada `setInsets` sayfanın üst ve alt boşluğunu bu paya göre ayarlar.

## Kabuğun Verdiği Girdi

Sayfa her şeyi kendi bilgisiyle yapamaz. Ekranın çentik ve alt çubuk payı gibi bilgiyi aygıtın penceresi bilir. Web görünümü bu payın bir kısmını sayfaya kendisi aktarabilir; bu modelde ise pay kabuktan gelen bir girdidir. Kabuk bu payı sayfaya verir ve sayfa sabitlenmiş bir satırı bu payın altına yerleştirir.

**YA2.** Güvenli alan payı modelde `shell.mjs` dosyasındaki sabit bir tablodur: iOS için üst pay 47, alt pay 34; Android için üst pay 24, alt pay 0. Bu sayılar modelin girdisidir, bir aygıtın ölçüsü değildir; gerçek pay aygıta ve yönelime göre kabuğun her çağrıda bildirdiği değerdir.

```javascript
// shell.mjs — kabugun sayfaya verdigi guvenli alan payi
export function safeInset(platform) {
  return platform === "ios"
    ? { top: 47, bottom: 34 }
    : { top: 24, bottom: 0 };
}

export function flatInset() {
  return { top: 0, bottom: 0 };
}
```

```javascript
// page.mjs — sayfanin satiri yerlestiren islevi, kabugun payini disaridan alir
export function makePlacer(insetOf) {
  return function placeRow(row, platform) {
    const y = row.pinned ? insetOf(platform).top : row.index * 40;
    return `${row.title}:${y}`;
  };
}
```

`makePlacer` bir kabuk işlevi alır ve satırı yerleştiren işlevi döndürür. Sabitlenmiş satır payın altına oturur, öteki satırlar sırasına göre sıralanır. Aynı sayfa kodunu iki platformda ölçelim.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { safeInset } from "./shell.mjs";
import { makePlacer } from "./page.mjs";

function renderRow(row) {
  return `<li>${row.title} (${row.count})</li>`;
}

const placeRow = makePlacer(safeInset);

const rows = [
  { index: 0, title: "Kahve", count: 2, pinned: false },
  { index: 1, title: "Haftalik plan", count: 7, pinned: true },
  { index: 2, title: "Market listesi", count: 0, pinned: false },
  { index: 3, title: "Yillik abonelik", count: 12, pinned: false },
];

const features = [
  new Sharing("satir-cizimi", { ios: renderRow, android: renderRow }, rows),
  new Sharing("satir-yerlesimi", { ios: placeRow, android: placeRow }, rows),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
satir-cizimi: kod=1 | fark=0/4 -> ortak
satir-yerlesimi: kod=1 | fark=1/4 -> dalli
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

`satir-yerlesimi` dallıdır. `kod=1` olduğu için sayfa kodu iki platformda aynıdır; ama sabitlenmiş tek satırda iki platformun sonucu ayrışır, çünkü kabuk iki platformda farklı pay verir. Öteki üç satır sırasına göre yerleşir ve fark üretmez. Yani dört örnekten yalnız biri dalı tetikledi.

Burada kayda değer olan, dalın sayfanın kodunda bulunmamasıdır. `makePlacer` hiçbir yerde "platform şuysa şunu yap" demez. Platformu kabuğun cevabı taşır ve sayfa onu olduğu gibi kullanır. Paylaşım bu yüzden sayfa kodunu değil, kabuğun verdiği girdiyi izler.

## Yaygın Yanılgılar

Web görünümü tabanlı yaklaşımın etrafında üç yanılgı dolaşır ve ölçüm her birine somut bir karşılık verir.

Birincisi, "tek sayfa tek paylaşım demektir" düşüncesidir. Sayfa kodunun tek olması `kod=1` verir, ama paylaşımın ortak mı dallı mı olduğunu `fark` değeri belirler. Yukarıdaki ölçümde sayfa tekti ve yine de bir özellik dallıydı. Tek kod, özelliğin iki platformda aynı davrandığını garanti etmez; yalnız kodun bir yerde yazıldığını garanti eder.

İkincisi, "kabuk yok sayılabilir" düşüncesidir. Kabuk ince olsa da her platformda ayrı yazılır. Pay okuyan, iletiyi alan ve sayfayı yükleyen kod iki platformda iki ayrı yerel koddur. Kabuğun işlevleri kendi başlarına özellik olarak sınıflansaydı çoğu ikiz çıkardı. Yaklaşımın vaadi bu ikizleri sıfıra indirmek değil, küçültmektir: ikiz sayısı az ve her biri küçük kalır, ama bunu sağlayan kabuğun kendi disiplinidir.

Üçüncüsü, "sayfadan çağrılan her yetenek sayfa gibi ortaktır" düşüncesidir. Sayfa kamera, izin ya da dosya gibi bir yeteneği ileti yoluyla ister; isteği kuran satır ortaktır. Yeteneği karşılayan kod ise kabukta, her platformda ayrı yazılır ve bir platformda hiç bulunmayabilir. Aynı yetenek için sayfadaki çağrı ortak, kabuktaki karşılık ikiz, bir platformda eksikse tek çıkar. Hangi satırı ölçtüğünü söylemeden "bu özellik ortak" demek eksik bir cümledir. Yerel modül ihtiyacı konusu bu ayrımı ayrıca ele alır.

Üç yanılgının ortak yanı, düzeneğin vaadini bir paylaşım adıyla karıştırmaktır. Düzenek kodu bir yerde toplar; kodun her girdide aynı davranması, kabuğun verdiği girdilerin aynı olmasına bağlıdır.

## Sınırlayıcı Ölçüm

Bu çıkarımı sınayalım. Aynı sayfa kodunu, aynı dört satırı ve aynı iki platformu alıyoruz; yalnız kabuğu değiştiriyoruz. İkinci kabuk iki platformda da sıfır pay verir.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { safeInset, flatInset } from "./shell.mjs";
import { makePlacer } from "./page.mjs";

const withInset = makePlacer(safeInset);
const withoutInset = makePlacer(flatInset);

const rows = [
  { index: 0, title: "Kahve", count: 2, pinned: false },
  { index: 1, title: "Haftalik plan", count: 7, pinned: true },
  { index: 2, title: "Market listesi", count: 0, pinned: false },
  { index: 3, title: "Yillik abonelik", count: 12, pinned: false },
];

const features = [
  new Sharing("yerlesim-payli", { ios: withInset, android: withInset }, rows),
  new Sharing("yerlesim-paysiz", { ios: withoutInset, android: withoutInset }, rows),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
yerlesim-payli: kod=1 | fark=1/4 -> dalli
yerlesim-paysiz: kod=1 | fark=0/4 -> ortak
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

Sayfa kodu değişmedi; `makePlacer` iki ölçümde de aynı işlevdir ve iki ölçümde de `kod=1` çıktı. Kabuk pay verdiğinde dallı, vermediğinde ortak çıktı. İki ölçüm iki ayrı sayfa değildir; aynı sayfanın iki ayrı kabukla ölçülmesidir.

Bu sonuç yaklaşım kararında şu anlama gelir. Web görünümü tabanlı yaklaşımda sayfa kodunu tek tutmak yetmez; kabuğun sayfaya verdiği her girdi paylaşımı etkiler. Güvenli alan payı gibi girdiler arttıkça dallı özellikler artar. Kural bunu gösterir ama bedelini göstermez: bir payı yanlış okumanın bedeli yalnız ilgili aygıtta görünür ve sayfayı sınamak onu yakalamaz.

Yaklaşımın bir başka sınırı da kabuğun kendisidir. Uygulama mağazasına giden paket, izin istekleri ve yerel yetenekler kabukta durur. Bunlar sayfanın dışındaki özellikler olarak ayrı sınıflanır; sonraki derslerde ve platform gerçekleri konusunda yeniden ele alınırlar.

## Özet

- Bu dersin paylaşımı: ortak. Sayfanın liste satırını çizen işlev iki platformda aynı işlevdir ve platformu okumaz.
- Web görünümü tabanlı yaklaşım arayüzü tek sayfadan çizer; kabuk her platformda ayrı, ince bir yerel parçadır.
- Sayfa kabuktan güvenli alan payı okuyunca aynı sayfa kodu dallı çıktı: dört örnekten yalnız sabitlenmiş satır ayrıştı.
- Aynı sayfa kodu pay vermeyen bir kabukla ortak çıktı: paylaşım sayfa kodunu değil, kabuğun verdiği girdiyi izler.
- Kural paylaşımın yapısını görür, bedelini görmez; kabuktan okunan her payın bedeli düzyazıda söylenir.

## Sonraki Adım

Bu derste arayüz tek bir sayfadan çizildi ve sayfa yalnız kabukla konuştu. İkinci düzenek arayüzü sayfada çizmez; ortak kodun ürettiği bir ağacı her platformun yerel bileşenlerine eşler. Sıradaki ders, `Köprü Tabanlı Yaklaşım`, bu eşlemenin paylaşımını ölçer.
