Skip to content
Mithril.js 2
Main Navigation ÚtmutatóAPI

magyar

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

magyar

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

Megjelenés

Sidebar Navigation

Első lépések

Telepítés

Egyszerű példa

Források

JSX

ES6+ öreg böngészőkön

Animációk

Tesztelés

Példák

Harmadik féltől való integráció

Útvonalkezelés

Kulcsfogalmak

Virtuális DOM csomópontok

Komponensek

Életciklus-metódusok

Kulcsok

Az automatikus újrarajzolási rendszer

Egyéb

Framework összehasonlítás

Migrálás v1.x-ről

Átállás v0.2.x verzióról

API

Ezen az oldalon

Komponensek ​

Szerkezet ​

A komponensek a nézet részeinek elkapszulázására szolgáló mechanizmusok, amelyek segítségével a kód könnyebben szervezhető és/vagy újra felhasználható.

Minden olyan JavaScript objektum, amely rendelkezik view metódussal, Mithril.js komponensnek minősül. A komponensek a m() segédeszközzel használhatók:

javascript
// definiáljuk a komponenst
var Example = {
  view: function (vnode) {
    return m('div', 'Hello');
  },
};

// használjuk a komponenst
m(Example);

// ezzel egyenértékű HTML
// <div>Hello</div>

Életciklus metódusok ​

A komponensek ugyanazokkal az életciklus metódusokkal rendelkezhetnek, mint a virtuális DOM csomópontok. Fontos, hogy a vnode argumentumként kerül átadásra minden életciklus metódusnak, valamint a view-nak is (az előző vnode az onbeforeupdate metódusnak is átadásra kerül):

javascript
var ComponentWithHooks = {
  oninit: function (vnode) {
    console.log('inicializálva');
  },
  oncreate: function (vnode) {
    console.log('DOM létrehozva');
  },
  onbeforeupdate: function (newVnode, oldVnode) {
    return true;
  },
  onupdate: function (vnode) {
    console.log('DOM frissítve');
  },
  onbeforeremove: function (vnode) {
    console.log('kilépési animáció elindítható');
    return new Promise(function (resolve) {
      // hívjuk meg az animáció befejezése után
      resolve();
    });
  },
  onremove: function (vnode) {
    console.log('DOM elem eltávolítása');
  },
  view: function (vnode) {
    return 'hello';
  },
};

A virtuális DOM más csomóponttípusaihoz hasonlóan a komponensek további életciklus metódusokkal is rendelkezhetnek, amelyek vnode típusként való használatkor vannak definiálva.

javascript
function initialize(vnode) {
  console.log('vnode-ként inicializálva');
}

m(ComponentWithHooks, { oninit: initialize });

A vnode-ok életciklus metódusai nem írják felül a komponens metódusait, és fordítva sem. A komponens életciklus metódusai mindig a vnode megfelelő metódusa után futnak le.

Ügyeljünk arra, hogy ne használjuk az életciklus metódusok neveit saját callback függvényeink neveiként a vnode-okban.

Az életciklus metódusokról bővebben az életciklus metódusok oldalon olvashatunk.

Adatok átadása komponenseknek ​

Adatokat úgy adhatunk át a komponens példányoknak, hogy egy attrs objektumot adunk át a hyperscript függvény második paramétereként:

javascript
m(Example, { name: 'Floyd' });

Ezek az adatok elérhetők a komponens nézetében vagy életciklus metódusaiban a vnode.attrs segítségével:

javascript
var Example = {
  view: function (vnode) {
    return m('div', 'Hello, ' + vnode.attrs.name);
  },
};

MEGJEGYZÉS: Az életciklus metódusok definiálhatók az attrs objektumban is, ezért kerüljük a nevük használatát saját callback-jeinkhez, mivel azokat a Mithril.js is meghívná. Csak akkor használjuk őket az attrs-ban, ha kifejezetten életciklus metódusként szeretnénk használni őket.

Állapot ​

Az összes virtuális DOM csomóponthoz hasonlóan a komponens vnode-oknak is lehet állapota. A komponens állapota hasznos az objektumorientált architektúrák támogatásához, az elkapszulázáshoz és a felelősségek szétválasztásához.

