Skip to content
Mithril.js 2
Main Navigation KılavuzAPI

Türkçe

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
čeština
magyar

Türkçe

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
čeština
magyar

Görünüm

Sidebar Navigation

Başlangıç

Kurulum

Basit Uygulama

Kaynaklar

JSX

Eski Tarayıcılarda ES6+

Animasyonlar

Test

Örnekler

Üçüncü Taraf Entegrasyonu

Yol Yönetimi

Temel kavramlar

Sanal DOM Düğümleri

Bileşenler

Yaşam Döngüsü Yöntemleri

Anahtarlar

Otomatik Yeniden Çizim Sistemi

Çeşitli

Framework Karşılaştırması

v1.x'ten v2.x'e Geçiş

v0.2.x'ten Geçiş

API

Bu sayfada

Anahtarlar ​

Anahtarlar Nedir? ​

Anahtarlar, takip edilmesi gereken öğelerin kimliklerini temsil eder. Bunları özel key özelliği ile element, component ve fragment vnode'larına ekleyebilirsiniz ve kullanıldığında şöyle görünürler:

javascript
m('.user', { key: user.id }, [
  /* ... */
]);

Birkaç durumda faydalıdırlar:

  • Model verilerini veya state'li verileri işlerken, yerel durumu doğru alt ağaca bağlamak için anahtarlara ihtiyacınız vardır.
  • CSS kullanarak birden çok bitişik düğümü bağımsız olarak canlandırırken ve bunlardan herhangi birini ayrı ayrı kaldırabiliyorsanız, animasyonların doğru elementlere uygulanması ve beklenmedik atlamaların önlenmesi için anahtarlara ihtiyacınız vardır.
  • Bir alt ağacı zorla yeniden başlatmanız gerektiğinde, bir anahtar eklemeniz ve ardından yeniden başlatmak istediğinizde bu anahtarı değiştirip yeniden render etmeniz gerekir.

Anahtar Kısıtlamaları ​

Önemli: Tüm fragmentler için, çocukları ya yalnızca anahtar niteliğine sahip vnode'lar (anahtarlı fragment) ya da yalnızca anahtar niteliğine sahip olmayan vnode'lar (anahtarsız fragment) içermelidir. Anahtar niteliği, yalnızca özellikleri destekleyen vnode'larda, yani element, component ve fragment vnode'larında bulunabilir. null, undefined ve dizeler gibi diğer vnode'lar herhangi bir özelliğe sahip olamazlar, bu nedenle anahtar niteliğine sahip olamazlar ve bu nedenle anahtarlı fragmentlerde kullanılamazlar.

Bunun anlamı, [m(".foo", {key: 1}), null] ve ["foo", m(".bar", {key: 2})] gibi kullanımlar hatalı olacaktır. Doğru kullanımlar ise [m(".foo", {key: 1}), m(".bar", {key: 2})] ve [m(".foo"), null] şeklindedir. Bunu unutursanız, açıklayıcı ve yardımcı bir hata alırsınız.

Görüntüleme Listelerinde Model Verilerini Bağlama ​

Listeleri, özellikle düzenlenebilir listeleri işlerken, genellikle düzenlenebilir görevler ve benzeri şeylerle uğraşırsınız. Bunların state'leri ve kimlikleri vardır ve Mithril.js'nin bunları izlemesi için ihtiyaç duyduğu bilgileri vermeniz gerekir.

Yorum yapılabilen ve bildirme gibi nedenlerle gizlenebilen gönderilerden oluşan basit bir sosyal medya akışı olduğunu varsayalım.

javascript
// `User` ve `ComposeWindow` kısalık açısından çıkarıldı
function CommentCompose() {
  return {
    view: function (vnode) {
      var post = vnode.attrs.post;
      return m(ComposeWindow, {
        placeholder: 'Yorumunuzu yazın...',
        submit: function (text) {
          return Model.addComment(post, text);
        },
      });
    },
  };
}

