viewlogic 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +621 -716
- package/dist/viewlogic-router.js +295 -469
- package/dist/viewlogic-router.js.map +3 -3
- package/dist/viewlogic-router.min.js +3 -3
- package/dist/viewlogic-router.min.js.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
>
|
|
15
|
+
> **Complete Vue 3 Framework**: Routing + State Management + Authentication + i18n + Caching + API + Forms
|
|
16
16
|
|
|
17
17
|
## 🎯 Core Philosophy: Simplicity Through Design
|
|
18
18
|
|
|
@@ -24,907 +24,812 @@ ViewLogic Router revolutionizes Vue development with two fundamental core princi
|
|
|
24
24
|
### 🚀 Zero Build Development
|
|
25
25
|
**Zero build step required in development mode**. Work directly with source files, see changes instantly without any compilation, bundling, or build processes. True real-time development experience.
|
|
26
26
|
|
|
27
|
+
## 🚀 Why Choose ViewLogic Router?
|
|
28
|
+
|
|
29
|
+
**All-in-One Solution** - Replace 7+ libraries with one unified framework:
|
|
30
|
+
- 🔀 **Vue Router** → ViewLogic Routing
|
|
31
|
+
- 📦 **Pinia/Vuex** → StateHandler
|
|
32
|
+
- 🔐 **Auth Libraries** → AuthManager
|
|
33
|
+
- 🌐 **Vue I18n** → I18nManager
|
|
34
|
+
- 💾 **Cache Libraries** → CacheManager
|
|
35
|
+
- 🌐 **API Libraries** → ApiHandler
|
|
36
|
+
- 📝 **Form Libraries** → FormHandler
|
|
37
|
+
|
|
38
|
+
**Tiny Bundle Size** - Complete framework in just 51KB minified / 17KB gzipped!
|
|
39
|
+
|
|
40
|
+
**Easy Integration** - Drop-in UMD build available for instant usage without build tools.
|
|
41
|
+
|
|
27
42
|
## ✨ Key Features
|
|
28
43
|
|
|
29
|
-
- 🚀 **Ultra-Lightweight** - Complete routing system
|
|
30
|
-
- 🔄 **Multiple API Support** - Parallel data fetching from multiple APIs with named data storage
|
|
44
|
+
- 🚀 **Ultra-Lightweight** - Complete routing system with zero dependencies and optimized codebase
|
|
45
|
+
- 🔄 **Multiple API Support** - Parallel data fetching from multiple APIs with named data storage
|
|
31
46
|
- 📝 **Automatic Form Handling** - Revolutionary form submission with `{paramName}` variable parameters
|
|
32
47
|
- 🛠️ **Built-in Components** - Preloaded UI components including revolutionary DynamicInclude & HtmlInclude
|
|
33
48
|
- 🔗 **Query-Based Parameter System** - Simple query-only parameters (`/users?id=123`) instead of complex path parameters
|
|
34
49
|
- ⚡ **Optimized Production** - Pre-built individual route bundles for lightning-fast production
|
|
35
50
|
- 📁 **Intuitive Structure** - Organized folder structure for views, logic, styles, layouts, and components
|
|
36
|
-
- 💾 **Smart Caching** - Intelligent route and component caching
|
|
37
|
-
- 🔐 **Authentication** - Built-in auth management
|
|
38
|
-
- 🌐 **i18n Ready** - Built-in internationalization support
|
|
51
|
+
- 💾 **Smart Caching** - Intelligent route and component caching with TTL and LRU eviction
|
|
52
|
+
- 🔐 **Authentication** - Built-in JWT auth management with multiple storage options
|
|
53
|
+
- 🌐 **i18n Ready** - Built-in internationalization support with lazy loading
|
|
54
|
+
- 📊 **State Management** - Reactive state management without external dependencies
|
|
55
|
+
|
|
56
|
+
### What's Included
|
|
57
|
+
- ✅ Complete routing system with hash/history mode
|
|
58
|
+
- ✅ Advanced caching with TTL and size limits
|
|
59
|
+
- ✅ Built-in authentication with multiple storage options
|
|
60
|
+
- ✅ Internationalization system with lazy loading
|
|
61
|
+
- ✅ Form handling with automatic validation
|
|
62
|
+
- ✅ RESTful API client with parameter substitution
|
|
63
|
+
- ✅ Component loading and management
|
|
64
|
+
- ✅ Error handling and logging system
|
|
65
|
+
- ✅ Query parameter management
|
|
66
|
+
- ✅ Layout system with slot-based composition
|
|
67
|
+
- ✅ Global state management with reactivity
|
|
68
|
+
- ✅ Event system for component communication
|
|
39
69
|
|
|
40
|
-
##
|
|
70
|
+
## 🏗️ Project Structure
|
|
41
71
|
|
|
42
|
-
|
|
72
|
+
ViewLogic Router follows a clean, intuitive folder structure that promotes maintainability:
|
|
43
73
|
|
|
44
|
-
```bash
|
|
45
|
-
npm create viewlogic my-app
|
|
46
|
-
cd my-app
|
|
47
|
-
# Ready to go! No additional setup needed
|
|
48
74
|
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
project/
|
|
76
|
+
├── src/
|
|
77
|
+
│ ├── views/ # HTML templates (pure presentation)
|
|
78
|
+
│ │ ├── home.html
|
|
79
|
+
│ │ ├── user-profile.html
|
|
80
|
+
│ │ └── dashboard.html
|
|
81
|
+
│ ├── logic/ # Vue component logic (pure JavaScript)
|
|
82
|
+
│ │ ├── home.js
|
|
83
|
+
│ │ ├── user-profile.js
|
|
84
|
+
│ │ └── dashboard.js
|
|
85
|
+
│ ├── components/ # Reusable UI components
|
|
86
|
+
│ │ ├── UserCard.vue
|
|
87
|
+
│ │ └── NavigationMenu.vue
|
|
88
|
+
│ ├── layouts/ # Layout templates
|
|
89
|
+
│ │ ├── default.html
|
|
90
|
+
│ │ └── admin.html
|
|
91
|
+
│ ├── styles/ # CSS files
|
|
92
|
+
│ │ ├── home.css
|
|
93
|
+
│ │ └── global.css
|
|
94
|
+
│ └── locales/ # i18n files (optional)
|
|
95
|
+
│ ├── en.json
|
|
96
|
+
│ └── ko.json
|
|
97
|
+
├── index.html # Main entry point
|
|
98
|
+
├── package.json
|
|
99
|
+
└── config.json # Optional configuration
|
|
53
100
|
```
|
|
54
101
|
|
|
55
102
|
## 🚀 Quick Start
|
|
56
103
|
|
|
57
|
-
###
|
|
104
|
+
### Method 1: ES6 Modules (Recommended)
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm install viewlogic
|
|
108
|
+
```
|
|
58
109
|
|
|
59
110
|
```html
|
|
60
111
|
<!DOCTYPE html>
|
|
61
112
|
<html>
|
|
62
113
|
<head>
|
|
63
|
-
<
|
|
64
|
-
<title>My ViewLogic App - Development</title>
|
|
65
|
-
<link rel="stylesheet" href="/css/base.css">
|
|
114
|
+
<title>My ViewLogic App</title>
|
|
66
115
|
</head>
|
|
67
116
|
<body>
|
|
68
117
|
<div id="app"></div>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
<script>
|
|
75
|
-
// Development mode - loads files directly from src/
|
|
76
|
-
ViewLogicRouter({
|
|
77
|
-
environment: 'development',
|
|
118
|
+
<script type="module">
|
|
119
|
+
import { ViewLogicRouter } from 'viewlogic';
|
|
120
|
+
const router = new ViewLogicRouter({
|
|
121
|
+
authEnabled: true,
|
|
122
|
+
useI18n: true
|
|
78
123
|
});
|
|
79
124
|
</script>
|
|
80
125
|
</body>
|
|
81
126
|
</html>
|
|
82
127
|
```
|
|
83
128
|
|
|
84
|
-
###
|
|
129
|
+
### Method 2: UMD Build (No Build Tools)
|
|
85
130
|
|
|
86
131
|
```html
|
|
87
132
|
<!DOCTYPE html>
|
|
88
133
|
<html>
|
|
89
134
|
<head>
|
|
90
|
-
<meta charset="UTF-8">
|
|
91
135
|
<title>My ViewLogic App</title>
|
|
92
|
-
<
|
|
136
|
+
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
|
137
|
+
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.umd.js"></script>
|
|
93
138
|
</head>
|
|
94
139
|
<body>
|
|
95
140
|
<div id="app"></div>
|
|
96
|
-
|
|
97
|
-
<!-- Vue 3 (production version) -->
|
|
98
|
-
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
|
|
99
|
-
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.umd.js"></script>
|
|
100
|
-
|
|
101
141
|
<script>
|
|
102
|
-
|
|
103
|
-
ViewLogicRouter({
|
|
104
|
-
environment: 'production',
|
|
105
|
-
useI18n: true,
|
|
106
|
-
logLevel: 'error' // Only log errors
|
|
107
|
-
});
|
|
142
|
+
const router = new ViewLogicRouter();
|
|
108
143
|
</script>
|
|
109
144
|
</body>
|
|
110
145
|
</html>
|
|
111
146
|
```
|
|
112
147
|
|
|
113
|
-
###
|
|
148
|
+
### Create Your First Route
|
|
114
149
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
150
|
+
**src/views/home.html**
|
|
151
|
+
```html
|
|
152
|
+
<div class="home">
|
|
153
|
+
<h1>{{ message }}</h1>
|
|
154
|
+
<p>Counter: {{ count }} | Global Count: {{ globalCount }}</p>
|
|
155
|
+
<button @click="increment">Local +1</button>
|
|
156
|
+
<button @click="incrementGlobal">Global +1</button>
|
|
157
|
+
<button @click="saveUser" :disabled="loading">
|
|
158
|
+
{{ loading ? 'Saving...' : 'Save User' }}
|
|
159
|
+
</button>
|
|
160
|
+
<div v-if="user">
|
|
161
|
+
<h3>{{ $t('welcome.message', { name: user.name }) }}</h3>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
124
164
|
```
|
|
125
165
|
|
|
126
|
-
|
|
127
|
-
|
|
166
|
+
**src/logic/home.js**
|
|
128
167
|
```javascript
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
168
|
+
export default {
|
|
169
|
+
data() {
|
|
170
|
+
return {
|
|
171
|
+
message: 'Welcome to ViewLogic!',
|
|
172
|
+
count: 0,
|
|
173
|
+
loading: false
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
computed: {
|
|
177
|
+
globalCount() {
|
|
178
|
+
return this.$state.get('globalCounter', 0);
|
|
179
|
+
},
|
|
180
|
+
user() {
|
|
181
|
+
return this.$state.get('currentUser');
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
methods: {
|
|
185
|
+
increment() {
|
|
186
|
+
this.count++;
|
|
187
|
+
},
|
|
188
|
+
incrementGlobal() {
|
|
189
|
+
const current = this.$state.get('globalCounter', 0);
|
|
190
|
+
this.$state.set('globalCounter', current + 1);
|
|
191
|
+
},
|
|
192
|
+
async saveUser() {
|
|
193
|
+
this.loading = true;
|
|
194
|
+
try {
|
|
195
|
+
const userData = {
|
|
196
|
+
name: 'John Doe',
|
|
197
|
+
email: 'john@example.com'
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const savedUser = await this.$api.post('/api/users', userData);
|
|
201
|
+
this.$state.set('currentUser', savedUser);
|
|
202
|
+
|
|
203
|
+
this.$toast?.success?.('User saved successfully!');
|
|
204
|
+
} catch (error) {
|
|
205
|
+
this.$toast?.error?.('Failed to save user');
|
|
206
|
+
} finally {
|
|
207
|
+
this.loading = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
mounted() {
|
|
212
|
+
// Watch for global state changes
|
|
213
|
+
this.$state.watch('currentUser', (newUser) => {
|
|
214
|
+
console.log('User changed globally:', newUser);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
136
218
|
```
|
|
137
219
|
|
|
138
|
-
##
|
|
220
|
+
## 🎯 Core APIs
|
|
139
221
|
|
|
140
|
-
###
|
|
141
|
-
```
|
|
142
|
-
my-app/
|
|
143
|
-
├── index.html
|
|
144
|
-
├── i18n/ # Language files (top-level)
|
|
145
|
-
│ ├── ko.json
|
|
146
|
-
│ └── en.json
|
|
147
|
-
├── css/ # Global styles
|
|
148
|
-
│ └── base.css # Base styles for entire site
|
|
149
|
-
├── js/ # System files (optional, can use CDN)
|
|
150
|
-
│ ├── viewlogic-router.js
|
|
151
|
-
│ ├── viewlogic-router.min.js
|
|
152
|
-
│ └── viewlogic-router.umd.js
|
|
153
|
-
├── src/ # Source files (not deployed)
|
|
154
|
-
│ ├── views/ # View templates (HTML)
|
|
155
|
-
│ │ ├── home.html
|
|
156
|
-
│ │ ├── about.html
|
|
157
|
-
│ │ └── products/
|
|
158
|
-
│ │ ├── list.html
|
|
159
|
-
│ │ └── detail.html
|
|
160
|
-
│ ├── logic/ # Business logic (JavaScript)
|
|
161
|
-
│ │ ├── home.js
|
|
162
|
-
│ │ ├── about.js
|
|
163
|
-
│ │ └── products/
|
|
164
|
-
│ │ ├── list.js
|
|
165
|
-
│ │ └── detail.js
|
|
166
|
-
│ ├── styles/ # Page-specific CSS
|
|
167
|
-
│ │ ├── home.css
|
|
168
|
-
│ │ ├── about.css
|
|
169
|
-
│ │ └── products/
|
|
170
|
-
│ │ ├── list.css
|
|
171
|
-
│ │ └── detail.css
|
|
172
|
-
│ ├── layouts/ # Layout templates
|
|
173
|
-
│ │ ├── default.html
|
|
174
|
-
│ │ └── admin.html
|
|
175
|
-
│ └── components/ # Reusable components
|
|
176
|
-
│ ├── Button.js
|
|
177
|
-
│ ├── Modal.js
|
|
178
|
-
│ └── Card.js
|
|
179
|
-
└── package.json
|
|
180
|
-
```
|
|
222
|
+
### State Management
|
|
181
223
|
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
my-app/
|
|
185
|
-
├── index.html
|
|
186
|
-
├── i18n/ # Language files
|
|
187
|
-
│ ├── ko.json
|
|
188
|
-
│ └── en.json
|
|
189
|
-
├── css/ # Global styles
|
|
190
|
-
│ └── base.css
|
|
191
|
-
├── js/ # Router system (or use CDN)
|
|
192
|
-
│ ├── viewlogic-router.umd.js
|
|
193
|
-
│ └── viewlogic-router.min.js
|
|
194
|
-
├── routes/ # Built & optimized route bundles
|
|
195
|
-
│ ├── home.js # Combined view + logic + style
|
|
196
|
-
│ ├── about.js
|
|
197
|
-
│ └── products/
|
|
198
|
-
│ ├── list.js
|
|
199
|
-
│ └── detail.js
|
|
200
|
-
└── assets/ # Static assets
|
|
201
|
-
├── images/
|
|
202
|
-
└── fonts/
|
|
203
|
-
|
|
204
|
-
Note: src/ folder is excluded from production deployment
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
## 🔧 Configuration Options
|
|
224
|
+
ViewLogic Router includes a powerful built-in state management system:
|
|
208
225
|
|
|
209
226
|
```javascript
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
cacheTTL: 300000, // Cache TTL in milliseconds
|
|
224
|
-
maxCacheSize: 50, // Maximum cache entries
|
|
225
|
-
|
|
226
|
-
// Components
|
|
227
|
-
useComponents: true, // Enable built-in components
|
|
228
|
-
componentNames: [ // Components to preload
|
|
229
|
-
'Button', 'Modal', 'Card', 'Toast',
|
|
230
|
-
'Input', 'Tabs', 'Checkbox', 'Alert'
|
|
231
|
-
],
|
|
232
|
-
|
|
233
|
-
// Internationalization
|
|
234
|
-
useI18n: true, // Enable i18n
|
|
235
|
-
defaultLanguage: 'ko', // Default language
|
|
236
|
-
|
|
237
|
-
// Authentication
|
|
238
|
-
authEnabled: false, // Enable authentication
|
|
239
|
-
loginRoute: 'login', // Login route name
|
|
240
|
-
protectedRoutes: [], // Protected route names
|
|
241
|
-
publicRoutes: ['login', 'register', 'home'],
|
|
242
|
-
authStorage: 'cookie', // 'cookie' or 'localStorage'
|
|
243
|
-
|
|
244
|
-
// Security
|
|
245
|
-
enableParameterValidation: true,
|
|
246
|
-
maxParameterLength: 1000,
|
|
247
|
-
maxParameterCount: 50,
|
|
248
|
-
|
|
249
|
-
// Development
|
|
250
|
-
logLevel: 'info', // 'debug', 'info', 'warn', 'error'
|
|
251
|
-
enableErrorReporting: true
|
|
252
|
-
};
|
|
253
|
-
```
|
|
227
|
+
// Set state (any component can access)
|
|
228
|
+
this.$state.set('user', { name: 'John', age: 30 });
|
|
229
|
+
this.$state.set('theme', 'dark');
|
|
230
|
+
this.$state.set('shoppingCart', []);
|
|
231
|
+
|
|
232
|
+
// Get state with optional default
|
|
233
|
+
const user = this.$state.get('user');
|
|
234
|
+
const theme = this.$state.get('theme', 'light');
|
|
235
|
+
|
|
236
|
+
// Check if state exists
|
|
237
|
+
if (this.$state.has('user')) {
|
|
238
|
+
console.log('User is logged in');
|
|
239
|
+
}
|
|
254
240
|
|
|
255
|
-
|
|
241
|
+
// Watch for changes (reactive)
|
|
242
|
+
this.$state.watch('user', (newValue, oldValue) => {
|
|
243
|
+
console.log('User changed:', newValue);
|
|
244
|
+
this.updateUI();
|
|
245
|
+
});
|
|
256
246
|
|
|
257
|
-
|
|
247
|
+
// Stop watching
|
|
248
|
+
this.$state.unwatch('user', callbackFunction);
|
|
258
249
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
i18nPath: '/i18n' // → https://example.com/i18n
|
|
250
|
+
// Bulk updates
|
|
251
|
+
this.$state.update({
|
|
252
|
+
theme: 'dark',
|
|
253
|
+
language: 'ko',
|
|
254
|
+
sidebar: 'collapsed'
|
|
265
255
|
});
|
|
266
256
|
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
basePath: 'src', // → https://example.com/myapp/src (relative)
|
|
270
|
-
routesPath: 'routes', // → https://example.com/myapp/routes (relative)
|
|
271
|
-
i18nPath: 'i18n', // → https://example.com/myapp/i18n (relative)
|
|
272
|
-
});
|
|
257
|
+
// Get all state
|
|
258
|
+
const allState = this.$state.getAll();
|
|
273
259
|
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
basePath: './src', // → https://example.com/projects/myapp/src
|
|
277
|
-
routesPath: '../shared/routes', // → https://example.com/projects/shared/routes
|
|
278
|
-
i18nPath: '/global/i18n' // → https://example.com/global/i18n (absolute)
|
|
279
|
-
});
|
|
260
|
+
// Clear specific state
|
|
261
|
+
this.$state.delete('temporaryData');
|
|
280
262
|
```
|
|
281
263
|
|
|
282
|
-
|
|
283
|
-
- **Absolute paths** (`/path`) → `https://domain.com/path`
|
|
284
|
-
- **Relative paths** (`path`, `./path`) → Resolved from current page location
|
|
285
|
-
- **Parent paths** (`../path`) → Navigate up directory levels
|
|
286
|
-
- **HTTP URLs** → Used as-is (no processing)
|
|
287
|
-
|
|
288
|
-
### 🔄 Hash vs History Mode in Subfolders
|
|
264
|
+
### Authentication System
|
|
289
265
|
|
|
290
|
-
|
|
266
|
+
Complete authentication management with multiple storage options:
|
|
291
267
|
|
|
292
268
|
```javascript
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
mode: 'hash' // Works anywhere, no server config needed
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
// History Mode (requires server configuration)
|
|
300
|
-
// URL: https://example.com/myapp/products?id=123
|
|
301
|
-
ViewLogicRouter({
|
|
302
|
-
mode: 'history' // Cleaner URLs, needs server setup
|
|
303
|
-
});
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
**History Mode Server Configuration:**
|
|
307
|
-
```nginx
|
|
308
|
-
# Nginx - redirect all subfolder requests to index.html
|
|
309
|
-
location /myapp/ {
|
|
310
|
-
try_files $uri $uri/ /myapp/index.html;
|
|
269
|
+
// Check authentication status
|
|
270
|
+
if (this.$isAuthenticated()) {
|
|
271
|
+
console.log('User is logged in');
|
|
311
272
|
}
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
```apache
|
|
315
|
-
# Apache .htaccess in /myapp/ folder
|
|
316
|
-
RewriteEngine On
|
|
317
|
-
RewriteBase /myapp/
|
|
318
|
-
RewriteRule ^index\.html$ - [L]
|
|
319
|
-
RewriteCond %{REQUEST_FILENAME} !-f
|
|
320
|
-
RewriteCond %{REQUEST_FILENAME} !-d
|
|
321
|
-
RewriteRule . /myapp/index.html [L]
|
|
322
|
-
```
|
|
323
273
|
|
|
324
|
-
|
|
274
|
+
// Login with token
|
|
275
|
+
this.$setToken('jwt-token-here');
|
|
325
276
|
|
|
326
|
-
|
|
277
|
+
// Login with options
|
|
278
|
+
this.$setToken('jwt-token', {
|
|
279
|
+
storage: 'localStorage', // 'localStorage', 'sessionStorage', 'cookie'
|
|
280
|
+
skipValidation: false // Skip JWT validation
|
|
281
|
+
});
|
|
327
282
|
|
|
328
|
-
|
|
283
|
+
// Get current token
|
|
284
|
+
const token = this.$getToken();
|
|
329
285
|
|
|
330
|
-
|
|
286
|
+
// Login success handling (redirects to protected route or default)
|
|
287
|
+
this.$loginSuccess('/dashboard');
|
|
331
288
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
const router = new ViewLogicRouter({ environment: 'development' });
|
|
335
|
-
router.navigateTo('products', { id: 123, category: 'electronics' });
|
|
336
|
-
const current = router.getCurrentRoute();
|
|
289
|
+
// Logout (clears token and redirects to login)
|
|
290
|
+
this.$logout();
|
|
337
291
|
|
|
338
|
-
//
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
this.navigateTo('detail', { id }); // Navigate
|
|
344
|
-
console.log('Data loaded:', this.products); // From dataURL
|
|
345
|
-
if (this.$isAuthenticated()) { /* auth check */ }
|
|
346
|
-
const text = this.$t('welcome.message'); // i18n
|
|
347
|
-
}
|
|
348
|
-
};
|
|
292
|
+
// Manual auth check for specific route
|
|
293
|
+
const authResult = await this.$checkAuth('admin-panel');
|
|
294
|
+
if (authResult.allowed) {
|
|
295
|
+
// User can access admin panel
|
|
296
|
+
}
|
|
349
297
|
```
|
|
350
298
|
|
|
351
|
-
###
|
|
352
|
-
|
|
353
|
-
-
|
|
354
|
-
- **Data Fetching**: `$fetchData()`, `$fetchAllData()` (with dataURL)
|
|
355
|
-
- **Authentication**: `$isAuthenticated()`, `$getToken()`, `$logout()`
|
|
356
|
-
- **Forms**: Auto-binding with `action` attribute and `{param}` templates
|
|
357
|
-
- **i18n**: `$t(key, params)` for translations
|
|
299
|
+
### API Management
|
|
300
|
+
|
|
301
|
+
Built-in HTTP client with automatic token injection and parameter substitution:
|
|
358
302
|
|
|
359
|
-
### Auto-Injected Properties
|
|
360
303
|
```javascript
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
```
|
|
304
|
+
// GET request
|
|
305
|
+
const users = await this.$api.get('/api/users');
|
|
364
306
|
|
|
365
|
-
|
|
307
|
+
// GET with query parameters
|
|
308
|
+
const filteredUsers = await this.$api.get('/api/users', {
|
|
309
|
+
params: { role: 'admin', active: true }
|
|
310
|
+
});
|
|
366
311
|
|
|
367
|
-
|
|
312
|
+
// POST with data
|
|
313
|
+
const newUser = await this.$api.post('/api/users', {
|
|
314
|
+
name: 'John',
|
|
315
|
+
email: 'john@example.com'
|
|
316
|
+
});
|
|
368
317
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
- **🔄 Hot Reload**: Instant changes without compilation or bundling
|
|
318
|
+
// PUT/PATCH/DELETE
|
|
319
|
+
await this.$api.put('/api/users/{userId}', userData);
|
|
320
|
+
await this.$api.patch('/api/users/{userId}', partialData);
|
|
321
|
+
await this.$api.delete('/api/users/{userId}');
|
|
374
322
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
- **Style**: `src/styles/products.css` - Pure CSS styles
|
|
323
|
+
// Parameter substitution (automatically uses route/query params)
|
|
324
|
+
// If current route is /users?userId=123, this becomes /api/users/123
|
|
325
|
+
const user = await this.$api.get('/api/users/{userId}');
|
|
379
326
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
327
|
+
// Custom headers
|
|
328
|
+
const response = await this.$api.post('/api/secure-endpoint', data, {
|
|
329
|
+
headers: { 'X-Custom-Header': 'value' }
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Automatic data loading in components
|
|
383
333
|
export default {
|
|
384
|
-
|
|
385
|
-
dataURL: '/api/
|
|
386
|
-
|
|
387
|
-
|
|
334
|
+
// Single API endpoint
|
|
335
|
+
dataURL: '/api/user/profile',
|
|
336
|
+
|
|
337
|
+
// Multiple endpoints
|
|
338
|
+
dataURL: {
|
|
339
|
+
profile: '/api/user/profile',
|
|
340
|
+
posts: '/api/posts?userId={userId}',
|
|
341
|
+
notifications: '/api/notifications'
|
|
388
342
|
},
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
343
|
+
|
|
344
|
+
mounted() {
|
|
345
|
+
// Data automatically loaded and available as:
|
|
346
|
+
// this.profile, this.posts, this.notifications
|
|
393
347
|
}
|
|
394
348
|
};
|
|
395
349
|
```
|
|
396
350
|
|
|
397
|
-
###
|
|
398
|
-
All separate files automatically combine into optimized bundles in `routes/` folder - maintaining the development philosophy while optimizing for production.
|
|
399
|
-
|
|
400
|
-
## 🔄 Zero Build Development vs Optimized Production
|
|
401
|
-
|
|
402
|
-
ViewLogic Router's **Zero Build Development** (core philosophy) vs optimized production:
|
|
351
|
+
### Internationalization
|
|
403
352
|
|
|
404
|
-
|
|
405
|
-
|------|------------|-------|----------|------------|
|
|
406
|
-
| **Development** | **Zero Build Required** | Separate files | 4 per route | **Real-time, instant changes** |
|
|
407
|
-
| **Production** | **Optimized Performance** | Single bundle | 1 per route | **Lightning-fast loading** |
|
|
353
|
+
Comprehensive i18n system with lazy loading and pluralization:
|
|
408
354
|
|
|
409
355
|
```javascript
|
|
410
|
-
//
|
|
411
|
-
|
|
356
|
+
// Simple translation
|
|
357
|
+
const message = this.$t('welcome.message');
|
|
412
358
|
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
```
|
|
359
|
+
// With parameters
|
|
360
|
+
const greeting = this.$t('hello.user', { name: 'John', role: 'admin' });
|
|
416
361
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
- 🚀 **Zero Setup** - No webpack, vite, or build tools required
|
|
420
|
-
- 🎯 **True Hot Reload** - Files load directly from src/ folder
|
|
421
|
-
- 🛠️ **Pure Development** - Focus on code, not build configuration
|
|
362
|
+
// Nested keys
|
|
363
|
+
const errorMsg = this.$t('errors.validation.email.required');
|
|
422
364
|
|
|
423
|
-
|
|
365
|
+
// Plural forms
|
|
366
|
+
const itemCount = this.$plural('items.count', count, { count });
|
|
367
|
+
// items.count.singular: "{count} item"
|
|
368
|
+
// items.count.plural: "{count} items"
|
|
424
369
|
|
|
425
|
-
|
|
370
|
+
// Check current language
|
|
371
|
+
const currentLang = this.$i18n.getCurrentLanguage();
|
|
426
372
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
- **Vue Router + Auth + i18n + Cache**: 50KB+ gzipped
|
|
373
|
+
// Change language (automatically reloads interface)
|
|
374
|
+
await this.$i18n.setLanguage('ko');
|
|
430
375
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
- ✅ Component lazy loading
|
|
438
|
-
- ✅ Layout system
|
|
439
|
-
- ✅ Error handling
|
|
440
|
-
- ✅ Development/production modes
|
|
441
|
-
- ✅ **Automatic data fetching with dataURL**
|
|
442
|
-
- ✅ **Revolutionary DynamicInclude & HtmlInclude components**
|
|
443
|
-
- ✅ **Automatic form handling with variable parameters**
|
|
444
|
-
- ✅ **10+ Built-in UI components (Button, Modal, Card, etc.)**
|
|
445
|
-
|
|
446
|
-
### Why So Small?
|
|
447
|
-
- **Zero Dependencies** - No external libraries required (except Vue 3)
|
|
448
|
-
- **Tree-Shakable** - Only includes what you use
|
|
449
|
-
- **Optimized Code** - Hand-crafted for minimal bundle size
|
|
450
|
-
- **Smart Bundling** - Efficient code organization and minification
|
|
451
|
-
|
|
452
|
-
### Performance Benefits
|
|
453
|
-
- **Faster Load Times** - 70% smaller than typical Vue router setups
|
|
454
|
-
- **Better UX** - Instant page loads with minimal JavaScript overhead
|
|
455
|
-
- **Mobile Optimized** - Perfect for mobile-first applications
|
|
456
|
-
- **CDN Friendly** - Small size ideal for CDN distribution
|
|
457
|
-
|
|
458
|
-
## 🏆 Performance Comparison
|
|
459
|
-
|
|
460
|
-
### Bundle Size Comparison
|
|
461
|
-
| Router System | Bundle Size (Gzipped) | Features Included |
|
|
462
|
-
|---------------|----------------------|------------------|
|
|
463
|
-
| **ViewLogic Router** | **13KB** | Routing + Auth + i18n + Cache + Query + Components |
|
|
464
|
-
| Vue Router | 12KB | Routing only |
|
|
465
|
-
| Vue Router + Pinia | 18KB | Routing + State |
|
|
466
|
-
| React Router | 15KB | Routing only |
|
|
467
|
-
| Next.js Router | 25KB+ | Routing + SSR |
|
|
468
|
-
| Nuxt Router | 30KB+ | Routing + SSR + Meta |
|
|
469
|
-
|
|
470
|
-
### Runtime Performance Comparison
|
|
471
|
-
|
|
472
|
-
#### Traditional SPA Routing
|
|
473
|
-
```
|
|
474
|
-
Route Change Process:
|
|
475
|
-
├── 1️⃣ Parse route
|
|
476
|
-
├── 2️⃣ Load component bundle
|
|
477
|
-
├── 3️⃣ Execute component code
|
|
478
|
-
├── 4️⃣ Load template (if separate)
|
|
479
|
-
├── 5️⃣ Load styles (if separate)
|
|
480
|
-
├── 6️⃣ Apply i18n translations
|
|
481
|
-
├── 7️⃣ Check authentication
|
|
482
|
-
└── 8️⃣ Render component
|
|
483
|
-
|
|
484
|
-
Total: Multiple operations + Bundle parsing
|
|
485
|
-
```
|
|
376
|
+
// Format dates/numbers according to locale
|
|
377
|
+
const date = this.$i18n.formatDate(new Date(), {
|
|
378
|
+
year: 'numeric',
|
|
379
|
+
month: 'long',
|
|
380
|
+
day: 'numeric'
|
|
381
|
+
});
|
|
486
382
|
|
|
487
|
-
|
|
383
|
+
const price = this.$i18n.formatNumber(1234.56, {
|
|
384
|
+
style: 'currency',
|
|
385
|
+
currency: 'USD'
|
|
386
|
+
});
|
|
488
387
|
```
|
|
489
|
-
Route Change Process:
|
|
490
|
-
├── 1️⃣ Load pre-built route bundle (all-in-one)
|
|
491
|
-
└── 2️⃣ Render component
|
|
492
388
|
|
|
493
|
-
|
|
494
|
-
```
|
|
389
|
+
### Navigation & Routing
|
|
495
390
|
|
|
496
|
-
|
|
497
|
-
- **🚀 75% Faster Loading** - Pre-bundled routes vs on-demand compilation
|
|
498
|
-
- **📦 Smaller Footprint** - 13KB includes everything others need 30KB+ for
|
|
499
|
-
- **⚡ Instant Navigation** - No build-time compilation in production
|
|
500
|
-
- **🎯 Route-Level Optimization** - Each route is independently optimized
|
|
501
|
-
- **💾 Superior Caching** - Route-level caching vs component-level caching
|
|
502
|
-
- **🔄 Zero Hydration** - No server-side rendering complexity
|
|
391
|
+
Simple yet powerful routing system:
|
|
503
392
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
3. **Zero Dependencies**: No additional libraries needed for full functionality
|
|
508
|
-
4. **Smart Caching**: Route-level caching with intelligent invalidation
|
|
509
|
-
5. **Optimized Architecture**: Purpose-built for maximum performance
|
|
510
|
-
6. **Revolutionary Components**: DynamicInclude & HtmlInclude for dynamic content loading
|
|
393
|
+
```javascript
|
|
394
|
+
// Navigate to route
|
|
395
|
+
this.navigateTo('user-profile');
|
|
511
396
|
|
|
512
|
-
|
|
397
|
+
// With query parameters
|
|
398
|
+
this.navigateTo('user-profile', { userId: 123, tab: 'settings' });
|
|
513
399
|
|
|
514
|
-
|
|
400
|
+
// Object syntax
|
|
401
|
+
this.navigateTo({
|
|
402
|
+
route: 'search-results',
|
|
403
|
+
params: { query: 'vue', category: 'tutorials' }
|
|
404
|
+
});
|
|
515
405
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
page="login"
|
|
521
|
-
:use-cache="false"
|
|
522
|
-
loading-text="로그인 페이지 로딩 중..."
|
|
523
|
-
wrapper-class="test-dynamic-include"
|
|
524
|
-
:params="{
|
|
525
|
-
returnUrl: '/dashboard',
|
|
526
|
-
showWelcome: true,
|
|
527
|
-
theme: 'compact',
|
|
528
|
-
testMode: true
|
|
529
|
-
}"
|
|
530
|
-
/>
|
|
531
|
-
```
|
|
406
|
+
// Get current route information
|
|
407
|
+
const currentRoute = this.$route.current;
|
|
408
|
+
const routeParams = this.$route.params;
|
|
409
|
+
const queryParams = this.$route.query;
|
|
532
410
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
- **Slot Support** - Custom loading and error templates
|
|
538
|
-
- **Cache Integration** - Automatic caching with TTL support
|
|
411
|
+
// Access specific parameters
|
|
412
|
+
const userId = this.$params.userId; // Route parameter
|
|
413
|
+
const tab = this.$query.tab; // Query parameter
|
|
414
|
+
const searchTerm = this.$param('search'); // Either route or query param
|
|
539
415
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
:
|
|
546
|
-
:
|
|
547
|
-
|
|
548
|
-
wrapper-class="test-html-include"
|
|
549
|
-
/>
|
|
416
|
+
// Check if route is protected
|
|
417
|
+
const isProtected = this.$route.isProtected('admin-dashboard');
|
|
418
|
+
|
|
419
|
+
// Navigate with state preservation
|
|
420
|
+
this.navigateTo('dashboard', {
|
|
421
|
+
preserveState: true,
|
|
422
|
+
scrollTop: false
|
|
423
|
+
});
|
|
550
424
|
```
|
|
551
425
|
|
|
552
|
-
|
|
553
|
-
- **Raw HTML Rendering** - Safely render dynamic HTML content
|
|
554
|
-
- **XSS Protection** - Built-in HTML sanitization
|
|
555
|
-
- **Vue Integration** - HTML content works with Vue reactivity
|
|
556
|
-
- **Fallback Support** - Default content when HTML is unavailable
|
|
557
|
-
- **Script Execution** - Optional JavaScript execution in HTML content
|
|
426
|
+
### Form Handling
|
|
558
427
|
|
|
559
|
-
|
|
428
|
+
Revolutionary form processing with automatic parameter substitution:
|
|
560
429
|
|
|
561
|
-
|
|
430
|
+
```html
|
|
431
|
+
<!-- Basic form with automatic handling -->
|
|
432
|
+
<form action="/api/users" method="POST">
|
|
433
|
+
<input name="name" v-model="userData.name" required>
|
|
434
|
+
<input name="email" v-model="userData.email" type="email" required>
|
|
435
|
+
<button type="submit">Create User</button>
|
|
436
|
+
</form>
|
|
562
437
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
// Data is already fetched and available!
|
|
577
|
-
console.log('Products loaded:', this.products);
|
|
578
|
-
console.log('Loading state:', this.$dataLoading);
|
|
579
|
-
},
|
|
580
|
-
methods: {
|
|
581
|
-
async refreshData() {
|
|
582
|
-
// Manual refresh if needed
|
|
583
|
-
await this.$fetchData();
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
};
|
|
438
|
+
<!-- Form with parameter substitution -->
|
|
439
|
+
<form action="/api/users/{userId}" method="PUT">
|
|
440
|
+
<input name="name" v-model="user.name">
|
|
441
|
+
<input name="email" v-model="user.email">
|
|
442
|
+
<button type="submit">Update User</button>
|
|
443
|
+
</form>
|
|
444
|
+
|
|
445
|
+
<!-- File upload form -->
|
|
446
|
+
<form action="/api/upload" method="POST" enctype="multipart/form-data">
|
|
447
|
+
<input name="avatar" type="file" accept="image/*">
|
|
448
|
+
<input name="description" v-model="description">
|
|
449
|
+
<button type="submit">Upload</button>
|
|
450
|
+
</form>
|
|
587
451
|
```
|
|
588
452
|
|
|
589
|
-
#### Multiple APIs (Advanced Usage) - 🆕 Revolutionary!
|
|
590
453
|
```javascript
|
|
591
|
-
//
|
|
454
|
+
// Programmatic form submission
|
|
592
455
|
export default {
|
|
593
|
-
name: 'DashboardMain',
|
|
594
|
-
dataURL: {
|
|
595
|
-
products: '/api/products',
|
|
596
|
-
categories: '/api/categories',
|
|
597
|
-
stats: '/api/dashboard/stats',
|
|
598
|
-
user: '/api/user/profile'
|
|
599
|
-
}, // ✨ Multiple APIs with named data!
|
|
600
|
-
data() {
|
|
601
|
-
return {
|
|
602
|
-
title: 'Dashboard'
|
|
603
|
-
// products: [], categories: [], stats: {}, user: {}
|
|
604
|
-
// All auto-populated from respective APIs!
|
|
605
|
-
};
|
|
606
|
-
},
|
|
607
|
-
mounted() {
|
|
608
|
-
// All APIs called in parallel, data available by name!
|
|
609
|
-
console.log('Products:', this.products);
|
|
610
|
-
console.log('Categories:', this.categories);
|
|
611
|
-
console.log('Stats:', this.stats);
|
|
612
|
-
console.log('User:', this.user);
|
|
613
|
-
console.log('Loading state:', this.$dataLoading);
|
|
614
|
-
},
|
|
615
456
|
methods: {
|
|
616
|
-
async
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
457
|
+
async submitForm() {
|
|
458
|
+
const formData = {
|
|
459
|
+
name: this.userData.name,
|
|
460
|
+
email: this.userData.email
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
const result = await this.$form.submit('/api/users', formData, {
|
|
465
|
+
method: 'POST',
|
|
466
|
+
onProgress: (progress) => {
|
|
467
|
+
this.uploadProgress = progress;
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
this.$state.set('newUser', result);
|
|
472
|
+
this.navigateTo('user-profile', { userId: result.id });
|
|
473
|
+
} catch (error) {
|
|
474
|
+
this.handleError(error);
|
|
475
|
+
}
|
|
627
476
|
}
|
|
628
477
|
}
|
|
629
478
|
};
|
|
630
479
|
```
|
|
631
480
|
|
|
632
|
-
|
|
633
|
-
- **Zero-Config API Calls** - Just define `dataURL` and data is automatically fetched
|
|
634
|
-
- **🆕 Multiple API Support** - Define multiple APIs with custom names
|
|
635
|
-
- **🚀 Parallel Processing** - Multiple APIs called simultaneously for best performance
|
|
636
|
-
- **🎯 Selective Refresh** - Refresh specific APIs independently
|
|
637
|
-
- **Query Parameter Integration** - Current route parameters are automatically sent to all APIs
|
|
638
|
-
- **Loading State Management** - `$dataLoading` property automatically managed
|
|
639
|
-
- **Advanced Error Handling** - Per-API error handling with detailed events
|
|
640
|
-
- **Named Data Storage** - Each API result stored with its defined name
|
|
641
|
-
- **Event Support** - `@data-loaded` and `@data-error` events with detailed info
|
|
481
|
+
## ⚙️ Configuration
|
|
642
482
|
|
|
643
|
-
###
|
|
483
|
+
### Basic Configuration
|
|
644
484
|
|
|
645
|
-
|
|
485
|
+
```javascript
|
|
486
|
+
const router = new ViewLogicRouter({
|
|
487
|
+
// Core routing settings
|
|
488
|
+
basePath: '/', // Base path for the application
|
|
489
|
+
mode: 'hash', // 'hash' or 'history'
|
|
646
490
|
|
|
647
|
-
|
|
491
|
+
// State management (always enabled)
|
|
492
|
+
// No configuration needed - StateHandler is always available
|
|
648
493
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
494
|
+
// Authentication settings
|
|
495
|
+
authEnabled: true, // Enable authentication system
|
|
496
|
+
loginRoute: 'login', // Route name for login page
|
|
497
|
+
protectedRoutes: ['dashboard', 'profile', 'admin'],
|
|
498
|
+
protectedPrefixes: ['admin/', 'secure/'],
|
|
499
|
+
publicRoutes: ['login', 'register', 'home', 'about'],
|
|
500
|
+
authStorage: 'localStorage', // 'localStorage', 'sessionStorage', 'cookie'
|
|
654
501
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
502
|
+
// Internationalization
|
|
503
|
+
useI18n: true, // Enable i18n system
|
|
504
|
+
defaultLanguage: 'en', // Default language
|
|
505
|
+
i18nPath: '/src/locales', // Path to language files
|
|
506
|
+
|
|
507
|
+
// Caching system
|
|
508
|
+
cacheMode: 'memory', // 'memory' only (others removed for simplicity)
|
|
509
|
+
cacheTTL: 300000, // Cache TTL in milliseconds (5 minutes)
|
|
510
|
+
maxCacheSize: 100, // Maximum number of cached items
|
|
511
|
+
|
|
512
|
+
// API settings
|
|
513
|
+
apiBaseURL: '/api', // Base URL for API requests
|
|
514
|
+
apiTimeout: 10000, // Request timeout in milliseconds
|
|
515
|
+
|
|
516
|
+
// Development settings
|
|
517
|
+
environment: 'development', // 'development' or 'production'
|
|
518
|
+
logLevel: 'info' // 'error', 'warn', 'info', 'debug'
|
|
519
|
+
});
|
|
520
|
+
```
|
|
661
521
|
|
|
662
|
-
|
|
522
|
+
### Advanced Configuration
|
|
663
523
|
|
|
664
|
-
|
|
524
|
+
```javascript
|
|
525
|
+
const router = new ViewLogicRouter({
|
|
526
|
+
// Custom authentication function
|
|
527
|
+
authEnabled: true,
|
|
528
|
+
checkAuthFunction: async (routeName) => {
|
|
529
|
+
const token = localStorage.getItem('authToken');
|
|
530
|
+
if (!token) return false;
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
const response = await fetch('/api/auth/verify', {
|
|
534
|
+
method: 'POST',
|
|
535
|
+
headers: {
|
|
536
|
+
'Authorization': `Bearer ${token}`,
|
|
537
|
+
'Content-Type': 'application/json'
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
if (response.ok) {
|
|
542
|
+
const userData = await response.json();
|
|
543
|
+
// Store user data in global state
|
|
544
|
+
router.stateHandler.set('currentUser', userData);
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
} catch (error) {
|
|
549
|
+
console.error('Auth verification failed:', error);
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
},
|
|
665
553
|
|
|
666
|
-
|
|
554
|
+
// Custom route loading
|
|
555
|
+
routeResolver: (routeName) => {
|
|
556
|
+
// Custom logic for resolving route files
|
|
557
|
+
if (routeName.startsWith('admin/')) {
|
|
558
|
+
return {
|
|
559
|
+
viewPath: `/admin/views/${routeName.slice(6)}.html`,
|
|
560
|
+
logicPath: `/admin/logic/${routeName.slice(6)}.js`
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
return null; // Use default resolution
|
|
564
|
+
},
|
|
667
565
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
<button type="submit">Send Message</button>
|
|
677
|
-
</form>
|
|
678
|
-
</div>
|
|
679
|
-
```
|
|
566
|
+
// Global error handler
|
|
567
|
+
onError: (error, context) => {
|
|
568
|
+
console.error('ViewLogic Error:', error, context);
|
|
569
|
+
// Send to error tracking service
|
|
570
|
+
if (window.errorTracker) {
|
|
571
|
+
window.errorTracker.log(error, context);
|
|
572
|
+
}
|
|
573
|
+
},
|
|
680
574
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
575
|
+
// Global route change handler
|
|
576
|
+
onRouteChange: (newRoute, oldRoute) => {
|
|
577
|
+
// Analytics tracking
|
|
578
|
+
if (window.analytics) {
|
|
579
|
+
window.analytics.track('page_view', {
|
|
580
|
+
route: newRoute,
|
|
581
|
+
previous_route: oldRoute
|
|
582
|
+
});
|
|
583
|
+
}
|
|
689
584
|
}
|
|
690
|
-
};
|
|
585
|
+
});
|
|
691
586
|
```
|
|
692
587
|
|
|
693
|
-
|
|
588
|
+
## 📦 Bundle Size Comparison
|
|
694
589
|
|
|
695
|
-
|
|
590
|
+
| Solution | Minified | Gzipped |
|
|
591
|
+
|----------|----------|---------|
|
|
592
|
+
| **ViewLogic Router** (Complete) | **51KB** | **17KB** |
|
|
593
|
+
| Vue Router + Pinia + Vue I18n | 150KB+ | 45KB+ |
|
|
594
|
+
| React Router + Redux + i18next | 200KB+ | 60KB+ |
|
|
595
|
+
| Next.js (Runtime) | 300KB+ | 85KB+ |
|
|
696
596
|
|
|
697
|
-
|
|
698
|
-
<!-- Dynamic form actions with variable parameters -->
|
|
699
|
-
<form action="/api/users/{userId}/posts" method="POST"
|
|
700
|
-
data-success="handlePostSuccess"
|
|
701
|
-
data-error="handlePostError">
|
|
702
|
-
<input type="text" name="title" required placeholder="Post Title">
|
|
703
|
-
<textarea name="content" required placeholder="Post Content"></textarea>
|
|
704
|
-
<button type="submit">Create Post</button>
|
|
705
|
-
</form>
|
|
597
|
+
*ViewLogic Router provides the functionality of 7+ libraries in a package smaller than most single-purpose libraries.*
|
|
706
598
|
|
|
707
|
-
|
|
708
|
-
<form action="/api/orders/{orderId}/update" method="PUT"
|
|
709
|
-
data-success="orderUpdated"
|
|
710
|
-
data-redirect="/orders">
|
|
711
|
-
<input type="number" name="quantity" required>
|
|
712
|
-
<select name="status">
|
|
713
|
-
<option value="pending">Pending</option>
|
|
714
|
-
<option value="processing">Processing</option>
|
|
715
|
-
<option value="completed">Completed</option>
|
|
716
|
-
</select>
|
|
717
|
-
<button type="submit">Update Order</button>
|
|
718
|
-
</form>
|
|
599
|
+
## 🎯 Migration Guide
|
|
719
600
|
|
|
720
|
-
|
|
721
|
-
<form action="/api/profile/{userId}/avatar" method="POST" enctype="multipart/form-data"
|
|
722
|
-
data-success="avatarUploaded">
|
|
723
|
-
<input type="file" name="avatar" accept="image/*" required>
|
|
724
|
-
<button type="submit">Upload Avatar</button>
|
|
725
|
-
</form>
|
|
726
|
-
```
|
|
601
|
+
### From Vue Router + Pinia
|
|
727
602
|
|
|
603
|
+
**Before:**
|
|
728
604
|
```javascript
|
|
729
|
-
//
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
605
|
+
// main.js - Multiple library setup
|
|
606
|
+
import { createApp } from 'vue';
|
|
607
|
+
import { createRouter, createWebHistory } from 'vue-router';
|
|
608
|
+
import { createPinia } from 'pinia';
|
|
609
|
+
import { createI18n } from 'vue-i18n';
|
|
610
|
+
import App from './App.vue';
|
|
611
|
+
|
|
612
|
+
const router = createRouter({
|
|
613
|
+
history: createWebHistory(),
|
|
614
|
+
routes: [/* complex route config */]
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
const pinia = createPinia();
|
|
618
|
+
const i18n = createI18n({/* complex i18n config */});
|
|
619
|
+
|
|
620
|
+
const app = createApp(App);
|
|
621
|
+
app.use(router).use(pinia).use(i18n);
|
|
622
|
+
app.mount('#app');
|
|
747
623
|
```
|
|
748
624
|
|
|
749
|
-
|
|
625
|
+
**After:**
|
|
626
|
+
```javascript
|
|
627
|
+
// index.html - Single setup
|
|
628
|
+
import { ViewLogicRouter } from 'viewlogic';
|
|
750
629
|
|
|
751
|
-
|
|
630
|
+
const router = new ViewLogicRouter({
|
|
631
|
+
mode: 'history',
|
|
632
|
+
useI18n: true,
|
|
633
|
+
authEnabled: true
|
|
634
|
+
});
|
|
635
|
+
// Everything is auto-configured and ready to use!
|
|
636
|
+
```
|
|
752
637
|
|
|
753
|
-
|
|
754
|
-
2. **Component Data**: `this.paramName` - from component's data properties
|
|
755
|
-
3. **Computed Properties**: `this.paramName` - from component's computed properties
|
|
638
|
+
### State Management Migration
|
|
756
639
|
|
|
640
|
+
**Before (Pinia):**
|
|
757
641
|
```javascript
|
|
758
|
-
//
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
642
|
+
// stores/user.js
|
|
643
|
+
import { defineStore } from 'pinia';
|
|
644
|
+
|
|
645
|
+
export const useUserStore = defineStore('user', {
|
|
646
|
+
state: () => ({
|
|
647
|
+
currentUser: null,
|
|
648
|
+
preferences: {}
|
|
649
|
+
}),
|
|
650
|
+
getters: {
|
|
651
|
+
isLoggedIn: (state) => !!state.currentUser,
|
|
652
|
+
userName: (state) => state.currentUser?.name || 'Guest'
|
|
766
653
|
},
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
654
|
+
actions: {
|
|
655
|
+
setUser(userData) {
|
|
656
|
+
this.currentUser = userData;
|
|
657
|
+
},
|
|
658
|
+
updatePreferences(prefs) {
|
|
659
|
+
this.preferences = { ...this.preferences, ...prefs };
|
|
770
660
|
}
|
|
771
|
-
},
|
|
772
|
-
mounted() {
|
|
773
|
-
// Route parameters also work: /user-profile?userId=789
|
|
774
|
-
// {userId} will use 789 from URL, or fall back to data() value of 123
|
|
775
661
|
}
|
|
776
|
-
};
|
|
777
|
-
```
|
|
662
|
+
});
|
|
778
663
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
<input type="email" name="email" required>
|
|
784
|
-
<button type="submit">Subscribe</button>
|
|
785
|
-
</form>
|
|
664
|
+
// In components
|
|
665
|
+
import { useUserStore } from '@/stores/user';
|
|
666
|
+
const userStore = useUserStore();
|
|
667
|
+
userStore.setUser(userData);
|
|
786
668
|
```
|
|
669
|
+
|
|
670
|
+
**After (ViewLogic):**
|
|
787
671
|
```javascript
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
672
|
+
// In any component - no store files needed!
|
|
673
|
+
this.$state.set('currentUser', userData);
|
|
674
|
+
this.$state.update({ preferences: newPrefs });
|
|
675
|
+
|
|
676
|
+
// Computed properties work naturally
|
|
677
|
+
computed: {
|
|
678
|
+
isLoggedIn() {
|
|
679
|
+
return !!this.$state.get('currentUser');
|
|
680
|
+
},
|
|
681
|
+
userName() {
|
|
682
|
+
return this.$state.get('currentUser')?.name || 'Guest';
|
|
792
683
|
}
|
|
793
|
-
}
|
|
684
|
+
}
|
|
794
685
|
```
|
|
795
686
|
|
|
796
|
-
|
|
797
|
-
```html
|
|
798
|
-
<form action="/api/resource/{id}" method="POST"
|
|
799
|
-
data-success="handleSuccess" data-error="handleError"
|
|
800
|
-
data-redirect="/success" data-confirm="Sure?"
|
|
801
|
-
enctype="multipart/form-data">
|
|
802
|
-
<input name="title" required>
|
|
803
|
-
<input type="file" name="file" accept=".pdf">
|
|
804
|
-
<button type="submit">Submit</button>
|
|
805
|
-
</form>
|
|
806
|
-
```
|
|
687
|
+
## 🔧 Production Build
|
|
807
688
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
<form action="/api/protected/resource" method="POST">
|
|
812
|
-
<input name="data" required>
|
|
813
|
-
<button type="submit">Save</button>
|
|
814
|
-
</form>
|
|
815
|
-
<!-- Authorization: Bearer <token> header added automatically -->
|
|
816
|
-
```
|
|
689
|
+
```bash
|
|
690
|
+
# Development mode (zero build)
|
|
691
|
+
npm run dev
|
|
817
692
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
<!-- HTML5 + custom validation -->
|
|
821
|
-
<form action="/api/register" method="POST">
|
|
822
|
-
<input type="email" name="email" required pattern="...">
|
|
823
|
-
<input type="password" name="password" minlength="8" required>
|
|
824
|
-
<button type="submit">Register</button>
|
|
825
|
-
</form>
|
|
826
|
-
```
|
|
693
|
+
# Production build with optimization
|
|
694
|
+
npm run build
|
|
827
695
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
<!-- User profile with dynamic parameters -->
|
|
831
|
-
<form action="/api/users/{userId}" method="PUT" data-success="profileUpdated">
|
|
832
|
-
<input name="firstName" required>
|
|
833
|
-
<button type="submit">Update</button>
|
|
834
|
-
</form>
|
|
696
|
+
# Preview production build
|
|
697
|
+
npm run serve
|
|
835
698
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
<select name="status" required>
|
|
839
|
-
<option value="pending">Pending</option>
|
|
840
|
-
<option value="shipped">Shipped</option>
|
|
841
|
-
</select>
|
|
842
|
-
<button type="submit">Update</button>
|
|
843
|
-
</form>
|
|
699
|
+
# Build with custom config
|
|
700
|
+
npm run build -- --config custom.config.js
|
|
844
701
|
```
|
|
845
702
|
|
|
846
|
-
###
|
|
847
|
-
- ✅ **Zero Setup** - Just add `action` attribute vs manual event handlers
|
|
848
|
-
- ✅ **Variable Parameters** - `{userId}` template syntax vs manual interpolation
|
|
849
|
-
- ✅ **Auto Authentication** - Tokens injected automatically
|
|
850
|
-
- ✅ **File Uploads** - Automatic multipart support
|
|
851
|
-
- ✅ **Built-in Validation** - HTML5 + custom functions
|
|
852
|
-
|
|
853
|
-
### Code Comparison
|
|
854
|
-
**Traditional**: 30+ lines of boilerplate for forms, API calls, loading states
|
|
855
|
-
**ViewLogic**: 5 lines with `action` attribute + callback method
|
|
856
|
-
**Result**: 80% less code, more features included
|
|
703
|
+
### Production Optimizations
|
|
857
704
|
|
|
858
|
-
|
|
705
|
+
ViewLogic Router automatically optimizes for production:
|
|
859
706
|
|
|
860
|
-
|
|
707
|
+
- **Code splitting**: Each route becomes a separate bundle
|
|
708
|
+
- **Tree shaking**: Unused features are eliminated
|
|
709
|
+
- **Minification**: Code is compressed and optimized
|
|
710
|
+
- **Caching**: Aggressive caching for static assets
|
|
711
|
+
- **Lazy loading**: Routes and components load on demand
|
|
861
712
|
|
|
862
|
-
|
|
713
|
+
## 🌟 Advanced Examples
|
|
863
714
|
|
|
864
|
-
###
|
|
865
|
-
1. **📍 Simple URLs**: `/product?id=123&category=electronics` (clear and readable)
|
|
866
|
-
2. **🎯 Consistent Access**: Always use `this.getParam('id')` - never mix path/query paradigms
|
|
867
|
-
3. **⚡ No Route Configuration**: No complex route definitions or parameter mappings needed
|
|
868
|
-
4. **🔍 SEO Friendly**: Descriptive parameter names make URLs self-documenting
|
|
869
|
-
5. **🌐 Universal Compatibility**: Query parameters work everywhere - no framework lock-in
|
|
715
|
+
### E-commerce Dashboard
|
|
870
716
|
|
|
871
|
-
### Simple Usage Example
|
|
872
717
|
```javascript
|
|
873
|
-
//
|
|
874
|
-
this.navigateTo('products', { id: 123, category: 'electronics' });
|
|
875
|
-
// → /products?id=123&category=electronics
|
|
876
|
-
|
|
877
|
-
// Access parameters - always the same way
|
|
718
|
+
// logic/dashboard.js
|
|
878
719
|
export default {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
}
|
|
884
|
-
};
|
|
885
|
-
```
|
|
720
|
+
dataURL: {
|
|
721
|
+
overview: '/api/dashboard/overview',
|
|
722
|
+
sales: '/api/sales/recent',
|
|
723
|
+
products: '/api/products/trending'
|
|
724
|
+
},
|
|
886
725
|
|
|
887
|
-
|
|
888
|
-
|
|
726
|
+
computed: {
|
|
727
|
+
totalRevenue() {
|
|
728
|
+
return this.sales?.reduce((sum, sale) => sum + sale.amount, 0) || 0;
|
|
729
|
+
},
|
|
730
|
+
cartItems() {
|
|
731
|
+
return this.$state.get('shoppingCart', []);
|
|
732
|
+
}
|
|
733
|
+
},
|
|
889
734
|
|
|
890
|
-
|
|
735
|
+
methods: {
|
|
736
|
+
async addToCart(product) {
|
|
737
|
+
const cart = this.$state.get('shoppingCart', []);
|
|
738
|
+
const existingItem = cart.find(item => item.id === product.id);
|
|
891
739
|
|
|
740
|
+
if (existingItem) {
|
|
741
|
+
existingItem.quantity += 1;
|
|
742
|
+
} else {
|
|
743
|
+
cart.push({ ...product, quantity: 1 });
|
|
744
|
+
}
|
|
892
745
|
|
|
893
|
-
|
|
746
|
+
this.$state.set('shoppingCart', cart);
|
|
894
747
|
|
|
895
|
-
|
|
748
|
+
// Sync with backend
|
|
749
|
+
await this.$api.post('/api/cart/sync', { items: cart });
|
|
750
|
+
},
|
|
896
751
|
|
|
897
|
-
|
|
752
|
+
async checkout() {
|
|
753
|
+
const items = this.$state.get('shoppingCart', []);
|
|
754
|
+
try {
|
|
755
|
+
const order = await this.$api.post('/api/orders', { items });
|
|
756
|
+
this.$state.set('shoppingCart', []);
|
|
757
|
+
this.$state.set('lastOrder', order);
|
|
758
|
+
this.navigateTo('order-confirmation', { orderId: order.id });
|
|
759
|
+
} catch (error) {
|
|
760
|
+
this.$toast?.error?.('Checkout failed. Please try again.');
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
```
|
|
898
766
|
|
|
899
|
-
|
|
900
|
-
2. **Deploy**: Set `environment: 'production'` and use CDN or local files
|
|
901
|
-
3. **Structure**: Deploy `routes/`, `css/`, `i18n/` folders (exclude `src/`)
|
|
767
|
+
### Multi-tenant Application
|
|
902
768
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
769
|
+
```javascript
|
|
770
|
+
// Handle different tenants with state management
|
|
771
|
+
export default {
|
|
772
|
+
async mounted() {
|
|
773
|
+
const tenantId = this.$params.tenantId || this.$query.tenant;
|
|
774
|
+
|
|
775
|
+
if (tenantId !== this.$state.get('currentTenant')?.id) {
|
|
776
|
+
await this.switchTenant(tenantId);
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
methods: {
|
|
781
|
+
async switchTenant(tenantId) {
|
|
782
|
+
try {
|
|
783
|
+
const tenant = await this.$api.get('/api/tenants/{tenantId}', {
|
|
784
|
+
params: { tenantId }
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Update global state
|
|
788
|
+
this.$state.update({
|
|
789
|
+
currentTenant: tenant,
|
|
790
|
+
userPermissions: tenant.permissions,
|
|
791
|
+
theme: tenant.branding.theme
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
// Update language if tenant has preference
|
|
795
|
+
if (tenant.defaultLanguage) {
|
|
796
|
+
await this.$i18n.setLanguage(tenant.defaultLanguage);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Update API base URL for tenant
|
|
800
|
+
this.$api.setBaseURL(`/api/tenants/${tenantId}`);
|
|
801
|
+
|
|
802
|
+
} catch (error) {
|
|
803
|
+
this.navigateTo('tenant-not-found');
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
909
808
|
```
|
|
910
809
|
|
|
911
810
|
## 🤝 Contributing
|
|
912
811
|
|
|
913
|
-
|
|
812
|
+
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) for details on:
|
|
914
813
|
|
|
915
|
-
|
|
814
|
+
- Code style and conventions
|
|
815
|
+
- Testing requirements
|
|
816
|
+
- Pull request process
|
|
817
|
+
- Issue reporting guidelines
|
|
916
818
|
|
|
917
|
-
|
|
819
|
+
## 📄 License
|
|
918
820
|
|
|
919
|
-
|
|
821
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
920
822
|
|
|
921
|
-
|
|
823
|
+
## 🙏 Acknowledgments
|
|
922
824
|
|
|
923
|
-
|
|
825
|
+
Built with ❤️ for the Vue.js community. Special thanks to:
|
|
924
826
|
|
|
925
|
-
-
|
|
926
|
-
-
|
|
827
|
+
- Vue.js team for the amazing framework
|
|
828
|
+
- The open-source community for inspiration and feedback
|
|
829
|
+
- All contributors who helped shape ViewLogic Router
|
|
927
830
|
|
|
928
831
|
---
|
|
929
832
|
|
|
930
|
-
|
|
833
|
+
**ViewLogic Router** - One framework to rule them all! 🚀
|
|
834
|
+
|
|
835
|
+
*Simplify your Vue development with the power of unified architecture.*
|