Vegye figyelembe, hogy sok más keretrendszertől eltérően a komponens állapotának megváltoztatása nem vált ki újrarajzolásokat vagy DOM frissítéseket. Ehelyett az újrarajzolások az eseménykezelők aktiválódásakor, a m.request által végzett HTTP kérések befejeződésekor, vagy amikor a böngésző különböző útvonalakra navigál történnek. A Mithril.js komponens állapot mechanizmusai egyszerűen kényelmi funkcióként léteznek az alkalmazások számára.

Ha olyan állapotváltozás következik be, amely nem a fenti feltételek eredménye (pl. setTimeout után), akkor a m.redraw() segítségével manuálisan is elindíthatunk egy újrarajzolást.

Closure komponens állapot ​

A fenti példákban minden komponens POJO-ként (Plain Old JavaScript Object - egyszerű régi JavaScript objektum) van definiálva, amelyet a Mithril.js belsőleg használ az adott komponens példányainak prototípusaként. Lehetséges komponens állapotot használni egy POJO-val (ahogy azt alább tárgyaljuk), de ez nem a legtisztább vagy legegyszerűbb megközelítés. Ehhez egy closure komponenst fogunk használni, ami nem más, mint egy burkoló függvény, ami visszaad egy POJO komponens példányt, aminek saját, zárt hatóköre van.

Egy closure komponenssel az állapot egyszerűen a külső függvényen belül deklarált változók segítségével tartható fenn:

javascript
function ComponentWithState(initialVnode) {
  // Komponens állapotváltozó, minden példányra egyedi
  var count = 0;

  // POJO komponens példány: bármely objektum, amely rendelkezik
  // view függvénnyel, amely egy vnode-ot ad vissza
  return {
    oninit: function (vnode) {
      console.log('closure komponens inicializálása');
    },
    view: function (vnode) {
      return m(
        'div',
        m('p', 'Count: ' + count),
        m(
          'button',
          {
            onclick: function () {
              count += 1;
            },
          },
          'Increment count'
        )
      );
    },
  };
}

A closure-en belül deklarált bármely függvény hozzáfér az állapotváltozóihoz.

javascript
function ComponentWithState(initialVnode) {
  var count = 0;

  function increment() {
    count += 1;
  }

  function decrement() {
    count -= 1;
  }

  return {
    view: function (vnode) {
      return m(
        'div',
        m('p', 'Count: ' + count),
        m(
          'button',
          {
            onclick: increment,
          },
          'Increment'
        ),
        m(
          'button',
          {
            onclick: decrement,
          },
          'Decrement'
        )
      );
    },
  };
}

A closure komponensek ugyanúgy használhatók, mint a POJO-k, pl. m(ComponentWithState, { passedData: ... }).

A closure komponensek nagy előnye, hogy nem kell aggódnunk a this kötése miatt az eseménykezelő callback-ekhez való csatoláskor. Valójában a this soha nem kerül használatra, és soha nem kell gondolkodnunk a this kontextus kétértelműségein.

POJO komponens állapot ​

Általánosságban ajánlott closure-öket használni a komponens állapotának kezelésére. Ha azonban valamilyen okból POJO-ban szeretnénk kezelni az állapotot, a komponens állapota háromféleképpen érhető el: inicializáláskor tervrajzként, a vnode.state segítségével, illetve a this kulcsszóval a komponens metódusaiban.

Inicializáláskor ​

A POJO komponensek esetében a komponens objektum az egyes komponens példányok prototípusa, így a komponens objektumon definiált bármely tulajdonság elérhető a vnode.state tulajdonságaként. Ez lehetővé teszi az egyszerű "tervrajz" állapot inicializálást.

Az alábbi példában a data a ComponentWithInitialState komponens vnode.state objektumának tulajdonságává válik.

javascript
var ComponentWithInitialState = {
  data: 'Initial content',
  view: function (vnode) {
    return m('div', vnode.state.data);
  },
};

m(ComponentWithInitialState);