function Comment() {
  return {
    view: function (vnode) {
      var comment = vnode.attrs.comment;
      return m(
        '.comment',
        m(User, { user: comment.user }),
        m('.comment-body', comment.text),
        m(
          'a.comment-hide',
          {
            onclick: function () {
              Model.hideComment(comment).then(m.redraw);
            },
          },
          "Bunu beğenmedim"
        )
      );
    },
  };
}

function PostCompose() {
  return {
    view: function (vnode) {
      var comment = vnode.attrs.comment;
      return m(ComposeWindow, {
        placeholder: 'Gönderinizi yazın...',
        submit: Model.createPost,
      });
    },
  };
}

function Post(vnode) {
  var showComments = false;
  var commentsFetched = false;

  return {
    view: function (vnode) {
      var post = vnode.attrs.post;
      var comments = showComments ? Model.getComments(post) : null;
      return m(
        '.post',
        m(User, { user: post.user }),
        m('.post-body', post.text),
        m(
          '.post-meta',
          m(
            'a.post-comment-count',
            {
              onclick: function () {
                if (!showComments && !commentsFetched) {
                  commentsFetched = true;
                  Model.fetchComments(post).then(m.redraw);
                }
                showComments = !showComments;
              },
            },
            post.commentCount,
            ' yorum',
            post.commentCount === 1 ? '' : 's'
          ),
          m(
            'a.post-hide',
            {
              onclick: function () {
                Model.hidePost(post).then(m.redraw);
              },
            },
            "Bunu beğenmedim"
          )
        ),
        showComments
          ? m(
              '.post-comments',
              comments == null
                ? m('.comment-list-loading', 'Yükleniyor...')
                : [
                    m(
                      '.comment-list',
                      comments.map(function (comment) {
                        return m(Comment, { comment: comment });
                      })
                    ),
                    m(CommentCompose, { post: post }),
                  ]
            )
          : null
      );
    },
  };
}

function Feed() {
  Model.fetchPosts().then(m.redraw);
  return {
    view: function () {
      var posts = Model.getPosts();
      return m(
        '.feed',
        m('h1', 'Akış'),
        posts == null
          ? m('.post-list-loading', 'Yükleniyor...')
          : m(
              '.post-view',
              m(PostCompose),
              m(
                '.post-list',
                posts.map(function (post) {
                  return m(Post, { post: post });
                })
              )
            )
      );
    },
  };
}

Bu örnek, birçok işlevi içermektedir. Şimdi iki önemli noktaya odaklanalım:

javascript
// `Feed` bileşeninde
m(
  '.post-list',
  posts.map(function (post) {
    return m(Post, { post: post });
  })
);

// `Post` bileşeninde
m(
  '.comment-list',
  comments.map(function (comment) {
    return m(Comment, { comment: comment });
  })
);

Bunların her biri, Mithril.js'nin hakkında hiçbir fikrinin olmadığı ilişkili state'e sahip bir alt ağaca atıfta bulunur. (Mithril.js yalnızca vnode'lar ile etkileşim kurar.) Bunları anahtarsız bıraktığınızda, beklenmedik sorunlar ortaya çıkabilir. Örneğin, yorumları göstermek için "N yorum" üzerine tıklayın, yorum kutusuna bir şeyler yazın ve ardından yukarıdaki bir gönderide "Bunu beğenmedim" seçeneğine tıklayın. İşte üzerinde deneyebileceğiniz canlı bir demo, mock bir modelle birlikte. (Not: Edge veya IE'deyseniz, bağlantının karma uzunluğu nedeniyle bağlantının hash uzunluğu nedeniyle sorunlarla karşılaşabilirsiniz.)

Ancak, beklenen davranış yerine, uygulama hatalı çalışır: açtığınız yorum listesi kapanır ve yorumların yüklendiği sanılmasına rağmen, sonraki gönderi sürekli olarak "Yükleniyor..." mesajını gösterir. Bunun nedeni, yorumların geç yükleme (lazy loading) ile yüklenmesi ve her seferinde aynı yorumun geçtiğini varsayıyor olmalarıdır.

