viewlogic 1.2.2 → 1.2.4
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 +532 -857
- package/dist/viewlogic-router.js +293 -466
- 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,1080 +12,755 @@
|
|
|
12
12
|
</a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
>
|
|
15
|
+
> **Complete Vue 3 Framework**: All-in-one solution for modern web development
|
|
16
16
|
|
|
17
|
-
## 🎯 Core Philosophy
|
|
17
|
+
## 🎯 Core Philosophy
|
|
18
18
|
|
|
19
|
-
ViewLogic Router revolutionizes Vue development with two fundamental
|
|
19
|
+
ViewLogic Router revolutionizes Vue development with two fundamental principles:
|
|
20
20
|
|
|
21
21
|
### 🎭 View-Logic Separation
|
|
22
|
-
**Complete separation between View (presentation) and Logic (business logic)**. Views are pure HTML templates, logic is pure JavaScript components
|
|
22
|
+
**Complete separation between View (presentation) and Logic (business logic)**. Views are pure HTML templates, logic is pure JavaScript components. This separation makes your code more maintainable, testable, and scalable.
|
|
23
23
|
|
|
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
|
-
## ✨
|
|
27
|
+
## ✨ What Makes ViewLogic Special
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- 🔐 **Authentication** - Built-in auth management system
|
|
38
|
-
- 🌐 **i18n Ready** - Built-in internationalization support
|
|
29
|
+
**All-in-One Solution** - Replace multiple libraries with one unified framework:
|
|
30
|
+
- 🔀 **Routing** - File-based routing with zero configuration
|
|
31
|
+
- 📦 **State Management** - Built-in reactive state without external dependencies
|
|
32
|
+
- 🔐 **Authentication** - JWT auth with multiple storage options
|
|
33
|
+
- 🌐 **Internationalization** - Multi-language support with lazy loading
|
|
34
|
+
- 💾 **Caching** - Smart caching with TTL and LRU eviction
|
|
35
|
+
- 🌐 **API Client** - HTTP client with automatic token injection
|
|
36
|
+
- 📝 **Form Handling** - Revolutionary form processing with parameter substitution
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
**Tiny Bundle Size** - Complete framework in just **51KB minified / 17KB gzipped**!
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
**Easy Integration** - Drop-in UMD build available for instant usage without build tools.
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
## 🏗️ Project Structure
|
|
43
|
+
|
|
44
|
+
ViewLogic Router follows a clean, intuitive folder structure:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
project/
|
|
48
|
+
├── src/
|
|
49
|
+
│ ├── views/ # HTML templates (pure presentation)
|
|
50
|
+
│ │ ├── home.html
|
|
51
|
+
│ │ ├── user-profile.html
|
|
52
|
+
│ │ └── dashboard.html
|
|
53
|
+
│ ├── logic/ # Vue component logic (pure JavaScript)
|
|
54
|
+
│ │ ├── home.js
|
|
55
|
+
│ │ ├── user-profile.js
|
|
56
|
+
│ │ └── dashboard.js
|
|
57
|
+
│ ├── components/ # Reusable UI components
|
|
58
|
+
│ │ ├── UserCard.vue
|
|
59
|
+
│ │ └── NavigationMenu.vue
|
|
60
|
+
│ ├── layouts/ # Layout templates
|
|
61
|
+
│ │ ├── default.html
|
|
62
|
+
│ │ └── admin.html
|
|
63
|
+
│ └── styles/ # Page-specific CSS files
|
|
64
|
+
│ ├── home.css
|
|
65
|
+
│ └── user-profile.css
|
|
66
|
+
├── css/ # Global CSS files
|
|
67
|
+
│ └── base.css # Base styles
|
|
68
|
+
├── js/ # JavaScript library files
|
|
69
|
+
│ ├── viewlogic-router.js # Development version
|
|
70
|
+
│ ├── viewlogic-router.min.js # Minified version
|
|
71
|
+
│ └── viewlogic-router.umd.js # UMD bundle
|
|
72
|
+
├── i18n/ # Internationalization files
|
|
73
|
+
│ ├── en.json # English translations
|
|
74
|
+
│ ├── ko.json # Korean translations
|
|
75
|
+
│ └── ja.json # Japanese translations
|
|
76
|
+
├── routes/ # Auto-generated after building
|
|
77
|
+
│ ├── home.js # Built route bundles
|
|
78
|
+
│ ├── user-profile.js
|
|
79
|
+
│ └── dashboard.js
|
|
80
|
+
├── index.html # Main entry point
|
|
81
|
+
└── package.json
|
|
48
82
|
```
|
|
49
83
|
|
|
50
|
-
|
|
84
|
+
## 🚀 Quick Start
|
|
85
|
+
|
|
86
|
+
### Method 1: Create New Project (Recommended)
|
|
87
|
+
|
|
51
88
|
```bash
|
|
52
|
-
npm
|
|
89
|
+
npm create viewlogic myapp
|
|
90
|
+
cd myapp
|
|
91
|
+
npm run dev
|
|
53
92
|
```
|
|
54
93
|
|
|
55
|
-
|
|
94
|
+
This creates a complete project with examples and starts the development server.
|
|
56
95
|
|
|
57
|
-
###
|
|
96
|
+
### Method 2: UMD Build (No Build Tools)
|
|
58
97
|
|
|
59
98
|
```html
|
|
60
99
|
<!DOCTYPE html>
|
|
61
100
|
<html>
|
|
62
101
|
<head>
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
<
|
|
102
|
+
<title>My ViewLogic App</title>
|
|
103
|
+
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
|
104
|
+
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.umd.js"></script>
|
|
66
105
|
</head>
|
|
67
106
|
<body>
|
|
68
107
|
<div id="app"></div>
|
|
69
|
-
|
|
70
|
-
<!-- Vue 3 (development version) -->
|
|
71
|
-
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
|
|
72
|
-
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.umd.js"></script>
|
|
73
|
-
|
|
74
108
|
<script>
|
|
75
|
-
// Development mode - loads files directly from src/
|
|
76
109
|
ViewLogicRouter({
|
|
77
|
-
environment: 'development'
|
|
110
|
+
environment: 'development'
|
|
78
111
|
});
|
|
79
112
|
</script>
|
|
80
113
|
</body>
|
|
81
114
|
</html>
|
|
82
115
|
```
|
|
83
116
|
|
|
84
|
-
###
|
|
117
|
+
### Method 3: ES6 Modules
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm install viewlogic
|
|
121
|
+
```
|
|
85
122
|
|
|
86
123
|
```html
|
|
87
124
|
<!DOCTYPE html>
|
|
88
125
|
<html>
|
|
89
126
|
<head>
|
|
90
|
-
<meta charset="UTF-8">
|
|
91
127
|
<title>My ViewLogic App</title>
|
|
92
|
-
<link rel="stylesheet" href="/css/base.css">
|
|
93
128
|
</head>
|
|
94
129
|
<body>
|
|
95
130
|
<div id="app"></div>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.umd.js"></script>
|
|
100
|
-
|
|
101
|
-
<script>
|
|
102
|
-
// Production mode - loads pre-built bundles from routes/
|
|
103
|
-
ViewLogicRouter({
|
|
131
|
+
<script type="module">
|
|
132
|
+
import { ViewLogicRouter } from 'viewlogic';
|
|
133
|
+
const router = new ViewLogicRouter({
|
|
104
134
|
environment: 'production',
|
|
105
|
-
|
|
106
|
-
|
|
135
|
+
authEnabled: true,
|
|
136
|
+
useI18n: true
|
|
107
137
|
});
|
|
108
138
|
</script>
|
|
109
139
|
</body>
|
|
110
140
|
</html>
|
|
111
141
|
```
|
|
112
142
|
|
|
113
|
-
###
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
import { ViewLogicRouter } from 'js/viewlogic-router.js';
|
|
143
|
+
### Create Your First Route
|
|
117
144
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
**src/views/home.html**
|
|
146
|
+
```html
|
|
147
|
+
<div class="home">
|
|
148
|
+
<h1>{{ message }}</h1>
|
|
149
|
+
<button @click="increment">Click me: {{ count }}</button>
|
|
150
|
+
</div>
|
|
124
151
|
```
|
|
125
152
|
|
|
126
|
-
|
|
127
|
-
|
|
153
|
+
**src/logic/home.js**
|
|
128
154
|
```javascript
|
|
129
|
-
|
|
155
|
+
export default {
|
|
156
|
+
// Optional: specify layout (defaults to 'default')
|
|
157
|
+
layout: 'default', // Use layouts/default.html
|
|
158
|
+
// layout: 'admin', // Use layouts/admin.html
|
|
159
|
+
// layout: null, // No layout (page-only content)
|
|
130
160
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
161
|
+
data() {
|
|
162
|
+
return {
|
|
163
|
+
message: 'Welcome to ViewLogic!',
|
|
164
|
+
count: 0
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
methods: {
|
|
168
|
+
increment() {
|
|
169
|
+
this.count++;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
136
173
|
```
|
|
137
174
|
|
|
138
|
-
|
|
175
|
+
**src/styles/home.css** (optional)
|
|
176
|
+
```css
|
|
177
|
+
.home {
|
|
178
|
+
padding: 2rem;
|
|
179
|
+
text-align: center;
|
|
180
|
+
}
|
|
139
181
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
182
|
+
.home h1 {
|
|
183
|
+
color: #2c3e50;
|
|
184
|
+
margin-bottom: 1rem;
|
|
185
|
+
}
|
|
180
186
|
```
|
|
181
187
|
|
|
182
|
-
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
188
|
+
**src/layouts/default.html** (optional)
|
|
189
|
+
```html
|
|
190
|
+
<!DOCTYPE html>
|
|
191
|
+
<html>
|
|
192
|
+
<head>
|
|
193
|
+
<title>My ViewLogic App</title>
|
|
194
|
+
<meta charset="utf-8">
|
|
195
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
196
|
+
</head>
|
|
197
|
+
<body>
|
|
198
|
+
<header class="navbar">
|
|
199
|
+
<h1>My App</h1>
|
|
200
|
+
<nav>
|
|
201
|
+
<a href="#/home">Home</a>
|
|
202
|
+
<a href="#/about">About</a>
|
|
203
|
+
</nav>
|
|
204
|
+
</header>
|
|
205
|
+
|
|
206
|
+
<main class="main-content">
|
|
207
|
+
<div class="container">
|
|
208
|
+
{{ content }}
|
|
209
|
+
</div>
|
|
210
|
+
</main>
|
|
211
|
+
|
|
212
|
+
<footer>
|
|
213
|
+
<p>© 2024 My ViewLogic App</p>
|
|
214
|
+
</footer>
|
|
215
|
+
</body>
|
|
216
|
+
</html>
|
|
205
217
|
```
|
|
206
218
|
|
|
207
|
-
|
|
219
|
+
### Query Parameter Example
|
|
208
220
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
defaultLayout: 'default', // Default layout name
|
|
219
|
-
useLayout: true, // Enable layouts
|
|
220
|
-
|
|
221
|
-
// Caching
|
|
222
|
-
cacheMode: 'memory', // 'memory' or 'session' or 'none'
|
|
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
|
-
};
|
|
221
|
+
**src/views/user-profile.html**
|
|
222
|
+
```html
|
|
223
|
+
<div class="user-profile">
|
|
224
|
+
<h1>User Profile</h1>
|
|
225
|
+
<p>User ID: {{ userId }}</p>
|
|
226
|
+
<p>Tab: {{ activeTab }}</p>
|
|
227
|
+
<button @click="switchTab('settings')">Settings</button>
|
|
228
|
+
<button @click="switchTab('posts')">Posts</button>
|
|
229
|
+
</div>
|
|
253
230
|
```
|
|
254
231
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
ViewLogic Router supports deployment in subfolders with smart path resolution:
|
|
258
|
-
|
|
232
|
+
**src/logic/user-profile.js**
|
|
259
233
|
```javascript
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
});
|
|
234
|
+
export default {
|
|
235
|
+
computed: {
|
|
236
|
+
userId() {
|
|
237
|
+
return this.getParam('userId', 'No ID');
|
|
238
|
+
},
|
|
239
|
+
activeTab() {
|
|
240
|
+
return this.getParam('tab', 'profile');
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
methods: {
|
|
244
|
+
switchTab(tab) {
|
|
245
|
+
// Navigate with query parameters
|
|
246
|
+
this.navigateTo('user-profile', {
|
|
247
|
+
userId: this.userId,
|
|
248
|
+
tab: tab
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
280
253
|
```
|
|
281
254
|
|
|
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
|
|
289
|
-
|
|
290
|
-
Both routing modes work seamlessly in subfolder deployments:
|
|
255
|
+
**Usage:** Navigate to `/user-profile?userId=123&tab=settings` or `#/user-profile?userId=123&tab=settings` (hash mode)
|
|
291
256
|
|
|
257
|
+
**Multiple ways to access parameters:**
|
|
292
258
|
```javascript
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
259
|
+
// Direct parameter access
|
|
260
|
+
const userId = this.$params.userId; // Route parameter (direct access)
|
|
261
|
+
const tab = this.$query.tab; // Query parameter (direct access)
|
|
262
|
+
const search = this.getParam('search'); // Either route or query param
|
|
263
|
+
const allParams = this.getParams(); // Get all parameters
|
|
298
264
|
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
mode: 'history' // Cleaner URLs, needs server setup
|
|
303
|
-
});
|
|
265
|
+
// With default values
|
|
266
|
+
const page = this.getParam('page', 1); // Default to 1 if not found
|
|
267
|
+
const sort = this.$query.sort || 'asc'; // Manual default for query params
|
|
304
268
|
```
|
|
305
269
|
|
|
306
|
-
|
|
307
|
-
```nginx
|
|
308
|
-
# Nginx - redirect all subfolder requests to index.html
|
|
309
|
-
location /myapp/ {
|
|
310
|
-
try_files $uri $uri/ /myapp/index.html;
|
|
311
|
-
}
|
|
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
|
-
```
|
|
270
|
+
### Reusable Components
|
|
323
271
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
For comprehensive API documentation including all methods, configuration options, and detailed examples, see:
|
|
327
|
-
|
|
328
|
-
**📚 [Complete API Reference →](./docs/index.md)**
|
|
329
|
-
|
|
330
|
-
### Quick API Overview
|
|
272
|
+
ViewLogic automatically loads components from the `components/` folder:
|
|
331
273
|
|
|
274
|
+
**src/components/UserCard.js**
|
|
332
275
|
```javascript
|
|
333
|
-
// Basic router usage
|
|
334
|
-
const router = new ViewLogicRouter({ environment: 'development' });
|
|
335
|
-
router.navigateTo('products', { id: 123, category: 'electronics' });
|
|
336
|
-
const current = router.getCurrentRoute();
|
|
337
|
-
|
|
338
|
-
// In route components - global methods automatically available:
|
|
339
276
|
export default {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
277
|
+
name: 'UserCard',
|
|
278
|
+
template: `
|
|
279
|
+
<div class="user-card">
|
|
280
|
+
<img :src="user.avatar" :alt="user.name" class="avatar">
|
|
281
|
+
<div class="info">
|
|
282
|
+
<h3>{{ user.name }}</h3>
|
|
283
|
+
<p>{{ user.email }}</p>
|
|
284
|
+
<button @click="$emit('edit', user)" class="btn-edit">
|
|
285
|
+
Edit User
|
|
286
|
+
</button>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
`,
|
|
290
|
+
emits: ['edit'],
|
|
291
|
+
props: {
|
|
292
|
+
user: {
|
|
293
|
+
type: Object,
|
|
294
|
+
required: true
|
|
295
|
+
}
|
|
352
296
|
}
|
|
353
297
|
};
|
|
354
298
|
```
|
|
355
299
|
|
|
356
|
-
|
|
357
|
-
- **Navigation**: `navigateTo()`, `getCurrentRoute()`
|
|
358
|
-
- **Parameters**: `getParams()`, `getParam(key, defaultValue)`
|
|
359
|
-
- **Data Fetching**: `$fetchData()` (with dataURL), `$api.get()`, `$api.post()`, `$api.put()`, `$api.patch()`, `$api.delete()`
|
|
360
|
-
- **Authentication**: `$isAuthenticated()`, `$getToken()`, `$logout()`
|
|
361
|
-
- **Forms**: Auto-binding with `action` attribute, duplicate prevention, validation
|
|
362
|
-
- **i18n**: `$t(key, params)` for translations
|
|
300
|
+
**Using components in views:**
|
|
363
301
|
|
|
364
|
-
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
|
|
302
|
+
**src/views/users.html**
|
|
303
|
+
```html
|
|
304
|
+
<div class="users-page">
|
|
305
|
+
<h1>Users</h1>
|
|
306
|
+
<UserCard
|
|
307
|
+
v-for="user in users"
|
|
308
|
+
:key="user.id"
|
|
309
|
+
:user="user"
|
|
310
|
+
@edit="editUser"
|
|
311
|
+
/>
|
|
312
|
+
</div>
|
|
368
313
|
```
|
|
369
314
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
ViewLogic Router's fundamental philosophy of **View-Logic Separation** creates clear boundaries between concerns:
|
|
373
|
-
|
|
374
|
-
### Philosophy Benefits
|
|
375
|
-
- **🎨 Pure Presentation**: Views contain only HTML - no mixed logic or scripts
|
|
376
|
-
- **🧠 Pure Logic**: JavaScript components focus solely on business logic
|
|
377
|
-
- **⚡ Zero Build Required**: Work directly with separate files in development
|
|
378
|
-
- **🔄 Hot Reload**: Instant changes without compilation or bundling
|
|
379
|
-
|
|
380
|
-
### File Structure (Core Philosophy)
|
|
381
|
-
- **View**: `src/views/products.html` - Pure HTML template
|
|
382
|
-
- **Logic**: `src/logic/products.js` - Pure Vue component logic
|
|
383
|
-
- **Style**: `src/styles/products.css` - Pure CSS styles
|
|
384
|
-
|
|
385
|
-
### Example: Philosophy in Practice
|
|
315
|
+
**src/logic/users.js**
|
|
386
316
|
```javascript
|
|
387
|
-
// src/logic/products.js - Pure business logic
|
|
388
317
|
export default {
|
|
389
|
-
name: 'ProductsList',
|
|
390
|
-
dataURL: '/api/products', // Auto-fetch data
|
|
391
318
|
data() {
|
|
392
|
-
return {
|
|
319
|
+
return {
|
|
320
|
+
users: []
|
|
321
|
+
};
|
|
393
322
|
},
|
|
394
323
|
methods: {
|
|
395
|
-
|
|
396
|
-
this.navigateTo('
|
|
324
|
+
editUser(user) {
|
|
325
|
+
this.navigateTo('user-edit', { userId: user.id });
|
|
397
326
|
}
|
|
398
327
|
}
|
|
399
328
|
};
|
|
400
329
|
```
|
|
401
330
|
|
|
402
|
-
|
|
403
|
-
|
|
331
|
+
**Dynamic Component Loading:**
|
|
332
|
+
- Just add `.js` files to `src/components/` folder
|
|
333
|
+
- Components are automatically discovered and loaded
|
|
334
|
+
- No import statements needed - ViewLogic handles everything
|
|
335
|
+
- Components are available instantly in all views
|
|
404
336
|
|
|
405
|
-
##
|
|
337
|
+
## 🎯 Core APIs
|
|
406
338
|
|
|
407
|
-
|
|
339
|
+
### State Management
|
|
408
340
|
|
|
409
|
-
|
|
410
|
-
|------|------------|-------|----------|------------|
|
|
411
|
-
| **Development** | **Zero Build Required** | Separate files | 4 per route | **Real-time, instant changes** |
|
|
412
|
-
| **Production** | **Optimized Performance** | Single bundle | 1 per route | **Lightning-fast loading** |
|
|
341
|
+
Built-in reactive state management system:
|
|
413
342
|
|
|
414
343
|
```javascript
|
|
415
|
-
//
|
|
416
|
-
|
|
344
|
+
// Set state (any component can access)
|
|
345
|
+
this.$state.set('user', { name: 'John', age: 30 });
|
|
346
|
+
this.$state.set('theme', 'dark');
|
|
417
347
|
|
|
418
|
-
//
|
|
419
|
-
|
|
420
|
-
|
|
348
|
+
// Get state with optional default
|
|
349
|
+
const user = this.$state.get('user');
|
|
350
|
+
const theme = this.$state.get('theme', 'light');
|
|
421
351
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
- 🛠️ **Pure Development** - Focus on code, not build configuration
|
|
427
|
-
|
|
428
|
-
## 🪶 Ultra-Lightweight Bundle
|
|
429
|
-
|
|
430
|
-
ViewLogic Router provides a complete routing solution in an incredibly small package:
|
|
431
|
-
|
|
432
|
-
### Size Comparison
|
|
433
|
-
- **ViewLogic Router**: 13KB gzipped (48KB minified)
|
|
434
|
-
- **Vue Router + Auth + i18n + Cache**: 50KB+ gzipped
|
|
435
|
-
|
|
436
|
-
### What's Included in 13KB
|
|
437
|
-
- ✅ Complete Vue 3 routing system
|
|
438
|
-
- ✅ Authentication & authorization
|
|
439
|
-
- ✅ Internationalization (i18n)
|
|
440
|
-
- ✅ Smart caching system
|
|
441
|
-
- ✅ Query parameter management
|
|
442
|
-
- ✅ Component lazy loading
|
|
443
|
-
- ✅ Layout system
|
|
444
|
-
- ✅ Error handling
|
|
445
|
-
- ✅ Development/production modes
|
|
446
|
-
- ✅ **Automatic data fetching with dataURL**
|
|
447
|
-
- ✅ **Revolutionary DynamicInclude & HtmlInclude components**
|
|
448
|
-
- ✅ **Automatic form handling with variable parameters**
|
|
449
|
-
- ✅ **10+ Built-in UI components (Button, Modal, Card, etc.)**
|
|
450
|
-
|
|
451
|
-
### Why So Small?
|
|
452
|
-
- **Zero Dependencies** - No external libraries required (except Vue 3)
|
|
453
|
-
- **Tree-Shakable** - Only includes what you use
|
|
454
|
-
- **Optimized Code** - Hand-crafted for minimal bundle size
|
|
455
|
-
- **Smart Bundling** - Efficient code organization and minification
|
|
456
|
-
|
|
457
|
-
### Performance Benefits
|
|
458
|
-
- **Faster Load Times** - 70% smaller than typical Vue router setups
|
|
459
|
-
- **Better UX** - Instant page loads with minimal JavaScript overhead
|
|
460
|
-
- **Mobile Optimized** - Perfect for mobile-first applications
|
|
461
|
-
- **CDN Friendly** - Small size ideal for CDN distribution
|
|
462
|
-
|
|
463
|
-
## 🏆 Performance Comparison
|
|
464
|
-
|
|
465
|
-
### Bundle Size Comparison
|
|
466
|
-
| Router System | Bundle Size (Gzipped) | Features Included |
|
|
467
|
-
|---------------|----------------------|------------------|
|
|
468
|
-
| **ViewLogic Router** | **13KB** | Routing + Auth + i18n + Cache + Query + Components |
|
|
469
|
-
| Vue Router | 12KB | Routing only |
|
|
470
|
-
| Vue Router + Pinia | 18KB | Routing + State |
|
|
471
|
-
| React Router | 15KB | Routing only |
|
|
472
|
-
| Next.js Router | 25KB+ | Routing + SSR |
|
|
473
|
-
| Nuxt Router | 30KB+ | Routing + SSR + Meta |
|
|
474
|
-
|
|
475
|
-
### Runtime Performance Comparison
|
|
476
|
-
|
|
477
|
-
#### Traditional SPA Routing
|
|
478
|
-
```
|
|
479
|
-
Route Change Process:
|
|
480
|
-
├── 1️⃣ Parse route
|
|
481
|
-
├── 2️⃣ Load component bundle
|
|
482
|
-
├── 3️⃣ Execute component code
|
|
483
|
-
├── 4️⃣ Load template (if separate)
|
|
484
|
-
├── 5️⃣ Load styles (if separate)
|
|
485
|
-
├── 6️⃣ Apply i18n translations
|
|
486
|
-
├── 7️⃣ Check authentication
|
|
487
|
-
└── 8️⃣ Render component
|
|
488
|
-
|
|
489
|
-
Total: Multiple operations + Bundle parsing
|
|
490
|
-
```
|
|
352
|
+
// Check if state exists
|
|
353
|
+
if (this.$state.has('user')) {
|
|
354
|
+
console.log('User is logged in');
|
|
355
|
+
}
|
|
491
356
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
357
|
+
// Watch for changes (reactive)
|
|
358
|
+
this.$state.watch('user', (newValue, oldValue) => {
|
|
359
|
+
console.log('User changed:', newValue);
|
|
360
|
+
this.updateUI();
|
|
361
|
+
});
|
|
497
362
|
|
|
498
|
-
|
|
363
|
+
// Bulk updates
|
|
364
|
+
this.$state.update({
|
|
365
|
+
theme: 'dark',
|
|
366
|
+
language: 'ko',
|
|
367
|
+
sidebar: 'collapsed'
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Get all state
|
|
371
|
+
const allState = this.$state.getAll();
|
|
372
|
+
|
|
373
|
+
// Clear specific state
|
|
374
|
+
this.$state.delete('temporaryData');
|
|
499
375
|
```
|
|
500
376
|
|
|
501
|
-
###
|
|
502
|
-
- **🚀 75% Faster Loading** - Pre-bundled routes vs on-demand compilation
|
|
503
|
-
- **📦 Smaller Footprint** - 13KB includes everything others need 30KB+ for
|
|
504
|
-
- **⚡ Instant Navigation** - No build-time compilation in production
|
|
505
|
-
- **🎯 Route-Level Optimization** - Each route is independently optimized
|
|
506
|
-
- **💾 Superior Caching** - Route-level caching vs component-level caching
|
|
507
|
-
- **🔄 Zero Hydration** - No server-side rendering complexity
|
|
377
|
+
### Authentication System
|
|
508
378
|
|
|
509
|
-
|
|
510
|
-
1. **Pre-compilation**: Routes are pre-built, not compiled at runtime
|
|
511
|
-
2. **All-in-One Bundles**: View + Logic + Style in single optimized file
|
|
512
|
-
3. **Zero Dependencies**: No additional libraries needed for full functionality
|
|
513
|
-
4. **Smart Caching**: Route-level caching with intelligent invalidation
|
|
514
|
-
5. **Optimized Architecture**: Purpose-built for maximum performance
|
|
515
|
-
6. **Revolutionary Components**: DynamicInclude & HtmlInclude for dynamic content loading
|
|
379
|
+
Complete authentication management:
|
|
516
380
|
|
|
517
|
-
|
|
381
|
+
```javascript
|
|
382
|
+
// Check authentication status
|
|
383
|
+
if (this.isAuth()) {
|
|
384
|
+
console.log('User is logged in');
|
|
385
|
+
}
|
|
518
386
|
|
|
519
|
-
|
|
387
|
+
// Login with token
|
|
388
|
+
this.setToken('jwt-token-here');
|
|
520
389
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
:use-cache="false"
|
|
527
|
-
loading-text="로그인 페이지 로딩 중..."
|
|
528
|
-
wrapper-class="test-dynamic-include"
|
|
529
|
-
:params="{
|
|
530
|
-
returnUrl: '/dashboard',
|
|
531
|
-
showWelcome: true,
|
|
532
|
-
theme: 'compact',
|
|
533
|
-
testMode: true
|
|
534
|
-
}"
|
|
535
|
-
/>
|
|
536
|
-
```
|
|
390
|
+
// Login with options
|
|
391
|
+
this.setToken('jwt-token', {
|
|
392
|
+
storage: 'localStorage', // 'localStorage', 'sessionStorage', 'cookie'
|
|
393
|
+
skipValidation: false // Skip JWT validation
|
|
394
|
+
});
|
|
537
395
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
- **Parameter Injection** - Pass dynamic parameters to the URL
|
|
541
|
-
- **Event Handling** - React to loading states and errors
|
|
542
|
-
- **Slot Support** - Custom loading and error templates
|
|
543
|
-
- **Cache Integration** - Automatic caching with TTL support
|
|
396
|
+
// Get current token
|
|
397
|
+
const token = this.getToken();
|
|
544
398
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
<!-- Include raw HTML content with Vue reactivity -->
|
|
548
|
-
<HtmlInclude
|
|
549
|
-
src="/src/views/404.html"
|
|
550
|
-
:sanitize="true"
|
|
551
|
-
:use-cache="false"
|
|
552
|
-
loading-text="위젯 로딩 중..."
|
|
553
|
-
wrapper-class="test-html-include"
|
|
554
|
-
/>
|
|
399
|
+
// Logout (clears token and redirects to login)
|
|
400
|
+
this.logout();
|
|
555
401
|
```
|
|
556
402
|
|
|
557
|
-
|
|
558
|
-
- **Raw HTML Rendering** - Safely render dynamic HTML content
|
|
559
|
-
- **XSS Protection** - Built-in HTML sanitization
|
|
560
|
-
- **Vue Integration** - HTML content works with Vue reactivity
|
|
561
|
-
- **Fallback Support** - Default content when HTML is unavailable
|
|
562
|
-
- **Script Execution** - Optional JavaScript execution in HTML content
|
|
563
|
-
|
|
564
|
-
### Automatic Data Fetching with dataURL
|
|
403
|
+
### API Management
|
|
565
404
|
|
|
566
|
-
|
|
405
|
+
Built-in HTTP client with automatic token injection:
|
|
567
406
|
|
|
568
|
-
#### Single API (Simple Usage)
|
|
569
407
|
```javascript
|
|
570
|
-
//
|
|
571
|
-
|
|
572
|
-
name: 'ProductsList',
|
|
573
|
-
dataURL: '/api/products', // ✨ Magic happens here!
|
|
574
|
-
data() {
|
|
575
|
-
return {
|
|
576
|
-
title: 'Our Products'
|
|
577
|
-
// products: [] - No need to declare, auto-populated from API
|
|
578
|
-
};
|
|
579
|
-
},
|
|
580
|
-
mounted() {
|
|
581
|
-
// Data is already fetched and available!
|
|
582
|
-
console.log('Products loaded:', this.products);
|
|
583
|
-
console.log('Loading state:', this.$dataLoading);
|
|
584
|
-
},
|
|
585
|
-
methods: {
|
|
586
|
-
async refreshData() {
|
|
587
|
-
// Manual refresh if needed
|
|
588
|
-
await this.$fetchData();
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
};
|
|
592
|
-
```
|
|
408
|
+
// GET request
|
|
409
|
+
const users = await this.$api.get('/api/users');
|
|
593
410
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
411
|
+
// GET with query parameters
|
|
412
|
+
const filteredUsers = await this.$api.get('/api/users', {
|
|
413
|
+
params: { role: 'admin', active: true }
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// POST with data
|
|
417
|
+
const newUser = await this.$api.post('/api/users', {
|
|
418
|
+
name: 'John',
|
|
419
|
+
email: 'john@example.com'
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// PUT/PATCH/DELETE
|
|
423
|
+
await this.$api.put('/api/users/{userId}', userData);
|
|
424
|
+
await this.$api.patch('/api/users/{userId}', partialData);
|
|
425
|
+
await this.$api.delete('/api/users/{userId}');
|
|
426
|
+
|
|
427
|
+
// Parameter substitution (automatically uses route/query params)
|
|
428
|
+
// If current route is /users?userId=123, this becomes /api/users/123
|
|
429
|
+
const user = await this.$api.get('/api/users/{userId}');
|
|
430
|
+
|
|
431
|
+
// Automatic data loading in components
|
|
597
432
|
export default {
|
|
598
|
-
|
|
433
|
+
// Single API endpoint
|
|
434
|
+
dataURL: '/api/user/profile',
|
|
435
|
+
|
|
436
|
+
// Multiple endpoints
|
|
599
437
|
dataURL: {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
user: '/api/user/profile'
|
|
604
|
-
}, // ✨ Multiple APIs with named data!
|
|
605
|
-
data() {
|
|
606
|
-
return {
|
|
607
|
-
title: 'Dashboard'
|
|
608
|
-
// products: [], categories: [], stats: {}, user: {}
|
|
609
|
-
// All auto-populated from respective APIs!
|
|
610
|
-
};
|
|
438
|
+
profile: '/api/user/profile',
|
|
439
|
+
posts: '/api/posts?userId={userId}',
|
|
440
|
+
notifications: '/api/notifications'
|
|
611
441
|
},
|
|
442
|
+
|
|
612
443
|
mounted() {
|
|
613
|
-
//
|
|
614
|
-
|
|
615
|
-
console.log('Categories:', this.categories);
|
|
616
|
-
console.log('Stats:', this.stats);
|
|
617
|
-
console.log('User:', this.user);
|
|
618
|
-
console.log('Loading state:', this.$dataLoading);
|
|
444
|
+
// Data automatically loaded and available as:
|
|
445
|
+
// this.profile, this.posts, this.notifications
|
|
619
446
|
},
|
|
447
|
+
|
|
620
448
|
methods: {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
await this
|
|
449
|
+
// Manual data refresh - reuses dataURL configuration
|
|
450
|
+
async refreshData() {
|
|
451
|
+
await this.fetchData();
|
|
452
|
+
// All dataURL endpoints are called again
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
// Refresh when filters change
|
|
456
|
+
async applyFilter(filterType) {
|
|
457
|
+
this.$query.filter = filterType;
|
|
458
|
+
await this.fetchData(); // Refetch with new filter parameter
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
// Refresh when user changes
|
|
462
|
+
async switchUser(newUserId) {
|
|
463
|
+
this.$params.userId = newUserId;
|
|
464
|
+
await this.fetchData(); // Refetch with new userId parameter
|
|
624
465
|
},
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
466
|
+
|
|
467
|
+
// Pagination
|
|
468
|
+
async changePage(page) {
|
|
469
|
+
this.$query.page = page;
|
|
470
|
+
await this.fetchData(); // Load new page data
|
|
628
471
|
},
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
472
|
+
|
|
473
|
+
// Custom data loading
|
|
474
|
+
async loadSpecificData() {
|
|
475
|
+
const customData = await this.fetchData({
|
|
476
|
+
profile: '/api/admin/profile',
|
|
477
|
+
stats: '/api/stats?period={period}'
|
|
478
|
+
});
|
|
479
|
+
// Override default dataURL temporarily
|
|
632
480
|
}
|
|
633
481
|
}
|
|
634
482
|
};
|
|
635
483
|
```
|
|
636
484
|
|
|
637
|
-
|
|
638
|
-
- **Zero-Config API Calls** - Just define `dataURL` and data is automatically fetched
|
|
639
|
-
- **🆕 Multiple API Support** - Define multiple APIs with custom names
|
|
640
|
-
- **🚀 Parallel Processing** - Multiple APIs called simultaneously for best performance
|
|
641
|
-
- **🎯 Selective Refresh** - Refresh specific APIs independently
|
|
642
|
-
- **Query Parameter Integration** - Current route parameters are automatically sent to all APIs
|
|
643
|
-
- **Loading State Management** - `$dataLoading` property automatically managed
|
|
644
|
-
- **Advanced Error Handling** - Per-API error handling with detailed events
|
|
645
|
-
- **Named Data Storage** - Each API result stored with its defined name
|
|
646
|
-
- **Event Support** - `@data-loaded` and `@data-error` events with detailed info
|
|
485
|
+
### Internationalization
|
|
647
486
|
|
|
648
|
-
|
|
487
|
+
Comprehensive i18n system:
|
|
649
488
|
|
|
650
|
-
|
|
489
|
+
```javascript
|
|
490
|
+
// Simple translation
|
|
491
|
+
const message = this.$t('welcome.message');
|
|
651
492
|
|
|
652
|
-
|
|
493
|
+
// With parameters
|
|
494
|
+
const greeting = this.$t('hello.user', { name: 'John', role: 'admin' });
|
|
653
495
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
- **Multiple APIs**: `dataURL: { stats: '/api/stats', users: '/api/users' }` - Dashboards, admin panels
|
|
657
|
-
- **Dynamic Content**: `<DynamicInclude page="login" :params="{ theme: 'compact' }" />`
|
|
658
|
-
- **HTML Includes**: `<HtmlInclude src="/widgets/weather.html" :sanitize="true" />`
|
|
496
|
+
// Nested keys
|
|
497
|
+
const errorMsg = this.$t('errors.validation.email.required');
|
|
659
498
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
- ✅ **Parameter Integration** - Query params sent automatically
|
|
663
|
-
- ✅ **Loading States** - `$dataLoading` auto-managed
|
|
664
|
-
- ✅ **Built-in Security** - HTML sanitization included
|
|
665
|
-
- ✅ **Zero Setup** - Works immediately without configuration
|
|
499
|
+
// Check current language
|
|
500
|
+
const currentLang = this.getLanguage();
|
|
666
501
|
|
|
667
|
-
|
|
502
|
+
// Change language (automatically reloads interface)
|
|
503
|
+
await this.setLanguage('ko');
|
|
504
|
+
```
|
|
668
505
|
|
|
669
|
-
|
|
506
|
+
### Logging & Error Handling
|
|
670
507
|
|
|
671
|
-
|
|
508
|
+
Built-in logging system for debugging and error tracking:
|
|
672
509
|
|
|
673
510
|
```javascript
|
|
674
|
-
// src/logic/user-profile.js
|
|
675
511
|
export default {
|
|
676
|
-
name: 'UserProfile',
|
|
677
|
-
|
|
678
512
|
async mounted() {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const user = await this.$api.get('/api/users/{userId}');
|
|
682
|
-
|
|
683
|
-
// POST request with data
|
|
684
|
-
const response = await this.$api.post('/api/users/{userId}/posts', {
|
|
685
|
-
title: 'New Post',
|
|
686
|
-
content: 'Post content here'
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
// PUT request for updates
|
|
690
|
-
await this.$api.put('/api/users/{userId}', {
|
|
691
|
-
name: user.name,
|
|
692
|
-
email: user.email
|
|
693
|
-
});
|
|
694
|
-
|
|
695
|
-
// DELETE request
|
|
696
|
-
await this.$api.delete('/api/posts/{postId}');
|
|
697
|
-
|
|
698
|
-
} catch (error) {
|
|
699
|
-
console.error('API call failed:', error);
|
|
700
|
-
this.handleError(error);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
};
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### Advanced API Features
|
|
513
|
+
this.log('info', 'Component mounted successfully');
|
|
514
|
+
},
|
|
707
515
|
|
|
708
|
-
```javascript
|
|
709
|
-
export default {
|
|
710
516
|
methods: {
|
|
711
|
-
async
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
await this.$api.post('/api/upload', formData);
|
|
721
|
-
|
|
722
|
-
// With query parameters (automatically added from current route)
|
|
723
|
-
// URL: /users?id=123 → API call includes ?id=123
|
|
724
|
-
const result = await this.$api.get('/api/user-data');
|
|
517
|
+
async handleUserAction() {
|
|
518
|
+
this.log('debug', 'User action started', { action: 'submit' });
|
|
519
|
+
|
|
520
|
+
try {
|
|
521
|
+
const result = await this.$api.post('/api/users', userData);
|
|
522
|
+
this.log('info', 'User created successfully', result);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
this.log('error', 'Failed to create user:', error);
|
|
525
|
+
}
|
|
725
526
|
},
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
527
|
+
|
|
528
|
+
async loadData() {
|
|
529
|
+
this.log('debug', 'Loading user data...');
|
|
530
|
+
|
|
729
531
|
try {
|
|
730
|
-
const
|
|
731
|
-
this.
|
|
732
|
-
|
|
532
|
+
const data = await this.fetchData();
|
|
533
|
+
this.log('info', 'Data loaded', { count: data.length });
|
|
733
534
|
} catch (error) {
|
|
734
|
-
|
|
735
|
-
this.showError('User not found');
|
|
736
|
-
} else if (error.message.includes('401')) {
|
|
737
|
-
this.navigateTo('login');
|
|
738
|
-
} else {
|
|
739
|
-
this.showError('Something went wrong');
|
|
740
|
-
}
|
|
535
|
+
this.log('warn', 'Data loading failed, using cache', error);
|
|
741
536
|
}
|
|
742
537
|
}
|
|
743
538
|
}
|
|
744
539
|
};
|
|
745
540
|
```
|
|
746
541
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
-
|
|
750
|
-
-
|
|
751
|
-
-
|
|
752
|
-
- **🔗 Query Integration**: Current route query parameters automatically included
|
|
753
|
-
- **⚡ Error Standardization**: Consistent error format across all API calls
|
|
754
|
-
- **🚀 RESTful Pattern**: Clean `get()`, `post()`, `put()`, `patch()`, `delete()` methods
|
|
542
|
+
**Log Levels:**
|
|
543
|
+
- `debug` - Development debugging information
|
|
544
|
+
- `info` - General information and success messages
|
|
545
|
+
- `warn` - Warning messages for recoverable issues
|
|
546
|
+
- `error` - Error messages for failures
|
|
755
547
|
|
|
756
|
-
|
|
548
|
+
All logs include the component name automatically: `[routeName] Your message`
|
|
757
549
|
|
|
758
|
-
|
|
550
|
+
### Navigation & Routing
|
|
759
551
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
```html
|
|
763
|
-
<!-- src/views/contact.html -->
|
|
764
|
-
<div class="contact-page">
|
|
765
|
-
<h1>Contact Us</h1>
|
|
766
|
-
<form action="/api/contact" method="POST">
|
|
767
|
-
<input type="text" name="name" required placeholder="Your Name">
|
|
768
|
-
<input type="email" name="email" required placeholder="Your Email">
|
|
769
|
-
<textarea name="message" required placeholder="Your Message"></textarea>
|
|
770
|
-
<button type="submit">Send Message</button>
|
|
771
|
-
</form>
|
|
772
|
-
</div>
|
|
773
|
-
```
|
|
552
|
+
Simple yet powerful routing system:
|
|
774
553
|
|
|
775
554
|
```javascript
|
|
776
|
-
//
|
|
777
|
-
|
|
778
|
-
name: 'ContactPage',
|
|
779
|
-
mounted() {
|
|
780
|
-
// Forms are automatically bound with smart features:
|
|
781
|
-
// ✅ Duplicate submission prevention
|
|
782
|
-
// ✅ Automatic validation
|
|
783
|
-
// ✅ Loading state management
|
|
784
|
-
// ✅ Error handling
|
|
785
|
-
console.log('Smart form handling is automatic!');
|
|
786
|
-
}
|
|
787
|
-
};
|
|
788
|
-
```
|
|
789
|
-
|
|
790
|
-
### Smart Form Features - 🆕 Enhanced!
|
|
555
|
+
// Navigate to route
|
|
556
|
+
this.navigateTo('user-profile');
|
|
791
557
|
|
|
792
|
-
|
|
558
|
+
// With query parameters
|
|
559
|
+
this.navigateTo('user-profile', { userId: 123, tab: 'settings' });
|
|
793
560
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
data-error-handler="handleError"
|
|
800
|
-
data-loading-handler="handleLoading"
|
|
801
|
-
data-redirect="/profile">
|
|
802
|
-
|
|
803
|
-
<input type="text" name="name" required
|
|
804
|
-
data-validation="validateName">
|
|
805
|
-
<input type="email" name="email" required>
|
|
806
|
-
<button type="submit">Update Profile</button>
|
|
807
|
-
</form>
|
|
808
|
-
```
|
|
561
|
+
// Object syntax
|
|
562
|
+
this.navigateTo({
|
|
563
|
+
route: 'search-results',
|
|
564
|
+
params: { query: 'vue', category: 'tutorials' }
|
|
565
|
+
});
|
|
809
566
|
|
|
810
|
-
```javascript
|
|
811
|
-
export default {
|
|
812
|
-
methods: {
|
|
813
|
-
// Custom validation
|
|
814
|
-
validateName(value) {
|
|
815
|
-
return value.length >= 2 && value.length <= 50;
|
|
816
|
-
},
|
|
817
|
-
|
|
818
|
-
// Success handler
|
|
819
|
-
handleSuccess(response, form) {
|
|
820
|
-
this.showToast('Profile updated successfully!', 'success');
|
|
821
|
-
// Automatic redirect to /profile happens after this
|
|
822
|
-
},
|
|
823
|
-
|
|
824
|
-
// Error handler with smart error detection
|
|
825
|
-
handleError(error, form) {
|
|
826
|
-
if (error.message.includes('validation')) {
|
|
827
|
-
this.showToast('Please check your input', 'warning');
|
|
828
|
-
} else {
|
|
829
|
-
this.showToast('Update failed. Please try again.', 'error');
|
|
830
|
-
}
|
|
831
|
-
},
|
|
832
|
-
|
|
833
|
-
// Loading state handler
|
|
834
|
-
handleLoading(isLoading, form) {
|
|
835
|
-
const button = form.querySelector('button[type="submit"]');
|
|
836
|
-
button.disabled = isLoading;
|
|
837
|
-
button.textContent = isLoading ? 'Updating...' : 'Update Profile';
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
567
|
```
|
|
842
568
|
|
|
843
|
-
### Key Form Features
|
|
844
|
-
|
|
845
|
-
- **🚫 Duplicate Prevention**: Automatic duplicate submission blocking
|
|
846
|
-
- **⏱️ Timeout Management**: 30-second default timeout with abort capability
|
|
847
|
-
- **✅ Built-in Validation**: HTML5 + custom validation functions
|
|
848
|
-
- **🔄 Loading States**: Automatic loading state management
|
|
849
|
-
- **🎯 Smart Error Handling**: Network vs validation error distinction
|
|
850
|
-
- **📄 File Upload Support**: Automatic FormData vs JSON detection
|
|
851
|
-
- **🔀 Auto Redirect**: Post-success navigation
|
|
852
|
-
- **🏷️ Parameter Substitution**: Dynamic URL parameter replacement
|
|
853
569
|
|
|
854
|
-
###
|
|
570
|
+
### Form Handling
|
|
855
571
|
|
|
856
|
-
|
|
572
|
+
Revolutionary automatic form processing with parameter substitution:
|
|
857
573
|
|
|
858
574
|
```html
|
|
859
|
-
<!--
|
|
860
|
-
<form action="/api/users
|
|
861
|
-
data-success="
|
|
862
|
-
data-error="
|
|
863
|
-
|
|
864
|
-
<
|
|
865
|
-
<
|
|
575
|
+
<!-- Basic form - automatically handled on submit -->
|
|
576
|
+
<form action="/api/users" method="POST"
|
|
577
|
+
data-success="onUserCreated"
|
|
578
|
+
data-error="onUserError"
|
|
579
|
+
data-loading="setLoading">
|
|
580
|
+
<input name="name" v-model="userData.name" required>
|
|
581
|
+
<input name="email" v-model="userData.email" type="email" required>
|
|
582
|
+
<button type="submit">Create User</button>
|
|
583
|
+
</form>
|
|
584
|
+
|
|
585
|
+
<!-- Form with parameter substitution -->
|
|
586
|
+
<form action="/api/users/{userId}" method="PUT"
|
|
587
|
+
data-success="onUserUpdated"
|
|
588
|
+
data-redirect="user-profile">
|
|
589
|
+
<input name="name" v-model="user.name">
|
|
590
|
+
<input name="email" v-model="user.email">
|
|
591
|
+
<button type="submit">Update User</button>
|
|
866
592
|
</form>
|
|
867
593
|
|
|
868
|
-
<!--
|
|
869
|
-
<form action="/api/
|
|
870
|
-
data-success="
|
|
871
|
-
|
|
872
|
-
<input
|
|
873
|
-
<
|
|
874
|
-
<option value="pending">Pending</option>
|
|
875
|
-
<option value="processing">Processing</option>
|
|
876
|
-
<option value="completed">Completed</option>
|
|
877
|
-
</select>
|
|
878
|
-
<button type="submit">Update Order</button>
|
|
594
|
+
<!-- File upload form -->
|
|
595
|
+
<form action="/api/upload" method="POST" enctype="multipart/form-data"
|
|
596
|
+
data-success="onFileUploaded">
|
|
597
|
+
<input name="avatar" type="file" accept="image/*">
|
|
598
|
+
<input name="description" v-model="description">
|
|
599
|
+
<button type="submit">Upload</button>
|
|
879
600
|
</form>
|
|
880
601
|
|
|
881
|
-
<!--
|
|
882
|
-
<form action="/api/
|
|
883
|
-
|
|
884
|
-
<
|
|
885
|
-
<button type="submit">
|
|
602
|
+
<!-- Form with custom validation -->
|
|
603
|
+
<form action="/api/contact" method="POST">
|
|
604
|
+
<input name="email" type="email" data-validation="validateEmail" required>
|
|
605
|
+
<textarea name="message" data-validation="validateMessage" required></textarea>
|
|
606
|
+
<button type="submit">Send Message</button>
|
|
886
607
|
</form>
|
|
887
608
|
```
|
|
888
609
|
|
|
889
610
|
```javascript
|
|
890
|
-
// Component logic
|
|
611
|
+
// Component logic with form handlers
|
|
891
612
|
export default {
|
|
892
|
-
name: 'UserProfile',
|
|
893
613
|
data() {
|
|
894
614
|
return {
|
|
895
|
-
|
|
896
|
-
|
|
615
|
+
userData: { name: '', email: '' },
|
|
616
|
+
user: { name: 'John', email: 'john@example.com' },
|
|
617
|
+
loading: false
|
|
897
618
|
};
|
|
898
619
|
},
|
|
899
620
|
methods: {
|
|
900
|
-
|
|
901
|
-
|
|
621
|
+
// Success handlers
|
|
622
|
+
onUserCreated(response, form) {
|
|
623
|
+
this.$state.set('newUser', response);
|
|
624
|
+
this.navigateTo('user-profile', { userId: response.id });
|
|
902
625
|
},
|
|
903
|
-
orderUpdated(response) {
|
|
904
|
-
console.log('Order updated!', response);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
};
|
|
908
|
-
```
|
|
909
626
|
|
|
910
|
-
|
|
627
|
+
onUserUpdated(response, form) {
|
|
628
|
+
this.$state.set('currentUser', response);
|
|
629
|
+
// Auto redirect via data-redirect="user-profile"
|
|
630
|
+
},
|
|
911
631
|
|
|
912
|
-
|
|
632
|
+
onFileUploaded(response, form) {
|
|
633
|
+
this.$state.set('uploadedFile', response.file);
|
|
634
|
+
},
|
|
913
635
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
636
|
+
// Error handler
|
|
637
|
+
onUserError(error, form) {
|
|
638
|
+
console.error('User operation failed:', error);
|
|
639
|
+
},
|
|
917
640
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
data() {
|
|
923
|
-
return {
|
|
924
|
-
userId: 123, // Available as {userId} in action URLs
|
|
925
|
-
productId: 456 // Available as {productId} in action URLs
|
|
926
|
-
};
|
|
927
|
-
},
|
|
928
|
-
computed: {
|
|
929
|
-
currentOrderId() { // Available as {currentOrderId} in action URLs
|
|
930
|
-
return this.getParam('orderId') || this.defaultOrderId;
|
|
931
|
-
}
|
|
932
|
-
},
|
|
933
|
-
mounted() {
|
|
934
|
-
// Route parameters also work: /user-profile?userId=789
|
|
935
|
-
// {userId} will use 789 from URL, or fall back to data() value of 123
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
```
|
|
641
|
+
// Loading handler
|
|
642
|
+
setLoading(isLoading, form) {
|
|
643
|
+
this.loading = isLoading;
|
|
644
|
+
},
|
|
939
645
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
export default {
|
|
950
|
-
methods: {
|
|
951
|
-
subscriptionSuccess(response) { this.$toast('Success!', 'success'); },
|
|
952
|
-
subscriptionError(error) { this.$toast('Failed!', 'error'); }
|
|
646
|
+
// Custom validation functions
|
|
647
|
+
validateEmail(value, input) {
|
|
648
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
649
|
+
return emailRegex.test(value);
|
|
650
|
+
},
|
|
651
|
+
|
|
652
|
+
validateMessage(value, input) {
|
|
653
|
+
return value.length >= 10;
|
|
654
|
+
}
|
|
953
655
|
}
|
|
954
656
|
};
|
|
955
657
|
```
|
|
956
658
|
|
|
957
|
-
|
|
958
|
-
```html
|
|
959
|
-
<form action="/api/resource/{id}" method="POST"
|
|
960
|
-
data-success="handleSuccess" data-error="handleError"
|
|
961
|
-
data-redirect="/success" data-confirm="Sure?"
|
|
962
|
-
enctype="multipart/form-data">
|
|
963
|
-
<input name="title" required>
|
|
964
|
-
<input type="file" name="file" accept=".pdf">
|
|
965
|
-
<button type="submit">Submit</button>
|
|
966
|
-
</form>
|
|
967
|
-
```
|
|
968
|
-
|
|
969
|
-
### Authentication Integration
|
|
970
|
-
```html
|
|
971
|
-
<!-- Auth tokens automatically included for authenticated users -->
|
|
972
|
-
<form action="/api/protected/resource" method="POST">
|
|
973
|
-
<input name="data" required>
|
|
974
|
-
<button type="submit">Save</button>
|
|
975
|
-
</form>
|
|
976
|
-
<!-- Authorization: Bearer <token> header added automatically -->
|
|
977
|
-
```
|
|
659
|
+
## ⚙️ Configuration
|
|
978
660
|
|
|
979
|
-
###
|
|
980
|
-
```html
|
|
981
|
-
<!-- HTML5 + custom validation -->
|
|
982
|
-
<form action="/api/register" method="POST">
|
|
983
|
-
<input type="email" name="email" required pattern="...">
|
|
984
|
-
<input type="password" name="password" minlength="8" required>
|
|
985
|
-
<button type="submit">Register</button>
|
|
986
|
-
</form>
|
|
987
|
-
```
|
|
661
|
+
### Basic Configuration
|
|
988
662
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
663
|
+
```javascript
|
|
664
|
+
const router = new ViewLogicRouter({
|
|
665
|
+
// Core routing settings
|
|
666
|
+
basePath: '/', // Base path for the application
|
|
667
|
+
mode: 'hash', // 'hash' or 'history'
|
|
668
|
+
|
|
669
|
+
// Authentication settings
|
|
670
|
+
authEnabled: true, // Enable authentication system
|
|
671
|
+
loginRoute: 'login', // Route name for login page
|
|
672
|
+
protectedRoutes: ['dashboard', 'profile', 'admin'],
|
|
673
|
+
protectedPrefixes: ['admin/', 'secure/'],
|
|
674
|
+
publicRoutes: ['login', 'register', 'home', 'about'],
|
|
675
|
+
authStorage: 'localStorage', // 'localStorage', 'sessionStorage', 'cookie'
|
|
996
676
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
677
|
+
// Internationalization
|
|
678
|
+
useI18n: true, // Enable i18n system
|
|
679
|
+
defaultLanguage: 'en', // Default language
|
|
680
|
+
i18nPath: '/i18n', // Path to language files
|
|
681
|
+
|
|
682
|
+
// Caching system
|
|
683
|
+
cacheMode: 'memory', // Memory caching only
|
|
684
|
+
cacheTTL: 300000, // Cache TTL in milliseconds (5 minutes)
|
|
685
|
+
maxCacheSize: 100, // Maximum number of cached items
|
|
686
|
+
|
|
687
|
+
// API settings
|
|
688
|
+
apiBaseURL: '/api', // Base URL for API requests
|
|
689
|
+
apiTimeout: 10000, // Request timeout in milliseconds
|
|
690
|
+
|
|
691
|
+
// Development settings
|
|
692
|
+
environment: 'development', // 'development' or 'production'
|
|
693
|
+
logLevel: 'info' // 'error', 'warn', 'info', 'debug'
|
|
694
|
+
});
|
|
1005
695
|
```
|
|
1006
696
|
|
|
1007
|
-
###
|
|
1008
|
-
- ✅ **Zero Setup** - Just add `action` attribute vs manual event handlers
|
|
1009
|
-
- ✅ **Variable Parameters** - `{userId}` template syntax vs manual interpolation
|
|
1010
|
-
- ✅ **Auto Authentication** - Tokens injected automatically
|
|
1011
|
-
- ✅ **File Uploads** - Automatic multipart support
|
|
1012
|
-
- ✅ **Built-in Validation** - HTML5 + custom functions
|
|
1013
|
-
|
|
1014
|
-
### Code Comparison
|
|
1015
|
-
**Traditional**: 30+ lines of boilerplate for forms, API calls, loading states
|
|
1016
|
-
**ViewLogic**: 5 lines with `action` attribute + callback method
|
|
1017
|
-
**Result**: 80% less code, more features included
|
|
1018
|
-
|
|
1019
|
-
## 🔗 Query-Based Parameter System: Revolutionary Simplicity
|
|
1020
|
-
|
|
1021
|
-
ViewLogic Router's **Query-Based Parameter System** is a key feature that eliminates routing complexity:
|
|
1022
|
-
|
|
1023
|
-
**Philosophy**: **Everything is query-based** - no complex path parameters like `/users/:id`. Just simple, clean URLs: `/users?id=123`.
|
|
697
|
+
### Advanced Configuration
|
|
1024
698
|
|
|
1025
|
-
### Revolutionary Benefits
|
|
1026
|
-
1. **📍 Simple URLs**: `/product?id=123&category=electronics` (clear and readable)
|
|
1027
|
-
2. **🎯 Consistent Access**: Always use `this.getParam('id')` - never mix path/query paradigms
|
|
1028
|
-
3. **⚡ No Route Configuration**: No complex route definitions or parameter mappings needed
|
|
1029
|
-
4. **🔍 SEO Friendly**: Descriptive parameter names make URLs self-documenting
|
|
1030
|
-
5. **🌐 Universal Compatibility**: Query parameters work everywhere - no framework lock-in
|
|
1031
|
-
|
|
1032
|
-
### Simple Usage Example
|
|
1033
699
|
```javascript
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
700
|
+
const router = new ViewLogicRouter({
|
|
701
|
+
// Custom authentication function
|
|
702
|
+
authEnabled: true,
|
|
703
|
+
checkAuthFunction: async (route) => {
|
|
704
|
+
try {
|
|
705
|
+
// Use ViewLogic APIs like in components
|
|
706
|
+
const userData = await route.$api.get('/api/auth/verify');
|
|
707
|
+
route.$state.set('currentUser', userData);
|
|
708
|
+
return true;
|
|
709
|
+
} catch (error) {
|
|
710
|
+
console.error('Auth verification failed:', error);
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
1044
713
|
}
|
|
1045
|
-
};
|
|
714
|
+
});
|
|
1046
715
|
```
|
|
1047
716
|
|
|
1048
|
-
|
|
1049
|
-
**Traditional Routers**: Complex path parameters (`/users/:id/posts/:postId`) require route configuration, parameter extraction logic, and mixed paradigms.
|
|
717
|
+
## 🔧 Production Build
|
|
1050
718
|
|
|
1051
|
-
|
|
719
|
+
```bash
|
|
720
|
+
# Development mode (zero build)
|
|
721
|
+
npm run dev
|
|
1052
722
|
|
|
723
|
+
# Production build with optimization
|
|
724
|
+
npm run build
|
|
1053
725
|
|
|
1054
|
-
|
|
726
|
+
# Preview production build
|
|
727
|
+
npm run serve
|
|
728
|
+
```
|
|
1055
729
|
|
|
1056
|
-
|
|
730
|
+
### Production Optimizations
|
|
1057
731
|
|
|
1058
|
-
|
|
732
|
+
ViewLogic Router automatically optimizes for production:
|
|
1059
733
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
734
|
+
- **Code splitting**: Each route becomes a separate bundle
|
|
735
|
+
- **Tree shaking**: Unused features are eliminated
|
|
736
|
+
- **Minification**: Code is compressed and optimized
|
|
737
|
+
- **Caching**: Aggressive caching for static assets
|
|
738
|
+
- **Lazy loading**: Routes and components load on demand
|
|
1063
739
|
|
|
1064
|
-
**CDN Usage:**
|
|
1065
|
-
```html
|
|
1066
|
-
<script src="https://cdn.jsdelivr.net/npm/viewlogic/dist/viewlogic-router.umd.js"></script>
|
|
1067
|
-
<script>
|
|
1068
|
-
ViewLogicRouter({ environment: 'production' }).mount('#app');
|
|
1069
|
-
</script>
|
|
1070
|
-
```
|
|
1071
740
|
|
|
1072
741
|
## 🤝 Contributing
|
|
1073
742
|
|
|
1074
|
-
|
|
743
|
+
We welcome contributions! Please read our [Contributing Guide](CONTRIBUTING.md) for details on:
|
|
1075
744
|
|
|
1076
|
-
|
|
745
|
+
- Code style and conventions
|
|
746
|
+
- Testing requirements
|
|
747
|
+
- Pull request process
|
|
748
|
+
- Issue reporting guidelines
|
|
1077
749
|
|
|
1078
|
-
|
|
750
|
+
## 📄 License
|
|
1079
751
|
|
|
1080
|
-
|
|
752
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
1081
753
|
|
|
1082
|
-
|
|
754
|
+
## 🙏 Acknowledgments
|
|
1083
755
|
|
|
1084
|
-
|
|
756
|
+
Built with ❤️ for the Vue.js community. Special thanks to:
|
|
1085
757
|
|
|
1086
|
-
-
|
|
1087
|
-
-
|
|
758
|
+
- Vue.js team for the amazing framework
|
|
759
|
+
- The open-source community for inspiration and feedback
|
|
760
|
+
- All contributors who helped shape ViewLogic Router
|
|
1088
761
|
|
|
1089
762
|
---
|
|
1090
763
|
|
|
1091
|
-
|
|
764
|
+
**ViewLogic Router** - One framework to rule them all! 🚀
|
|
765
|
+
|
|
766
|
+
*Simplify your Vue development with the power of unified architecture.*
|