// Ezzel egyenértékű HTML
// <div>Initial content</div>

vnode.state-en keresztül ​

Mint látható, az állapot a vnode.state tulajdonságon keresztül is elérhető, amely minden életciklus metódus számára elérhető, valamint a komponens view metódusa számára is.

javascript
var ComponentWithDynamicState = {
  oninit: function (vnode) {
    vnode.state.data = vnode.attrs.text;
  },
  view: function (vnode) {
    return m('div', vnode.state.data);
  },
};

m(ComponentWithDynamicState, { text: 'Hello' });

// Ezzel egyenértékű HTML
// <div>Hello</div>

A this kulcsszóval ​

Az állapot a this kulcsszóval is elérhető, amely minden életciklus metódus számára elérhető, valamint a komponens view metódusa számára is.

javascript
var ComponentUsingThis = {
  oninit: function (vnode) {
    this.data = vnode.attrs.text;
  },
  view: function (vnode) {
    return m('div', this.data);
  },
};

m(ComponentUsingThis, { text: 'Hello' });

// Ezzel egyenértékű HTML
// <div>Hello</div>

Ne feledjük, hogy az ES5 függvények használatakor a this értéke a beágyazott anonim függvényekben nem a komponens példánya. Ennek a JavaScript korlátozásnak a megkerülésére két ajánlott módszer létezik: nyíl függvények használata, vagy ha azok nem támogatottak, a vnode.state használata.

Osztályok ​

Ha megfelel az igényeinknek (például objektumorientált projektekben), a komponensek osztályok használatával is megírhatók:

javascript
class ClassComponent {
  constructor(vnode) {
    this.kind = 'class component'; // osztálykomponens
  }
  view() {
    return m('div', `Hello from a ${this.kind}`); // Helló egy ${this.kind} típusú komponensből
  }
  oncreate() {
    console.log(`A ${this.kind} was created`); // Létrehoztunk egy ${this.kind} típusú komponenst
  }
}

Az osztálykomponenseknek definiálniuk kell egy view() metódust, amelyet a .prototype.view segítségével lehet észlelni, hogy a fa renderelhető legyen.

Ugyanúgy használhatók, mint a normál komponensek.

javascript
// PÉLDA: m.render-en keresztül
m.render(document.body, m(ClassComponent));

// PÉLDA: m.mount-on keresztül
m.mount(document.body, ClassComponent);

// PÉLDA: m.route-on keresztül
m.route(document.body, '/', {
  '/': ClassComponent,
});

// PÉLDA: komponens kompozíció
class AnotherClassComponent {
  view() {
    return m('main', [m(ClassComponent)]);
  }
}

Osztály komponens állapot ​

Osztályokkal az állapot az osztálypéldány tulajdonságaival és metódusaival kezelhető, és a this segítségével érhető el:

javascript
class ComponentWithState {
  constructor(vnode) {
    this.count = 0;
  }
  increment() {
    this.count += 1;
  }
  decrement() {
    this.count -= 1;
  }
  view() {
    return m(
      'div',
      m('p', 'Count: ', this.count),
      m(
        'button',
        {
          onclick: () => {
            this.increment();
          },
        },
        'Increment'
      ),
      m(
        'button',
        {
          onclick: () => {
            this.decrement();
          },
        },
        'Decrement'
      )
    );
  }
}

Vegye figyelembe, hogy nyíl függvényeket kell használnunk az eseménykezelő callback-ekhez, hogy a this kontextus helyesen hivatkozhasson.

Komponens típusok keverése ​

A komponensek szabadon keverhetők. Egy osztálykomponensnek lehetnek closure vagy POJO komponensei gyermekként, stb.

Speciális attribútumok ​

A Mithril.js speciális szemantikát rendel számos tulajdonság kulcshoz, ezért általában kerülni kell azok használatát a normál komponens attribútumokban.

  • Életciklus metódusok: oninit, oncreate, onbeforeupdate, onupdate, onbeforeremove és onremove
  • key (kulcs), amelyet a kulcsolt fragmentumokban lévő identitás nyomon követésére használnak
  • tag (címke), amelyet arra használnak, hogy megkülönböztessék a vnode-okat a normál attribútum objektumoktól és más olyan dolgoktól, amelyek nem vnode objektumok.

