vfunc.js

핵심 개념

컴포넌트

vf.vfunc(options)가 컴포넌트를 만듭니다. new를 붙여도, 붙이지 않아도 됩니다.

const box = vf.vfunc({
  tag: 'section',                         // 루트 요소, 기본 'div'
  state: { open: false },                 // 상태
  render: (s) => vf.html`<button type="button" data-action="toggle"
    aria-expanded="${s.open ? 'true' : 'false'}">Details</button>
    ${s.open ? vf.html`<p data-ref="body">More text</p>` : ''}`,
  methods: { close() { this.open = false; } },
  delegates: [{ selector: '[data-action="toggle"]', eventType: 'click',
                onEvent: (e) => { e.sender.open = !e.sender.open; } }]
});
box.mount('#app');

이벤트

방식쓰는 곳
delegates: [{ selector, eventType, onEvent }]루트에 리스너 하나. closest(selector)로 찾고 루트 밖은 무시. 목록의 모든 행을 이것 하나로
events: [{ id, eventType, onEvent }]특정 요소에 직접(예: 스크롤, focus)

이벤트 객체는 { sender, event, eventType, target, id, data }입니다. 셀렉터는 data-action·data-ref·id에만 거세요. CSS 클래스에 걸면 디자인을 바꿀 때 기능이 깨집니다.

기존 HTML 제어 — vf.attach

vfunc의 핵심 개념입니다. 퍼블리셔가 만든 HTML을 다시 쓰지 않고 동작만 붙입니다.

// render가 없으면: 기존 요소를 그대로 채택(마크업·입력값·스타일 유지)
vf.attach('#search', {
  delegates: [{ selector: '[data-action="search"]', eventType: 'submit',
                onEvent: (e) => { e.event.preventDefault(); run(vf.form.values(e.sender.$node)); } }]
});

// render가 있으면: 요소는 그대로, 안쪽을 render가 채움(데이터에 따라 바뀌는 영역)
const results = vf.attach('#results', { state: { items: [] }, render: (s) => vf.html`…` });

안전한 HTML — vf.html

vf.html은 값이 들어가는 위치를 보고 처리합니다.

위치처리
요소 내용 <p>${x}</p>HTML 이스케이프. 배열은 이어 붙이고, vf.html 결과는 마크업으로
따옴표 속성 title="${x}"이스케이프. false/null은 빈 값, 단 aria-*/data-*는 "true"/"false"
URL 속성 href, src, action …이스케이프 + vf.safeUrl(javascript: 차단)
태그 안 <button ${x}>disabled 같은 속성 이름만
on* 속성, <script>, 따옴표 없는 속성거부: 값을 빼고 콘솔에 알림(vf.config({ strict: true })면 예외)

라이프사이클과 서드파티 위젯

훅시점할 일
onMountmount()·vf.attach()로 페이지에 들어간 직후(childs의 인스턴스는 부모보다 먼저 한 번)차트·에디터 만들기, 데이터 불러오기, 구독
onUpdate다시 그릴 때마다(render가 없으면 상태가 바뀔 때마다)위젯이나 채택한 마크업에 새 상태 반영
onDestroydestroy() 때, childs의 인스턴스를 정리한 뒤위젯·타이머·구독 정리

다시 그려도 사라지면 안 되는 요소(차트 캔버스 등)는 data-vf-keep="chart"로 표시합니다. 새로 만들지 않고 그 요소를 옮깁니다. 자세한 예는 예제 15입니다.

자식과 슬롯

childs: [{ targetId: 'main', component: child }]는 자식을 id="main" 요소에 붙이고, 부모가 다시 그려도 자식을 옮겨 붙여 상태를 유지합니다. 자식 인스턴스는 부모가 페이지에 들어갈 때 onMount를 받고, 부모를 destroy()하면 함께 정리됩니다. childs에 넣지 않고 직접 만들어 붙인 인스턴스는 부모의 onDestroy에서 destroy()하세요.

폼

vf.form.values(form)은 id가 있는 입력의 값을 { id: value }로 읽습니다(체크박스는 boolean). vf.form.reset(form)은 값을 비웁니다. 값을 로그로 남기지 말고, 화면에 보일 때는 { skipPassword: true }로 비밀번호를 빼세요.

자주 하는 실수