Bunun nedeni, Mithril.js'nin anahtarsız fragmentleri nasıl işlediğidir: bunları çok basit bir şekilde yinelemeli olarak tek tek işler. Yani bu durumda, fark şöyle görünebilir:

  • Önce: A, B, C, D, E
  • Değiştirilmiş: A, B, C -> D, D -> E, E -> (kaldırıldı)

Ve bileşen aynı kaldığı için (her zaman Comment), yalnızca props değişir ve değiştirilmez.

Bu hatayı düzeltmek için, Mithril.js'nin sorunu düzeltmek için gerekirse state'i potansiyel olarak taşıması gerektiğini bilmesi için basitçe bir anahtar eklersiniz. Bu bağlantıda, sorunun anahtarlar ile çözüldüğü çalışan bir örneği bulabilirsiniz.

javascript
// `Feed` bileşeninde
m(
  '.post-list',
  posts.map(function (post) {
    return m(Post, { key: post.id, post: post });
  })
);

// `Post` bileşeninde
m(
  '.comment-list',
  comments.map(function (comment) {
    return m(Comment, { key: comment.id, comment: comment });
  })
);

Yorumlar için, bu durumda anahtarlara teknik olarak ihtiyaç duyulmasa da, iç içe geçmiş yorumlar veya düzenleme özelliği gibi şeyler eklerseniz benzer şekilde bozulur ve bunlara anahtar eklemeniz gerekirdi.

Animasyonlu Nesne Koleksiyonlarını Sorunsuz Tutma ​

Bazı durumlarda, listelere, kutulara ve benzeri öğelere animasyon uygulamak isteyebilirsiniz. Basit bir örnekle başlayalım:

javascript
var colors = ['red', 'yellow', 'blue', 'gray'];
var counter = 0;

function getColor() {
  var color = colors[counter];
  counter = (counter + 1) % colors.length;
  return color;
}

function Boxes() {
  var boxes = [];

  function add() {
    boxes.push({ color: getColor() });
  }

  function remove(box) {
    var index = boxes.indexOf(box);
    boxes.splice(index, 1);
  }

  return {
    view: function () {
      return [
        m('button', { onclick: add }, 'Kutu ekle, kaldırmak için kutuya tıklayın'),
        m(
          '.container',
          boxes.map(function (box, i) {
            return m(
              '.box',
              {
                'data-color': box.color,
                onclick: function () {
                  remove(box);
                },
              },
              m('.stretch')
            );
          })
        ),
      ];
    },
  };
}

Kod basit görünse de, canlı örneği deneyerek ne olduğunu görebilirsiniz. Birkaç kutu oluşturmak için tıklayın, bir kutu seçin ve boyutunu gözlemleyin. Boyut ve dönüşün, ızgaradaki konuma değil, renk ile belirtilen kutuya bağlı olmasını bekleriz. Ancak, boyutun aniden arttığını, ancak konumla sabit kaldığını fark edeceksiniz. Bunun nedeni, öğelere anahtar vermemiz gerektiğidir.

Bu durumda, öğelere benzersiz anahtar vermek oldukça basittir: her yeni öğe için artırdığınız bir sayaç kullanın.

javascript
var colors = ['red', 'yellow', 'blue', 'gray'];
var counter = 0;

function getColor() {
  var color = colors[counter];
  counter = (counter + 1) % colors.length;
  return color;
}

function Boxes() {
  var boxes = [];
  var nextKey = 0;

  function add() {
    boxes.push({ color: getColor() }); 
    var key = nextKey; 
    nextKey++; 
    boxes.push({ key: key, color: getColor() }); 
  }

  function remove(box) {
    var index = boxes.indexOf(box);
    boxes.splice(index, 1);
  }

  return {
    view: function () {
      return [
        m('button', { onclick: add }, 'Kutu ekle, kaldırmak için kutuya tıklayın'),
        m(
          '.container',
          boxes.map(function (box, i) {
            return m(
              '.box',
              {
                key: box.key, 
                'data-color': box.color,
                onclick: function () {
                  remove(box);
                },
              },
              m('.stretch')
            );
          })
        ),
      ];
    },
  };
}

