확장, 플러그인, IE
확장 규칙
vfunc 소스를 고치지 않고 공개 API로 확장합니다. 그래야 vfunc를 올려도 확장이 계속 동작합니다.
| 수준 | 방법 |
|---|---|
| 설정 | vf.config({ strict: true }), 메시지, 토큰 |
| 스타일 | 토큰 파일 뒤에서 --vf-* 덮어쓰기 |
| 조합 | 컴포넌트를 만들어 주는 함수, childs, 공식 컴포넌트를 감싼 내 컴포넌트(아래) |
| 서드파티 | onMount + data-vf-keep + onDestroy, 수준 L0·L1·L2는 서드파티 통합 |
| 플러그인 | vf.use(plugin) → vf.ext.<이름> |
vf.*의 공식 멤버는 읽기 전용이고, 확장은vf루트에 멤버를 더하지 않습니다.- 네이티브 prototype(
Event.prototype등)을 고치지 않습니다.
내 컴포넌트
공식 컴포넌트로 내 컴포넌트를 만들 때도 layer2 규칙을 따르면 나머지 코드와 같은 모양이 됩니다. 샘플은 layer2/examples/custom-component입니다.
// shop-ui.js — vf 루트가 아닌 내 모듈
export function vsPriceTag({ amount, was, currency = 'USD' }) {
const off = was > amount ? Math.round((1 - amount / was) * 100) : 0;
return vf.html`<span class="price-tag">${vf.fmt.currency(amount, currency)}${
off ? vf.vsBadge({ label: '-' + off + '%', variant: 'danger' }) : ''}</span>`;
}vs*는vf.html로 만든 SafeHtml을,vf*는getValue()/setValue(v)를 가진 인스턴스를 돌려줍니다. 콜백은{ sender, event, data }를 받습니다.- 안에서 쓰는 공식
vf*는childs({ targetId, component })에 넣습니다. 다시 그려도 유지되고 함께 정리됩니다. - 훅은
id·data-ref·data-action, 클래스는 내 블록 이름(vf-*는 공식), CSS는 토큰만 씁니다. - 여러 앱에서 쓰려면
vf.ext.<이름>플러그인으로 묶습니다.
플러그인 — vf.use
vf.use({
name: 'company', version: '1.0.0', requires: '^1.0.0',
install(vf, options) { return { toast: (message) => { /* … */ } }; }
}, { duration: 3000 });
vf.ext.company.toast('saved');공식 플러그인 — vf.ext.update
새로고침 버튼이 없는 환경(앱 웹뷰, 키오스크, PWA, 인앱 브라우저)에서 새 배포를 반영합니다.
import vfUpdate from 'vfunc/plugins/update'; // <script>는 dist/plugins/update.min.js → 전역 vfUpdate
vf.use(vfUpdate, { url: './version.json', current: APP_VERSION, policy: 'next-navigation' });| 정책 | 동작 |
|---|---|
next-navigation(기본) | 다음 화면 이동 때 교체. 작업 중인 내용을 잃지 않습니다 |
prompt | onAvailable(info, apply)로 앱이 안내를 그리고, 사용자가 누르면 교체 |
immediate | 즉시 교체(보안 패치) |
자세한 배포 방법은 배포와 캐시에 있습니다.
공식 플러그인 — vf.ext.shortcut
페이지 전체의 키보드 단축키입니다. 입력칸에서 누른 키는 허용하지 않으면 무시하고, IME 조합 중인 키도 무시합니다. 그래서 한글·일본어·중국어를 입력하는 동안에는 단축키가 실행되지 않습니다.
import vfShortcut from 'vfunc/plugins/shortcut'; // <script>는 dist/plugins/shortcut.min.js → 전역 vfShortcut
const keys = vf.use(vfShortcut);
const off = keys.add('mod+k', () => search.focus(), { label: '검색' }); // mod는 Apple 기기에서 ⌘, 그 밖에서는 Ctrl
keys.add('escape', closeDialog, { allowInInput: true });
keys.list(); // [{ combo: 'ctrl+k', label: '검색' }, …] 도움말 화면은 앱이 그립니다
off(); // 단축키 하나 해제. keys.destroy()는 모두 해제같은 조합은 가장 나중에 등록한 단축키가 실행됩니다. 대화상자가 열려 있는 동안 escape를 가져갔다가 off()로 돌려줄 수 있습니다. 플러그인은 화면을 그리지 않습니다.
공식 플러그인 — vf.ext.list
키가 있는 목록입니다. 컴포넌트의 render는 안쪽을 통째로 다시 그리므로 행이 많고 몇 행만 자주 바뀌는 목록(갱신, 교환, 삭제)에서는 느립니다. 이 플러그인은 항목마다 요소 하나를 유지하고 바뀐 행만 다시 그리며, 순서가 바뀌면 요소를 옮기기만 합니다. 수치는 FAQ에 있습니다.
import vfList from 'vfunc/plugins/list'; // <script>는 dist/plugins/list.min.js → 전역 vfList
const list = vf.use(vfList);
const rows = list.create('#tbody', { // 행의 직접 부모(tbody, ul, div…). 다른 요소를 넣지 않습니다
key: (item) => item.id,
render: (item) => vf.html`<tr><td>${item.name}</td><td><button type="button" data-action="remove">삭제</button></td></tr>`
});
rows.set(items); // 같은 키·같은 객체는 그대로, 새 객체는 그 행만 다시 그림
rows.set(rows.items().filter((x) => x.id !== id)); // 삭제: 그 행만 제거- 항목을 바꿀 때는 새 객체를 만듭니다. 객체를 제자리에서 고쳤으면
rows.refresh(key)를 부릅니다. - 각 행에는
data-vf-key가 붙습니다. 위임 핸들러에서e.target.closest('[data-vf-key]')로 항목을 찾습니다. - 컴포넌트 안에서는 컨테이너에
data-vf-keep을 붙여 컴포넌트의 render가 행을 지우지 않게 하고,onMount에서 만들어onDestroy에서rows.destroy()합니다. render 없는vf.attach대상에 만들어도 됩니다. render는vf.html로 요소 하나를 돌려줍니다. 문자열은 텍스트로 이스케이프되어 행이 되지 않습니다.vfunc-ui.js(1.2.0부터)에는 이 플러그인이 들어 있어vf.vfListView는 이미 행을 키별로 유지합니다.list.min.js는 목록을 직접 만들 때만 불러옵니다.
IE11·Edge IE 모드
엔진은 하나이고 배포 파일만 다릅니다. vfunc.legacy.min.js는 ES5로 변환되고 Promise 폴리필 하나를 담습니다(gzip 약 10KB).
- 앱 코드도 ES5여야 합니다. 화살표 함수,
const/let, 템플릿 리터럴, 클래스를 쓰지 않습니다. 마크업은vf.tpl('<b>{name}</b>', data)로 만듭니다. - 라우터는
hash모드를 쓰고, CSS는 고정값으로(IE11은 CSS 변수가 없음) 씁니다. - 앱에 필요한 다른 기능(
fetch,Object.assign등)은 직접 폴리필합니다. - 공개 사이트는
<script type="module">과<script nomodule>을 함께 두는 패턴을 씁니다. 예제 16을 Edge IE 모드에서 열어 확인할 수 있습니다.