Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Commit e974bc5

Browse files
authored
Merge pull request #464 from linbingquan/dev-vue
feat(framework/vue): framework support for Vue.js
2 parents d251289 + b94e8ee commit e974bc5

File tree

19 files changed

+1314
-60
lines changed

19 files changed

+1314
-60
lines changed

CONTRIBUTING.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ You will need [Deno](https://deno.land/) 1.20+.
1919
6. Make a [pull request](https://github.com/alephjs/aleph.js/pulls).
2020
7. Merge to master branch by our maintainers.
2121

22+
### react-app
23+
2224
```bash
2325
# run the example app in development mode
2426
deno task dev examples/react-app
@@ -30,6 +32,19 @@ deno task start examples/react-app
3032
deno task build examples/react-app
3133
```
3234

35+
### vue-app
36+
37+
```bash
38+
# run the example app in development mode
39+
deno task dev examples/vue-app
40+
41+
# run the example app in production mode
42+
deno task start examples/vue-app
43+
44+
# build the example app into a worker for serverless platform
45+
deno task build examples/vue-app
46+
```
47+
3348
## Testing
3449

3550
You can run all tests with the following command:

examples/vue-app/app.vue

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Loading

examples/vue-app/assets/logo.svg

Lines changed: 4 additions & 4 deletions
Loading

examples/vue-app/index.html

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,7 @@
55
<meta charset="UTF-8">
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
77
<link rel="icon" href="./assets/logo.svg">
8-
<style>
9-
body {
10-
margin: 0;
11-
width: 100vw;
12-
height: 100vh;
13-
display: flex;
14-
align-items: center;
15-
justify-content: center;
16-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
17-
}
18-
</style>
8+
<link rel="stylesheet" href="./style/app.css">
199
</head>
2010

2111
<body>

examples/vue-app/main.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createApp } from "vue";
2-
import app from "./app.vue";
1+
import { App, createSSRApp } from "aleph/vue";
32

4-
createApp(app).mount("#root", true);
3+
createSSRApp(App).mount("#root", true);

examples/vue-app/routes/index.vue

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script setup>
2+
import { Link, Head } from "aleph/vue"
3+
</script>
4+
5+
<template>
6+
<div class="page y-center">
7+
<Head>
8+
<title>Aleph.js</title>
9+
<meta name="description" content="The Fullstack Framework in Deno." />
10+
</Head>
11+
<p class="logo">
12+
<img src="/assets/logo.svg" width="75" height="75" title="Aleph.js" />
13+
</p>
14+
<h1>
15+
The Fullstack Framework in Deno.
16+
</h1>
17+
<p>
18+
<strong>Aleph.js</strong>
19+
gives you the best developer experience for building web applications
20+
<br />
21+
with modern toolings.
22+
</p>
23+
<div class="external-links">
24+
<a href="https://alephjs.org/docs/get-started" target="_blank">
25+
Get Started
26+
</a>
27+
<a href="https://alephjs.org/docs" target="_blank">
28+
Docs
29+
</a>
30+
<a href="https://github.com/alephjs/aleph.js" target="_blank">
31+
Github
32+
</a>
33+
</div>
34+
<nav>
35+
<Link to="/todos">
36+
<button>Todos App Demo</button>
37+
</Link>
38+
</nav>
39+
</div>
40+
</template>

examples/vue-app/routes/todos.vue

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<script setup>
2+
import { Head, useData } from "aleph/vue"
3+
4+
const { data, isMutating, mutation } = useData();
5+
6+
async function onChange(todo) {
7+
const { id } = todo;
8+
const completed = !todo.completed;
9+
mutation.patch({ id, completed }, "replace")
10+
}
11+
12+
async function onSubmit(e) {
13+
e.preventDefault();
14+
const form = e.currentTarget;
15+
const fd = new FormData(form);
16+
const message = fd.get("message")?.toString().trim();
17+
if (message) {
18+
mutation.put({ message }, {
19+
// optimistic update without waiting for the server response
20+
optimisticUpdate: (data) => {
21+
return {
22+
todos: [...data.todos, { id: 0, message, completed: false }],
23+
};
24+
},
25+
// replace the data with the new data from the server
26+
replace: true,
27+
});
28+
form.reset();
29+
setTimeout(() => {
30+
form.querySelector("input")?.focus();
31+
}, 0);
32+
}
33+
}
34+
35+
function onClick(todo) {
36+
mutation.delete({ id: todo.id }, "replace");
37+
}
38+
</script>
39+
40+
<script>
41+
const storage = {
42+
todos: JSON.parse(window.localStorage?.getItem("todos") || "[]"),
43+
};
44+
45+
export const data = {
46+
cacheTtl: 0,
47+
get: (_req, ctx) => {
48+
return ctx.json(storage);
49+
},
50+
put: async (req, ctx) => {
51+
const { message } = await req.json();
52+
if (typeof message === "string") {
53+
const id = Date.now();
54+
storage.todos.push({ id, message, completed: false });
55+
window.localStorage?.setItem("todos", JSON.stringify(storage.todos));
56+
}
57+
return ctx.json(storage);
58+
},
59+
patch: async (req, ctx) => {
60+
const { id, message, completed } = await req.json();
61+
const todo = storage.todos.find((todo) => todo.id === id);
62+
if (todo) {
63+
if (typeof message === "string") {
64+
todo.message = message;
65+
}
66+
if (typeof completed === "boolean") {
67+
todo.completed = completed;
68+
}
69+
window.localStorage?.setItem("todos", JSON.stringify(storage.todos));
70+
}
71+
return ctx.json(storage);
72+
},
73+
delete: async (req, ctx) => {
74+
const { id } = await req.json();
75+
if (id) {
76+
storage.todos = storage.todos.filter((todo) => todo.id !== id);
77+
window.localStorage?.setItem("todos", JSON.stringify(storage.todos));
78+
}
79+
return ctx.json(storage);
80+
},
81+
};
82+
</script>
83+
84+
<template>
85+
<div className="page todos-app">
86+
<Head>
87+
<title>Todos</title>
88+
<meta name="description" content="A todos app powered by Aleph.js" />
89+
</Head>
90+
<h1>
91+
<span>Todos</span>
92+
</h1>
93+
<ul>
94+
<li v-for="todo in data?.todos" :key="todo.id">
95+
<input type="checkbox" :checked="todo.completed" @change="onChange(todo)" />
96+
<label :class="todo.completed ? 'completed' : ''">{{ todo.message }}</label>
97+
<button @click="onClick(todo)"></button>
98+
</li>
99+
</ul>
100+
<form @submit="onSubmit">
101+
<input :disabled="!!isMutating" type="text" name="message" placeholder="What needs to be done?"
102+
autofocus="autofocus" autocomplete="off" />
103+
</form>
104+
</div>
105+
</template>

examples/vue-app/server.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import { App, createSSRApp } from "aleph/vue";
12
import { serve } from "aleph/server";
2-
import { createSSRApp } from "vue";
33
import { renderToString } from "vue/server-renderer";
4-
import app from "./app.vue";
54

65
serve({
7-
ssr: async (_ctx) => await renderToString(createSSRApp(app)),
6+
config: {
7+
routes: "./routes/**/*.{vue,tsx,ts}",
8+
unocss: {
9+
// to enable unocss, please add presets:
10+
// presets: [ unoPreset ],
11+
},
12+
},
13+
ssr: async (ctx) => await renderToString(createSSRApp(App, { ssrContext: ctx }), ctx),
814
});

0 commit comments

Comments
 (0)