Kerüljük az anti-mintákat ​

Bár a Mithril.js rugalmas, bizonyos kódminták használata nem ajánlott:

Kerüljük a kövér komponenseket ​

Általánosságban elmondható, hogy a "kövér" komponens egy olyan komponens, amely egyéni példánymetódusokkal rendelkezik. Más szavakkal, kerülni kell a függvények csatolását a vnode.state-hez vagy a this-hez. Rendkívül ritka, hogy olyan logika legyen, amely logikailag belefér egy komponens példánymetódusába, és amelyet más komponensek nem tudnak újrahasználni. Viszonylag gyakori, hogy az említett logikára egy másik komponensnek is szüksége lehet a jövőben.

Könnyebb a kódot refaktorálni, ha ez a logika az adatszintben van elhelyezve, mintha egy komponens állapotához lenne kötve.

Tekintsük ezt a kövér komponenst:

javascript
// views/Login.js
// KERÜLENDŐ
var Login = {
  username: '',
  password: '',
  setUsername: function (value) {
    this.username = value;
  },
  setPassword: function (value) {
    this.password = value;
  },
  canSubmit: function () {
    return this.username !== '' && this.password !== '';
  },
  login: function () {
    /*...*/
  },
  view: function () {
    return m('.login', [
      m('input[type=text]', {
        oninput: function (e) {
          this.setUsername(e.target.value);
        },
        value: this.username,
      }),
      m('input[type=password]', {
        oninput: function (e) {
          this.setPassword(e.target.value);
        },
        value: this.password,
      }),
      m(
        'button',
        { disabled: !this.canSubmit(), onclick: this.login },
        'Login'
      ),
    ]);
  },
};

Általában egy nagyobb alkalmazás kontextusában egy olyan bejelentkező komponens, mint a fenti, a felhasználói regisztrációhoz és a jelszó helyreállításhoz szükséges komponensek mellett létezik. Képzeljük el, hogy előre szeretnénk kitölteni az e-mail mezőt, amikor a bejelentkező képernyőről a regisztrációs vagy jelszó-helyreállítási képernyőre navigálunk (vagy fordítva), hogy a felhasználónak ne kelljen újra beírnia az e-mail címét, ha véletlenül rossz oldalt töltött ki (vagy talán a regisztrációs űrlapra szeretné irányítani a felhasználót, ha nem található felhasználónév).

Azonnal látjuk, hogy a username és password mezők megosztása ebből a komponensből egy másikba nehéz. Ez azért van, mert a kövér komponens elkapszulázza az állapotát, ami definíció szerint megnehezíti az állapot külső elérését.

Értelmesebb refaktorálni ezt a komponenst, és kihúzni az állapotkódot a komponensből az alkalmazás adatszintjébe. Ez lehet olyan egyszerű, mint egy új modul létrehozása:

javascript
// models/Auth.js
// ELŐNYBEN RÉSZESÍTENDŐ
var Auth = {
  username: '',
  password: '',
  setUsername: function (value) {
    Auth.username = value;
  },
  setPassword: function (value) {
    Auth.password = value;
  },
  canSubmit: function () {
    return Auth.username !== '' && Auth.password !== '';
  },
  login: function () {
    /*...*/
  },
};

module.exports = Auth;

Ezután megtisztíthatjuk a komponenst:

javascript
// views/Login.js
// ELŐNYBEN RÉSZESÍTENDŐ
var Auth = require('../models/Auth');

var Login = {
  view: function () {
    return m('.login', [
      m('input[type=text]', {
        oninput: function (e) {
          Auth.setUsername(e.target.value);
        },
        value: Auth.username,
      }),
      m('input[type=password]', {
        oninput: function (e) {
          Auth.setPassword(e.target.value);
        },
        value: Auth.password,
      }),
      m(
        'button',
        {
          disabled: !Auth.canSubmit(),
          onclick: Auth.login,
        },
        'Login'
      ),
    ]);
  },
};