Düzenlenmiş örneğin nasıl farklı çalıştığını görmek için deneyebilirsiniz.

Tek Alt Öğeli Anahtarlı Parçalarla Görüntüleri Yeniden Başlatma ​

Modellerde ve benzeri durumlarda, durum bilgisi olan varlıklarla çalışırken, model görünümlerini anahtarlarla oluşturmak genellikle faydalıdır. Örneğin, aşağıdaki gibi bir düzeniniz olduğunu varsayalım:

javascript
function Layout() {
  // ...
}

function Person() {
  // ...
}

m.route(rootElem, '/', {
  '/': Home,
  '/person/:id': {
    render: function () {
      return m(Layout, m(Person, { id: m.route.param('id') }));
    },
  },
  // ...
});

Büyük olasılıkla, Person bileşeniniz aşağıdaki gibi bir şeye benzeyecektir:

javascript
function Person(vnode) {
  var personId = vnode.attrs.id;
  var state = 'pending';
  var person, error;

  m.request('/api/person/:id', { params: { id: personId } }).then(
    function (p) {
      person = p;
      state = 'ready';
    },
    function (e) {
      error = e;
      state = 'error';
    }
  );

  return {
    view: function () {
      if (state === 'pending') return m(LoadingIcon);
      if (state === 'error') {
        return error.code === 404
          ? m('.person-missing', 'Kişi bulunamadı.')
          : m('.person-error', 'Bir hata meydana geldi. Lütfen daha sonra tekrar deneyin');
      }
      return m(
        '.person',
        m(
          m.route.Link,
          {
            class: 'person-edit',
            href: '/person/:id/edit',
            params: { id: personId },
          },
          'Değiştir'
        ),
        m('.person-name', 'Ad: ', person.name)
        // ...
      );
    },
  };
}

Şimdi, bu bileşenden diğer kişilere bağlantı vermenin bir yolunu eklediğinizi varsayalım, örneğin bir "yönetici" alanı ekleyerek.

javascript
function Person(vnode) {
  // ...

  return {
    view: function () {
      // ...
      return m(
        '.person',
        m(
          m.route.Link,
          {
            class: 'person-edit',
            href: '/person/:id/edit',
            params: { id: personId },
          },
          'Değiştir'
        ),
        m('.person-name', person.name),
        // ...
        m(
          '.manager',
          'Yönetici: ',
          m(
            m.route.Link,
            {
              href: '/person/:id',
              params: { id: person.manager.id },
            },
            person.manager.name
          )
        )
        // ...
      );
    },
  };
}

Kişinin ID'si 1 ve yöneticinin ID'si 2 olduğunu varsayarsak, /person/1 adresinden /person/2 adresine geçiş yaparsınız ve aynı rotada kalırsınız. Ancak rota çözümleyici render yöntemini kullandığınız için ağaç korunur. Bu nedenle m(Layout, m(Person, {id: "1"})) ifadesinden m(Layout, m(Person, {id: "2"})) ifadesine geçiş yaparsınız. Burada, Person bileşeni değişmediği için yeniden başlatılmaz. Ancak bu durum bizim için sorunlu, çünkü yeni kullanıcı bilgileri yüklenmiyor. İşte anahtarların devreye girdiği yer burasıdır. Düzeltmek için rota çözümleyiciyi şu şekilde değiştirebiliriz:

javascript
m.route(rootElem, '/', {
  '/': Home,
  '/person/:id': {
    render: function () {
      return m(
        Layout,
        // Daha sonra başka öğeler ekleyebilmek için bir dizi içine alın.
        // Unutmayın: parçalar ya sadece anahtarlı alt öğeler içermeli
        // ya da hiç anahtarlı alt öğeler içermemelidir.
        [m(Person, { id: m.route.param('id'), key: m.route.param('id') })]
      );
    },
  },
  // ...
});

Sıkça Yapılan Hatalar ​

Anahtarlar ile ilgili olarak insanların karşılaştığı yaygın hatalar vardır. İşte bunların bazıları, neden işe yaramadıklarını anlamanıza yardımcı olmak için.

