Минималистичный реактивный фреймворк без зависимостей и chaining. CDN-ready.
- Без зависимостей — чистый JavaScript, никаких jQuery, scopex, ts-fns
- Без chaining — явные вызовы методов, проще отладка
- Реактивность — Proxy-based state management
- Батчинг обновлений — throttle 16ms + requestAnimationFrame
- Директивы — w-for, w-if, w-show, w-bind, w-model, w-text, w-html, w-on, w-route, w-navigate, w-ref, w-class, w-static
- Хуки жизненного цикла — $mount, $update, $unmount, $change, $wiring
- AJAX модуль — Promise-based $wire (get, post, put, delete)
- Маршрутизация — встроенный router с поддержкой параметров
- Сохранение состояния — localStorage персистентность
- Утилиты — debounce, throttle, nextTick
- CDN-ready — UMD сборка для подключения через
<script>
<divid="app"><p>Счётчик: {{ count }}</p><button@click="increment">+</button></div><scriptsrc="miniwex.js"></script><script>constapp=MiniWex.createApp('#app',{count: 0},{methods: {increment: (e,state)=>{state.count++}}})app.mount()</script>constapp=MiniWex.createApp(selector,initialState,options)Параметры:
selector— CSS-селектор корневого элементаinitialState— начальное состояние (объект)options— конфигурация:methods— методы компонентаcomputed— вычисляемые свойстваhooks— хуки жизненного циклаstorage— ключ для localStorage персистентности
constapp=MiniWex.createApp('#app',{count: 0,user: {name: 'John',age: 30}},{computed: {doubleCount: (state)=>state.count*2,isAdult: (state)=>state.user.age>=18}})Изменения состояния автоматически обновляют DOM с батчингом (throttle 16ms).
<p>{{ message }}</p><p>{{ user.name }} - {{ user.age }} лет</p><ul><liw-for="item in items">
{{ item.name }} ({{ index }})
</li></ul><divw-if="isLoggedIn">Привет, {{ user.name }}!</div><divw-show="hasNotifications">🔔 {{ notificationCount }}</div><imgw-bind:src:imageUrlalt="Photo"><aw-bind:href:linkUrl>Click me</a><inputw-bind:value:inputValue><inputtype="text" w-model="username"><inputtype="checkbox" w-model="isChecked"><inputtype="radio" w-model="selectedOption" value="opt1"><button@click="handleClick">Click</button><input@input="handleInput"><form@submit="handleSubmit"><divw-class:active:isActive">Active item</div><buttonw-class:disabled:isDisabled">Submit</button><ahref="/about" w-route>О нас</a><buttonw-navigate:click:/contact>Контакты</button><inputw-ref:myInput>Доступ через app.refs.myInput
<divw-text="message"></div><divw-html="htmlContent"></div>Помечает блок как статический. Фреймворк не будет отслеживать изменения внутри этого элемента и его потомков. Используйте для крупного неизменяемого контента (статьи, футеры, иконки), чтобы ускорить работу приложения.
<articlew-static><h1>Заголовок статьи</h1><p>Этот текст никогда не изменится и не будет перерисовываться.</p><p>Выражения {{ count }} внутри работать НЕ будут.</p></article>Важно: Внутри элементов с
w-staticреактивность отключена полностью. Интерполяция{{ }}и другие директивы на потомках игнорируются.
constapp=MiniWex.createApp('#app',{count: 0},{methods: {// Базовый методincrement: (e,state)=>{state.count++},// Метод с доступом к приложениюloadData: async(e,state,app)=>{constdata=awaitapp.$wire.get('/api/data')state.items=data},// Метод с параметрами из событияremoveItem: (e,state)=>{constid=e.target.dataset.idstate.items=state.items.filter(item=>item.id!==id)}}})constapp=MiniWex.createApp('#app',{count: 0},{hooks: {// Вызывается после монтирования$mount: ({ state, refs })=>{console.log('App mounted',state)},// Вызывается после обновления DOM$update: ({ keys, state })=>{console.log('Updated keys:',keys)},// Вызывается при изменении состояния$change: ({ key, oldValue, newValue, state })=>{console.log(`${key}: ${oldValue} → ${newValue}`)},// Вызывается перед удалением$unmount: ({ state })=>{console.log('App unmounted')},// Вызывается при AJAX запросах$wiring: ({ url, method, data })=>{console.log('API call:',method,url)}}})// GET запросconstdata=awaitapp.$wire.get('/api/users')// POST запросconstnewUser=awaitapp.$wire.post('/api/users',{name: 'John',email: 'john@example.com'})// PUT запросawaitapp.$wire.put('/api/users/1',{name: 'John Updated'})// DELETE запросawaitapp.$wire.delete('/api/users/1')// С опциямиconstdata=awaitapp.$wire.get('/api/data',{headers: {'Authorization': 'Bearer token'}})constapp=MiniWex.createApp('#app',{currentRoute: '/',page: 'home'},{hooks: {$mount: ({ state },app)=>{// Регистрация маршрутовapp.route('/',()=>{state.page='home'state.currentRoute='/'})app.route('/about',()=>{state.page='about'state.currentRoute='/about'})app.route('/user/:id',(path,params)=>{state.page='user'state.userId=params.id})// Навигацияapp.navigate('/about')// Получение текущего маршрутаconstcurrent=app.router.getCurrentRoute()}}})constapp=MiniWex.createApp('#app',{settings: {theme: 'light',lang: 'ru'}},{storage: 'myAppSettings'// Ключ в localStorage})app.mount()// Ручное сохранениеapp.persist('myAppSettings')// DebounceconstdebouncedSearch=MiniWex.debounce((query)=>{console.log('Search:',query)},300)// ThrottleconstthrottledScroll=MiniWex.throttle(()=>{console.log('Scroll position:',window.scrollY)},100)// Next tickMiniWex.nextTick(()=>{console.log('DOM updated')})// Другие утилитыMiniWex.utils.deepClone(obj)MiniWex.utils.isObject(val)MiniWex.utils.generateUid('prefix')| Метод | Описание |
|---|---|
app.mount() | Монтирует приложение |
app.unmount() | Удаляет приложение |
app.navigate(path) | Переход по маршруту |
app.route(path, handler) | Регистрация маршрута |
app.persist(key) | Сохранение в localStorage |
app.nextTick(fn) | Выполнение после обновления DOM |
app.component(name, config) | Регистрация компонента |
| Возможность | Wex | MiniWex |
|---|---|---|
| Зависимости | jQuery, scopex, ts-fns | Нет |
| Chaining | ✅ | ❌ |
| Реактивность | Proxy | Proxy |
| Батчинг | ✅ | ✅ (throttle 16ms) |
| Директивы | 12+ | 12 |
| Хуки | ✅ | ✅ |
| AJAX | ✅ (chaining) | ✅ (Promise) |
| Роутер | ✅ | ✅ |
| localStorage | ✅ | ✅ |
| Размер | ~50KB+ | ~10KB |
| CDN | ✅ | ✅ |
Смотрите examples.html для полноценных примеров:
- Счётчик
- TODO список
- Загрузка API данных
- Маршрутизация
GPLv3