Ily módon az Auth modul most az igazság forrása a hitelesítéssel kapcsolatos állapotokhoz, és egy Register komponens könnyen hozzáférhet ezekhez az adatokhoz, és akár újra is felhasználhatja az olyan metódusokat, mint a canSubmit, ha szükséges. Ezenkívül, ha érvényesítési kódra van szükség (például az e-mail mezőhöz), akkor csak az setEmail-t kell módosítania, és ez a változás e-mail érvényesítést végez minden olyan komponens számára, amely módosítja az e-mail mezőt.

Bónuszként vegye figyelembe, hogy többé nem kell .bind-ot használnunk, hogy hivatkozást tartsunk a komponens eseménykezelőinek állapotára.

Ne továbbítsuk a vnode.attrs-t magát más vnode-oknak ​

Néha érdemes rugalmasan tartani az interfészt és egyszerűsíteni a megvalósítást azáltal, hogy attribútumokat továbbítunk egy adott gyermekkomponensnek vagy elemnek, például a Bootstrap modal-nak. Kísértést jelenthet egy vnode attribútumainak továbbítása az alábbiak szerint:

javascript
// KERÜLENDŐ
var Modal = {
  // ...
  view: function (vnode) {
    return m('.modal[tabindex=-1][role=dialog]', vnode.attrs, [
      //       a `vnode.attrs` továbbítása itt ^
      // ...
    ]);
  },
};

Ha a fentiek szerint csináljuk, problémákba ütközhetünk a használatakor:

javascript
var MyModal = {
  view: function () {
    return m(
      Modal,
      {
        // Ez kétszer váltja át, így nem jelenik meg
        onupdate: function (vnode) {
          if (toggle) $(vnode.dom).modal('toggle');
        },
      },
      [
        // ...
      ]
    );
  },
};

Ehelyett egyes attribútumokat kell továbbítanunk a vnode-okba:

javascript
// ELŐNYBEN RÉSZESÍTENDŐ
var Modal = {
  // ...
  view: function (vnode) {
    return m('.modal[tabindex=-1][role=dialog]', vnode.attrs.attrs, [
      //           az `attrs:` továbbítása itt ^
      // ...
    ]);
  },
};

// Példa
var MyModal = {
  view: function () {
    return m(Modal, {
      attrs: {
        // Ez egyszer váltja át
        onupdate: function (vnode) {
          if (toggle) $(vnode.dom).modal('toggle');
        },
      },
      // ...
    });
  },
};

Ne manipuláljuk a children-t ​

Ha egy komponens véleményes abban, hogy hogyan alkalmazza az attribútumokat vagy a gyermekeket, akkor át kell váltania az egyéni attribútumok használatára.

Gyakran kívánatos több gyermekhalmazt definiálni, például ha egy komponens konfigurálható címmel és törzzsel rendelkezik.

Kerüljük a children tulajdonság destrukturálását erre a célra.

javascript
// KERÜLENDŐ
var Header = {
  view: function (vnode) {
    return m('.section', [
      m('.header', vnode.children[0]),
      m('.tagline', vnode.children[1]),
    ]);
  },
};

m(Header, [m('h1', 'My title'), m('h2', 'Lorem ipsum')]);

// kínos használati eset
m(Header, [
  [m('h1', 'My title'), m('small', 'A small note')],
  m('h2', 'Lorem ipsum'),
]);

A fenti komponens megsérti azt a feltételezést, hogy a gyermekek ugyanabban a folytonos formátumban kerülnek kiadásra, mint ahogyan érkeznek. Nehéz megérteni a komponenst anélkül, hogy elolvasná a megvalósítását. Ehelyett használjunk attribútumokat névvel ellátott paraméterekként, és tartsuk fenn a children-t az egységes gyermektartalom számára:

javascript
// ELŐNYBEN RÉSZESÍTENDŐ
var BetterHeader = {
  view: function (vnode) {
    return m('.section', [
      m('.header', vnode.attrs.title),
      m('.tagline', vnode.attrs.tagline),
    ]);
  },
};