Anahtarlı Elementleri Sarmalamak ​

Bu iki kod parçacığı aynı şekilde çalışmaz:

javascript
users.map(function (user) {
  return m('.wrapper', [m(User, { user: user, key: user.id })]);
});

users.map(function (user) {
  return m('.wrapper', { key: user.id }, [m(User, { user: user })]);
});

İlki, anahtarı User bileşenine bağlar, ancak users.map(...) tarafından oluşturulan dış bileşen tamamen anahtarsızdır. Anahtarlı bir elementi bu şekilde sarmalamak işe yaramaz ve sonuç, listenin her değiştirilmesinde fazladan isteklerden, iç form girişlerinin durumunu kaybetmesine kadar çeşitli sorunlara yol açabilir. Ortaya çıkan davranış, gönderi listesinin bozuk örneğine benzer olacaktır, ancak durum bozulması sorunu olmadan.

İkincisi, anahtarı .wrapper elementine bağlar ve dış parçanın anahtarlı olmasını sağlar. Bu, muhtemelen başından beri yapmak istediğiniz şeyi yapar ve bir kullanıcının kaldırılması, diğer kullanıcı örneklerinin durumuyla ilgili herhangi bir soruna neden olmaz.

Anahtarları Bileşenin İçine Koymak ​

Kişi örneğinde, bunun yerine şunu yaptığınızı varsayalım:

javascript
// KAÇININ
function Person(vnode) {
  var personId = vnode.attrs.id;
  // ...

  return {
    view: function () {
      return m.fragment(
        { key: personId }
        // görünümde daha önce sahip olduğunuz şey
      );
    },
  };
}

Bu çalışmaz, çünkü anahtar bileşenin tamamına uygulanmaz. Sadece görünüme uygulanır ve bu nedenle verileri beklediğiniz gibi yeniden yükleyemezsiniz.

Burada kullanılan çözümü tercih edin, anahtarı bileşenin içinde değil, bileşeni kullanarak vnode'a koyun.

javascript
// TERCİH EDİN
return [m(Person, { id: m.route.param('id'), key: m.route.param('id') })];

Gereksiz Yere Elementleri Anahtarlamak ​

Anahtarların kendilerinin tanımlayıcılar olduğu yaygın bir yanılgıdır. Mithril.js, tüm fragment'ler için çocuklarının ya hepsinin anahtarları olması ya da hiçbirinin olmaması gerektiğini zorlar ve bunu unutursanız bir hata verir. Şu düzene sahip olduğunuzu varsayalım:

javascript
m('.page', m('.header', { key: 'header' }), m('.body'), m('.footer'));

Bu açıkça bir hata verecektir, çünkü .header'ın bir anahtarı vardır ve .body ve .footer'ın her ikisinde de anahtar yoktur. Ancak mesele şu ki: bunun için anahtarlara ihtiyacınız yok. Kendinizi bunun gibi şeyler için anahtarlar kullanırken bulursanız, çözüm anahtar eklemek değil, onları kaldırmaktır. Sadece gerçekten, gerçekten ihtiyacınız varsa ekleyin. Evet, temel DOM düğümlerinin kimlikleri vardır, ancak Mithril.js'nin bunları doğru şekilde güncellemek için bu kimlikleri izlemesi gerekmez. Pratik olarak asla yapmaz. Anahtarlara yalnızca, her bir öğenin Mithril.js tarafından doğrudan izlenmeyen bir duruma sahip olduğu listelerde ihtiyaç duyulur. Bu durum, bir modelde, bir bileşende veya DOM'un kendisinde saklanabilir.

Bir son not: statik anahtarlardan kaçının. Bunlar her zaman gereksizdir. key özelliğinizi hesaplamıyorsanız, muhtemelen yanlış bir şey yapıyorsunuzdur.

Yalıtılmış olarak tek bir anahtarlı elemente gerçekten ihtiyacınız varsa, tek çocuklu anahtarlı bir parça kullanın. Bu, sadece tek bir çocuğu olan bir dizidir; bu çocuk anahtarlı bir elementtir, örneğin [m("div", {key: foo})].

