Rehberler
DevTools sözleşmesi
useDevelopment(true) ile açılan __MOTIF_DEVTOOLS__ nesnesi; yollar, olaylar, ağaç, bileşen içeriği, reaktivite izi, sayaçlar, router ve hata kayıtları, yazma işlemleri.
Çekirdek bir DevTools arayüzü içermez; arayüzün okuyup yazabileceği tek bir nesne sunar. Bu sayfa o nesneyi anlatır. DevTools arayüzü yazanlar içindir. Paketinin kendi durumunu DevTools'a açmak isteyen paket yazarı Paketler için DevTools sayfasını okur; bu sayfa onun üstünde durduğu zemindir.
Açma
DevTools yalnız app.useDevelopment(true) ile açılır. Bu çağrı geliştirme modunu da açar. useDevelopment(false) ikisini birden kapatır. Adres çubuğundaki bir parametre ya da elle yazılan bir global DevTools'u açmaz.
Açıkken globalThis.__MOTIF_DEVTOOLS__ sözleşme nesnesini tutar; kapanınca nesne kaldırılır. Kapalıyken çekirdek hiçbir şey kaydetmez ve hiçbir olay üretmez. Sayaçlar ve kayıtlar DevTools'un açıldığı andan itibaren tutulur.
Modül düzeyindeki store'lar (export const cart = reactive({ … })) import sırasında, yani useDevelopment(true) çağrısından önce oluşur. @motifx/compiler Vite eklentisiyle geliştirirken, değeri @motifx/core'un reactive, useModel, createSignal, createComputed ya da createLazyComputed çağrısından gelen her üst düzey const ya da let, değişkeninin adıyla adlandırılır; .ts, .tsx, .js ve .jsx dosyalarının hepsinde. Bu store'lar getStores()'ta name (cart) ve file (src/stores/cart.ts, Vite köküne göre) ile listelenir. Üretim derlemesine hiçbir şey eklenmez.
Bir bileşen ya da servis alanında tutulan erken store'lar getStores() çağrısındaki taramayla listeye girer; yalnız bir kapanış içinde tutulan ve derleyicinin adlandırmadığı store'lar görünmez.
import type { MotifDevtools } from "@motifx/core/devtools";
app.useDevelopment(true);
const devtools = (globalThis as any).__MOTIF_DEVTOOLS__ as MotifDevtools;
app.useDevelopment(true);
const devtools = globalThis.__MOTIF_DEVTOOLS__;
version sözleşmenin sürümüdür, bugün 2. Bir alanın anlamı değiştiğinde artar; yeni alan eklenmesi sürümü değiştirmez. coreVersion uygulamanın çalıştığı @motifx/core paketinin sürümüdür ('1.2.0').
Yollar
Bileşenlerin kimlik numarası yoktur. Ağaç bir ormandır: [] bütün kökleri tutan sanal Application düğümüdür, yolun ilk sayısı kökü seçer, kalanı o kökten aşağı controls indeksleridir. Kökler sırayla run()'ın kurduğu uygulama kabuğu ([0]), oluşturulmuşsa sayfa katmanı app.layer (Layer) ve app.windows ile açılmış ya da bağlanmış her pencerenin gövdesini saran host bileşeni (Window); pencereler app.windows.list sırasındadır. [0, 2] kabuğun üçüncü çocuğu, [1, 0] katmanın ilk çocuğudur. Hiçbir kökün altında olmayan bir bileşenin yolu null'dur.
Yol, bileşenin listedeki sırasına bağlıdır. Öncesine bir bileşen eklenince ya da liste yeniden sıralanınca yol değişir. Bu yüzden ağaçtaki her ekleme, çıkarma ve taşıma tree:changed olayıyla bildirilir; olay, yeniden okunması gereken üst bileşenlerin o anki yollarını taşır. Katman oluştuğunda ve bir pencere açılıp kapandığında kök listesi değişir; bu da aynı olayda [] yoluyla bildirilir.
Olaylar
on(fn) bir dinleyici ekler ve dinleyiciyi kaldıran bir fonksiyon döndürür. Her olay { type, payload } biçimindedir. Bileşen olaylarının yükünde her zaman { path, type, tag } bulunur; hiçbir kökün altında olmayan bileşende path null'dur.
| Olay | Yük |
|---|---|
tree:changed |
{ parents }: değişen üst bileşenlerin yolları; kök listesi değiştiğinde []. Aynı turdaki değişiklikler tek olayda gelir. |
documents:changed |
Uygulamaya bağlı belgelerin listesi değişti: {}. Liste getDocuments() ile yeniden okunur. |
component:built |
duration: çocuklar dahil build süresi (ms). |
component:visibility |
visible; yalnız görünürlük değişince. |
component:wait |
waiting; yalnız bekleme durumu değişince. |
component:disposing |
Bileşen bilgisi. |
component:disposed |
duration; path, dispose başladığı andaki yoldur. |
reactivity:flush |
Her reaktivite turunun sonunda bir kez: { id, duration, runs, triggers, effects, operations }; bkz. Operation'lar. |
operation:started |
Bir iş başladı (bkz. Operation'lar): { id, kind, name?, data?, parent? }. |
operation:finished |
İş bitti: { id, duration, error? }; async işte promise sonuçlandığında. |
stores:changed |
Yeni store kaydedildiğinde, aynı turda kaç store oluşursa oluşsun bir kez: {}. Liste getStores() ile yeniden okunur. |
scopes:changed |
Bir DI kapsamı açıldığında, onaylandığında ya da kapandığında, tur başına bir kez: {}. Liste getScopes() ile yeniden okunur. |
service:created |
Bir servis örneği oluştu: { scope, token, lifetime, source, instance }. scope getScopes()'taki numaradır, source class, factory ya da value; transient her çözülüşte yayar. |
service:disposed |
Bir kapsam örneklerinden birini kapatmaya başladı: { scope, token, lifetime, instance }. |
route:navigation-start |
{ uri }. |
route:navigation-end |
{ uri, requested, outcome, reason?, params, duration }; her gezinme bir sonuçla kapanır. |
warning |
{ code, message, details }; her gelişte. |
error:caught |
{ context, error }; her gelişte. |
extension:registered |
Bir paket kayıt oldu (bkz. Paketler): { name }. |
extension:disposed |
Kaydı kaldırıldı: { name }. |
extension:changed |
Paket arayüzden yeniden okumasını istiyor: { name, source? }; source yoksa panelleri, varsa o kaynağı. |
extension:reveal |
Paket arayüzden bir şey göstermesini istiyor: { name, target }; target { panel, route?, params?, state? } biçimindedir. |
ext:<ad>:<tür> |
Bir paketin yaydığı olay (bkz. Paketler). |
Bileşenin sayfaya girdiği an için olay yoktur. Bir bileşenin sayfada olup olmadığı ağaçtaki connected alanından okunur.
Ağaç
getTree(path = [], depth = Infinity): yoldaki bileşeni ve verilen derinliğe kadar alt ağacını düz veri olarak döndürür. Her düğüm{ path, type, tag, built, visible, waiting, disposed, connected, document, childCount, children? }taşır.typesınıf bileşeninde sınıfın, fonksiyon bileşeninde fonksiyonun adıdır.[]düğümününtype'ıApplication,tag'i#roots'tur; kabuk köküComponent, katmanLayer, pencere köküWindowadını taşır ve pencere kökünde ayrıca pencerenin başlığıtitleolarak bulunur.documentelemanın bulunduğu belgeningetDocuments()'taki sırasıdır, sayfa0; uygulamanın elemanını başka bir belgeye kendi taşıdığı bileşen ağaçtaki yerini korur, yalnızdocumento belgeyi gösterir. Bir liste (JSX'te{items.map(…)}ya dabindings.list) iki fragment ekler:.map()ifadesinin kardeşleri arasındaki yeri olanListve onun içinde öğelerin durduğu aralık olanListItems; listesini kendi kuran bir bileşen adını korur veListItems'ı taşır. Bir koşul onu kuran bağın adıyla görünür:Ternary(bindings.ternary, JSX'tea ? b : c),When(bindings.when, JSX'tea && b),Expression(bileşen döndürenbindings.method, JSX'te{() => …}) ya daSwitch(bindings.switchCase); içinde gösterdiği şey, hiçbir şey göstermediği süreceEmptydurur.x-waitvex-displayağaca bir şey eklemez: bileşen adını korur ve bekliyor olarak görünür. Derinliği aşan düğümlerdechildrenyoktur,childCountvardır.getElement(path): o yoldaki bileşenin DOM düğümü; vurgulama ve kaydırma için.[]içinnull.pathOfElement(node): bir DOM düğümünden yukarı çıkarak ilk bileşenin yolunu döndürür, düğüm hangi belgede olursa olsun; hiçbir kökün altında değilsenull.getDocuments(): sayfanın belgesi ve uygulamaya bağlı belgeler (app.windowspencereleri,connectDocumentile bağlananlar),documentalanının saydığı sırada.
tag elemanın etiket adıdır; fragment için #fragment, metin için #text.
Bileşen içeriği
inspect(path) tek bir bileşenin içini düz veri olarak döndürür. Okuma sırasında bileşende hiçbir iç nesne yaratılmaz ve kullanıcı kodu çağrılmaz.
| Alan | İçerik |
|---|---|
props |
Her prop'un önizlemesi. Fonksiyon { kind: 'function', name } olarak gösterilir, çağrılmaz. Dizi { kind: 'array', length }, nesne { kind: 'object', type, keys }, bileşen { kind: 'component', path, type }, DOM düğümü { kind: 'node', tag }, signal ya da computed değerinin önizlemesiyle { kind: 'signal', type: 'signal' \| 'computed', value, stale? }; stale: true güncel değerini henüz hesaplamamış bir computed'i gösterir ve getter'ı çalıştırılmaz. |
fields |
Bileşenin kendi alanları (örneğin sınıf alanı state = reactive(…)), props ile aynı önizleme biçiminde; çerçevenin her bileşene yazdığı iç alanlar listelenmez. Getter olan alan { kind: 'getter' } olarak görünür ve çağrılmaz. |
functionProps |
Yalnız fonksiyon bileşeninin kökünde bulunur: fonksiyona verilen prop'lar, props ile aynı önizleme biçiminde. |
bindings |
{ kind, property?, member?, updateMode?, active }; kind: property, list, conditional, class, attr, style. |
events |
{ name, type, dom, handler, modifiers }; dom: false bileşen olayıdır. |
hooks |
{ name, method, handlers }: sınıf metodu olarak mı tanımlı, kaç handler bağlı. |
classes |
{ static, reactive: [{ id, classes }] }; reaktif kaynakların o anki sınıf kümesi. |
attributes |
{ name, value, reactive, held?, heldValue? }; value bileşenin kendi değeridir. Üstünde attr.hold ile bir değer tutulan öznitelikte held: true olur ve heldValue elemanda görünen değeri verir. |
services |
{ scope, requested }: bileşenin getService ile istediği servisler ve DI kapsamı. |
effects |
Bkz. Reaktivite izi. |
extensions |
Bu bileşen için veri veren paketlerin çıktısı. |
window |
Yalnız pencere kökünde: { kind, title, open, components }; kind popup, pip ya da attached, components penceredeki bileşen sayısı. |
readValue(path, source, keys), props, functionProps ya da fields (source) içindeki bir değerin bir düzeyini açar: keys değere giden yoldur (['stat', 'tags']). Sonuç aynı önizleme biçiminde { ok: true, value: { value, entries: [{ key, value, getter? }], total } } olur; en fazla 100 alan döner, total tam sayıyı verir. Getter ve fonksiyonlar hiç çağrılmaz: getter getter: true olarak görünür, getter'dan geçen yol { ok: false, error: 'getter' } döndürür. Dizi, düz nesne, sınıf örneği, Map (anahtarlar metin olarak) ve Set (sıra numarasıyla) açılabilir; signal ya da computed'den geçen yol değerinin içine devam eder. readStore(id, keys) aynı biçimde getStores()'taki bir store'un içini açar; signal ve computed'de değerin içi açılır.
getServices() uygulamaya kayıtlı servisleri { token, lifetime, created } olarak listeler.
getScopes() DI kapsamlarını ve her birinde oluşmuş servisleri listeler:
| Alan | İçerik |
|---|---|
id, parent |
Kapsamın numarası ve üst kapsamının numarası; kökte parent null'dır. |
kind, name |
root, navigation (her gezinmede bir tane) ya da user (createScope(name) ile açılan) ve kapsamın adı. |
current |
Güncel sayfanın scoped servisleri aldığı kapsam. |
pending |
Henüz kurulan, onaylanmamış gezinme kapsamı. |
pins |
Kapsamı güncel sayfanın dışında hâlâ tutan bileşen sayısı, örneğin keepAlive ile saklanan bir sayfa ya da serbest bırakılması süren ayrılan sayfa. Önceki gezinme kapsamı bu sayı sıfırın üstündeyken yaşar. |
services |
{ token, lifetime, instance, creating? }: kökte singleton'lar, her kapsamda scoped servisler, props ile aynı önizleme biçiminde; async servis kurulurken creating: true. |
transients |
enableAutoDisposeTransients() açıkken kapsamın kapatmak için tuttuğu transient örnekleri. |
createScope ile açılan kapsam kullanıldığı sürece listelenir, dispose() ile listeden çıkar. scopes:changed olayı listenin ne zaman yeniden okunacağını söyler.
readService(scope, token, keys) bir servis örneğinin bir düzeyini readValue ile aynı biçimde açar: scope getScopes()'taki numara, token o kapsamın services listesinde görünen addır. Singleton'lar kök kapsamdan, scoped servisler onları oluşturan kapsamdan okunur. Okuma servisi asla oluşturmaz: henüz oluşmamış servis { ok: false, error: 'no service' } döndürür. Yalnız örneğin kendi alanları listelenir; sınıftaki getter'lar ve #özel alanlar listelenmez.
Reaktivite izi
inspect(path).effects, bileşenin sahip olduğu effect'leri listeler. Bir kayıt şöyledir:
const effect = {
kind: "binding:textContent", runs: 4, lastDuration: 0.1,
lastTrigger: { target: { kind: "object", type: "Object", keys: 7 }, key: "title" },
deps: [{ target: { kind: "object", type: "Object", keys: 7 }, key: "title" }],
};
const effect = {
kind: "binding:textContent", runs: 4, lastDuration: 0.1,
lastTrigger: { target: { kind: "object", type: "Object", keys: 7 }, key: "title" },
deps: [{ target: { kind: "object", type: "Object", keys: 7 }, key: "title" }],
};
kind effect'in nereden geldiğini söyler: binding:<özellik>, list, when, method, ternary, watch, switch, conditional, class, attr:<ad>, style, virtual-data, computed. Doğrudan effect() ile oluşturulanlar effect olarak görünür ve sahipsizdir.
reactivity:flush olayı her turda bir kez gelir. triggers o turu başlatan değişiklikleri sayılarıyla, effects çalışan effect'leri sahip bileşenlerinin yollarıyla taşır. Değişen nesne getStores()'taki bir store'a aitse tetikleyici store: { id, path } alanını da taşır: path store'un kökünden değişen nesneye giden anahtarlardır (stats[1].value değişikliği { path: ['stats', '1'] } ve key: 'value' olarak gelir). Signal ve computed'de anahtar value'dur. Bir turda aynı değere yapılan bin değişiklik yine tek olaydır.
Her flush'ın artan bir id'si vardır. Bir operation içinde gerçekleşen tetikleyici operationId taşır; operations, o turun tetikleyicilerinin ait olduğu operation kimliklerini listeler. Aynı değere farklı operation'larda yapılan değişiklikler ayrı sayılır.
Sayaçlar
getCounters() tek çağrıda şu alanları taşıyan bir nesne döndürür:
| Alan | Anlamı |
|---|---|
components.live |
Yaşayan bileşen sayısı |
components.inTree |
Uygulama ağacındaki bileşen sayısı |
components.notInTree |
Yaşayan ama ağaçta olmayan bileşen sayısı |
components.byType |
Bileşen türüne göre canlı sayı ({ NoteRow: 12 }) |
bindings |
Ağaçtaki bağ sayısı |
listeners.dom / listeners.component |
DOM dinleyicileri / bileşen olayı dinleyicileri |
effects.live / effects.owned |
Yaşayan effect'ler / bunlardan bir bileşene ait olanlar |
reactivity.runs |
Effect çalışma sayısı |
reactivity.flushes / reactivity.flushRuns |
Tur sayısı / turlarda çalışan effect toplamı |
reactivity.lastFlushDuration / reactivity.maxFlushDuration |
Son turun / en uzun turun süresi (ms) |
Alanların hepsi sayıdır; byType tür adından sayıya bir nesnedir.
live bileşen kurulunca artar, dispose olunca azalır. inTree, bindings ve listeners çağrı anında ağaç dolaşılarak sayılır. notInTree, yaşayan ama ağaçta olmayan bileşenlerdir; sayfadan çıkıldığı halde düşmüyorsa o bileşenler bir yerde tutuluyordur. Sayaçlar DevTools her açıldığında sıfırdan başlar.
Router, hatalar ve uyarılar
getRoute(): o anki{ uri, params, chain }.getNavigations(): son 50 gezinme,{ requested, uri, outcome, reason?, params, duration, at }.outcome:done,skipped,cancelled(reason:middleware,onLeave,guardya daonLeave'in verdiği sebep),notFound,error. Bir guard başka adrese yönlendirdiğinde iki kayıt oluşur: önce yönlendirilen gezinme, sonra istenen adres ile varılan adresi ayrı tutan dış gezinme.getRoutes(): tanımlı rotalar.getWarnings(): uyarılar kod ve mesaja göre birleşir:{ code, message, details, count, firstAt, lastAt }. En fazla 200 kayıt tutulur; dolunca en uzun süredir görülmeyen silinir.detailsilk gelişteki haliyle kalır.getErrors(): hatalar bağlam, ad ve mesaja göre aynı biçimde birleşir; en fazla 100 kayıt.getStores(): devtools açıkken oluşturulan ve hâlâ kullanılanreactive,useModel, signal ve computed kayıtları; bir bileşen ya da servis alanında tutulan, devtools açılmadan önce oluşmuş store'lar da tarama sırasında listeye alınır; her kayıt{ id, name, file?, type, value, stale?, released?, holders }; modül store'ununnamevefilealanları derleyiciden gelir (yukarıya bakın).holdersstore'u bir alanında tutan bileşenleri ({ kind: 'component', path, type, field }) ve servisleri ({ kind: 'service', token, lifetime, field }; servisin kendisi store'safield: null) listeler; liste her çağrıda bileşen ağacı ve oluşmuş servisler taranarak, getter çağrılmadan çıkarılır. Boş liste, store'un modül düzeyinde ya da bir kapanış içinde tutulduğunu gösterir. Daha önce bir bileşen ya da servis alanında tutulan, artık tutulmayan storereleased: trueile gelir (örneğin kapanan bileşeninstatealanı). Kullanılmayan store çöp toplayıcı onu topladığında listeden düşer; bunun zamanını tarayıcı belirler. Signal ve computed değeri o anki değerdir; computed okunurken getter'ı çalıştırılmaz, hiç hesaplanmamış ya da bayat bir computedstale: trueile gelir.
Yazma işlemleri
Her yazma, uygulama kodunun da kullandığı public API'den geçer. Sonuç { ok: true, value? } ya da { ok: false, error } olur; işlemler hata fırlatmaz.
| İşlem | Ne yapar |
|---|---|
setClass(path, name, on) |
class.add / class.remove. |
setAttribute(path, name, value) |
attr.add; value: null siler. on… ile başlayan adlar yazılmaz. |
setVisible(path, visible) |
motif.show() / motif.hide(); geçişler beklenir. Kökler gizlenmez: tek sayılı yol ok: false döner. |
writeModel(path, value) |
bindings.writeModel(value); model bağlaması yoksa ok: false. |
setProp(path, keys, value) |
props üzerinde anahtar yoluna yazar, ardından setState() ile bağlamaları yeniden çalıştırır. |
navigate(uri) |
router.navigate(uri). |
setStoreValue(id, keys, value) |
getStores()'taki kimliğiyle bir reactive ya da signal kaydına yazar; signal'de keys boşsa değerin kendisini değiştirir. Computed yazılamaz. Reaktivite normal çalışır. |
callExtension(name, panel, command, args) |
Bir paketin kaydettiği komutu çalıştırır (bkz. Paketler); yalnız capabilities'i write izni veren bir panelden. Komutun dönüş değeri beklenir ve value olur. |
__proto__, prototype ve constructor anahtarları yazılmaz.
Sonraki adım
Kendi durumunu DevTools'ta gösteren paket registerDevtools ile kaydolur: Paketler için DevTools. Dışa aktarılan bütün sembollerin özeti için API Referansı bölümüne geçin.