m(BetterHeader, {
  title: m('h1', 'My title'),
  tagline: m('h2', 'Lorem ipsum'),
});

// tisztább használati eset
m(BetterHeader, {
  title: [m('h1', 'My title'), m('small', 'A small note')],
  tagline: m('h2', 'Lorem ipsum'),
});

Statikusan definiáljuk a komponenseket, dinamikusan hívjuk meg őket ​

Kerüljük a komponens definíciók létrehozását a nézeteken belül ​

Ha egy komponenst egy view metóduson belül hozunk létre (akár közvetlenül beágyazva, akár egy olyan függvény meghívásával, amely ezt teszi), minden újrarajzolásnak a komponens egy másik klónja lesz. A komponens vnode-ok diffelésekor, ha az új vnode által hivatkozott komponens nem szigorúan egyenlő a régi komponens által hivatkozott komponenssel, akkor a kettőt különböző komponenseknek tekintik, még akkor is, ha végső soron egyenértékű kódot futtatnak. Ez azt jelenti, hogy a dinamikusan, gyár által létrehozott komponensek mindig a semmiből lesznek újra létrehozva.

Ezért kerüljük a komponensek újbóli létrehozását. Ehelyett használjuk a komponenseket a bevett módon.

javascript
// KERÜLENDŐ
var ComponentFactory = function (greeting) {
  // minden híváskor új komponenst hoz létre
  return {
    view: function () {
      return m('div', greeting);
    },
  };
};
m.render(document.body, m(ComponentFactory('hello')));
// a második hívás a div-et a semmiből hozza létre újra, ahelyett, hogy nem csinálna semmit
m.render(document.body, m(ComponentFactory('hello')));

// ELŐNYBEN RÉSZESÍTENDŐ
var Component = {
  view: function (vnode) {
    return m('div', vnode.attrs.greeting);
  },
};
m.render(document.body, m(Component, { greeting: 'hello' }));
// a második hívás nem módosítja a DOM-ot
m.render(document.body, m(Component, { greeting: 'hello' }));

Kerüljük a komponens példányok létrehozását a nézeteken kívül ​

Ezzel szemben, hasonló okokból, ha egy komponens példányt a nézeten kívül hoznak létre, a jövőbeli újrarajzolások egyenlőségi ellenőrzést végeznek a csomóponton, és kihagyják azt. Ezért a komponens példányokat mindig a nézeteken belül kell létrehozni:

javascript
// KERÜLENDŐ
var Counter = {
  count: 0,
  view: function (vnode) {
    return m(
      'div',
      m('p', 'Count: ' + vnode.state.count),

      m(
        'button',
        {
          onclick: function () {
            vnode.state.count++;
          },
        },
        'Increase count'
      )
    );
  },
};

var counter = m(Counter);

m.mount(document.body, {
  view: function (vnode) {
    return [m('h1', 'My app'), counter];
  },
});

A fenti példában a számláló komponens gombjára kattintva növekszik az állapot száma, de a nézete nem fog frissülni, mert a komponenst képviselő vnode ugyanarra a memóriacímre mutat, ezért a renderelési folyamat nem érzékeli a változást. A komponenseket mindig a nézetben kell meghívnia, hogy biztosítsa egy új vnode létrehozását:

javascript
// ELŐNYBEN RÉSZESÍTENDŐ
var Counter = {
  count: 0,
  view: function (vnode) {
    return m(
      'div',
      m('p', 'Count: ' + vnode.state.count),

      m(
        'button',
        {
          onclick: function () {
            vnode.state.count++;
          },
        },
        'Increase count'
      )
    );
  },
};

m.mount(document.body, {
  view: function (vnode) {
    return [m('h1', 'My app'), m(Counter)];
  },
});
Pager
Előző oldalVirtuális DOM csomópontok
Következő oldalÉletciklus-metódusok

A MIT licenc alapján kiadva.

Copyright (c) 2024 Mithril Contributors

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

A MIT licenc alapján kiadva.

Copyright (c) 2024 Mithril Contributors