Anahtar Türlerini Karıştırmak ​

Anahtarlar, nesne özellik adları olarak okunur. Bu, 1 ve "1"'in aynı şekilde ele alındığı anlamına gelir. Sorun yaşamamak için, mümkünse anahtar türlerini karıştırmayın. Karıştırırsanız, yinelenen anahtarlarla ve beklenmedik davranışlarla karşılaşabilirsiniz.

javascript
// KAÇININ
var things = [
  { id: '1', name: 'Kitap' },
  { id: 1, name: 'Bardak' },
];

Kesinlikle yapmanız gerekiyorsa ve bunun üzerinde kontrolünüz yoksa, türünü belirten bir önek kullanın, böylece ayırt edilebilirler.

javascript
things.map(function (thing) {
  return m(
    '.thing',
    { key: typeof thing.id + ':' + thing.id }
    // ...
  );
});

Delikli Anahtarlı Elementleri Gizlemek ​

null, undefined ve boolean'lar gibi "delikler" anahtarsız vnode'lar olarak kabul edilir, bu nedenle aşağıdaki gibi kodlar işe yaramaz:

javascript
// KAÇININ
things.map(function (thing) {
  return shouldShowThing(thing)
    ? m(Thing, { key: thing.id, thing: thing })
    : null;
});

Bunun yerine, listeyi döndürmeden önce filtreleyin; Mithril.js bunu doğru şekilde işleyecektir. Çoğu zaman, Array.prototype.filter tam olarak ihtiyacınız olan şeydir.

javascript
// TERCİH EDİN
things
  .filter(function (thing) {
    return shouldShowThing(thing);
  })
  .map(function (thing) {
    return m(Thing, { key: thing.id, thing: thing });
  });

Yinelenen Anahtarlar ​

Fragment öğeleri için anahtarlar benzersiz olmalıdır; aksi takdirde hangi anahtarın nereye gitmesi gerektiği belirsiz ve muğlaktır. Ayrıca, elementlerin olması gerektiği gibi hareket etmemesiyle ilgili sorunlarınız da olabilir.

javascript
// KAÇININ
var things = [
  { id: '1', name: 'Kitap' },
  { id: '1', name: 'Bardak' },
];

Mithril.js, anahtarlı fragment'leri nasıl düzgün şekilde güncelleyeceğini bilmek için anahtarları dizinlere eşlerken boş bir nesne kullanır. Yinelenen bir anahtarınız olduğunda, o elementin nereye taşındığı artık net değildir ve bu nedenle Mithril.js bu durumda bozulacak ve özellikle liste değiştiyse, güncellemede beklenmedik davranışlar sergileyecektir. Mithril.js'nin eski düğümleri yenilere düzgün şekilde bağlaması için farklı anahtarlar gereklidir; bu nedenle anahtar olarak kullanmak için yerel olarak benzersiz bir şey seçmelisiniz.

Anahtarlar için Nesneler Kullanmak ​

Fragment öğeleri için anahtarlar, özellik anahtarları olarak ele alınır. Bu tür kullanımlar düşündüğünüz gibi çalışmayacaktır.

javascript
// KAÇININ
things.map(function (thing) {
  return m(Thing, { key: thing, thing: thing });
});

Üzerinde bir toString yöntemi varsa bu çağrılır ve döndürdüğü değere bağlı kalırsınız; bu yöntemin çağrıldığını fark etmeyebilirsiniz bile. Eğer yoksa, tüm nesneleriniz "[object Object]"'e dönüştürülür ve böylece ciddi bir yinelenen anahtar sorununuz olur.

Pager
Önceki sayfaYaşam Döngüsü Yöntemleri
Sonraki sayfaOtomatik Yeniden Çizim Sistemi

MIT Lisansı altında yayınlanmıştır.

Copyright (c) 2024 Mithril Contributors

https://mithril.js.org/keys.html

MIT Lisansı altında yayınlanmıştır.

Copyright (c) 2024 Mithril Contributors