create-soybean 1.7.0 → 1.7.1
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.en_US.md +7 -5
- package/README.md +7 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -15
- package/package.json +12 -24
- package/template-ts-lib/_vite-hooks/pre-commit +1 -0
- package/template-ts-lib/package.json +40 -0
- package/template-ts-lib/pnpm-workspace.yaml +11 -0
- package/{template-tsdown → template-ts-lib}/tsconfig.json +7 -6
- package/template-ts-lib/vite.config.ts +22 -0
- package/template-ubean/README.md +20 -0
- package/template-ubean/_editorconfig +11 -0
- package/template-ubean/_gitattributes +2 -0
- package/template-ubean/_gitignore +32 -0
- package/template-ubean/_vite-hooks/pre-commit +1 -0
- package/template-ubean/eslint.config.mjs +3 -0
- package/template-ubean/package.json +38 -0
- package/template-ubean/pnpm-workspace.yaml +11 -0
- package/template-ubean/public/data.json +5 -0
- package/template-ubean/public/favicon.svg +64 -0
- package/template-ubean/src/app.ts +39 -0
- package/template-ubean/src/components/islands/IslandClock.vue +57 -0
- package/template-ubean/src/components/islands/IslandCounter.vue +66 -0
- package/template-ubean/src/components/islands/IslandMedia.vue +69 -0
- package/template-ubean/src/components/islands/IslandOnly.vue +54 -0
- package/template-ubean/src/components/islands/IslandVisibility.vue +60 -0
- package/template-ubean/src/layouts/admin.vue +13 -0
- package/template-ubean/src/layouts/default.vue +97 -0
- package/template-ubean/src/locales/en.json +31 -0
- package/template-ubean/src/locales/zh.json +31 -0
- package/template-ubean/src/middleware/01.global.ts +9 -0
- package/template-ubean/src/pages/about.vue +135 -0
- package/template-ubean/src/pages/blog/[...slug].vue +30 -0
- package/template-ubean/src/pages/dashboard/index.vue +19 -0
- package/template-ubean/src/pages/data-fetch.vue +95 -0
- package/template-ubean/src/pages/index.vue +117 -0
- package/template-ubean/src/pages/islands-test.vue +155 -0
- package/template-ubean/src/routes/api/hello.ts +60 -0
- package/template-ubean/src/routes/api/users/[id].ts +94 -0
- package/template-ubean/src/routes/api/users/index.ts +79 -0
- package/template-ubean/src/server.ts +9 -0
- package/template-ubean/src/typings/env.d.ts +37 -0
- package/template-ubean/tsconfig.json +26 -0
- package/template-ubean/ubean.config.ts +24 -0
- package/template-ubean/vite.config.ts +9 -0
- package/template-ubean-client/README.md +18 -0
- package/template-ubean-client/_gitignore +32 -0
- package/template-ubean-client/_vite-hooks/pre-commit +1 -0
- package/template-ubean-client/eslint.config.mjs +3 -0
- package/template-ubean-client/index.html +14 -0
- package/template-ubean-client/package.json +37 -0
- package/template-ubean-client/pnpm-workspace.yaml +11 -0
- package/template-ubean-client/public/favicon.svg +64 -0
- package/template-ubean-client/src/App.vue +135 -0
- package/template-ubean-client/src/main.ts +63 -0
- package/template-ubean-client/src/pages/404.vue +36 -0
- package/template-ubean-client/src/pages/about.vue +151 -0
- package/template-ubean-client/src/pages/about2.reuse.ts +8 -0
- package/template-ubean-client/src/pages/blog/[...slug].vue +72 -0
- package/template-ubean-client/src/pages/guide.md +30 -0
- package/template-ubean-client/src/pages/index.vue +101 -0
- package/template-ubean-client/src/pages/users/[id=numeric].vue +80 -0
- package/template-ubean-client/src/pages/users/index.vue +56 -0
- package/template-ubean-client/src/typings/globals.d.ts +17 -0
- package/template-ubean-client/tsconfig.json +25 -0
- package/template-ubean-client/vite.config.ts +20 -0
- package/template-vue/.gitignore +2 -0
- package/template-vue/_vite-hooks/pre-commit +1 -0
- package/template-vue/eslint.config.mjs +3 -0
- package/template-vue/package.json +17 -26
- package/template-vue/pnpm-workspace.yaml +11 -0
- package/template-vue/src/App.vue +3 -3
- package/template-vue/src/main.ts +2 -7
- package/template-vue/uno.config.ts +3 -3
- package/template-vue/vite.config.ts +10 -6
- package/template-vue-lib/_editorconfig +11 -0
- package/template-vue-lib/_gitattributes +2 -0
- package/template-vue-lib/_vite-hooks/pre-commit +1 -0
- package/template-vue-lib/eslint.config.mjs +3 -0
- package/template-vue-lib/package.json +53 -0
- package/template-vue-lib/pnpm-workspace.yaml +11 -0
- package/template-vue-lib/vite.config.ts +33 -0
- package/template-tsdown/_npmrc +0 -1
- package/template-tsdown/_oxfmtrc.json +0 -7
- package/template-tsdown/_oxlintrc.json +0 -16
- package/template-tsdown/package.json +0 -54
- package/template-tsdown/tsdown.config.ts +0 -10
- package/template-vue/_npmrc +0 -1
- package/template-vue/_oxfmtrc.json +0 -7
- package/template-vue/_oxlintrc.json +0 -17
- package/template-vue/er.config.ts +0 -9
- package/template-vue/src/components/HelloWorld.vue +0 -5
- package/template-vue/src/constants/index.ts +0 -0
- package/template-vue/src/layouts/base/index.vue +0 -9
- package/template-vue/src/layouts/blank/index.vue +0 -9
- package/template-vue/src/plugins/assets.ts +0 -4
- package/template-vue/src/router/index.ts +0 -25
- package/template-vue/src/shared/index.ts +0 -0
- package/template-vue/src/store/_plugin.ts +0 -17
- package/template-vue/src/store/counter/index.ts +0 -14
- package/template-vue/src/store/index.ts +0 -12
- package/template-vue/src/styles/app.css +0 -49
- package/template-vue/src/typings/components.d.ts +0 -17
- package/template-vue/src/views/(builtin)/403.vue +0 -5
- package/template-vue/src/views/(builtin)/404.vue +0 -5
- package/template-vue/src/views/(builtin)/login/index.vue +0 -5
- package/template-vue/src/views/(builtin)/wip.vue +0 -5
- package/template-vue/src/views/demo.tsx +0 -7
- package/template-vue/src/views/home/child.vue +0 -5
- package/template-vue/src/views/home/child2.vue +0 -5
- package/template-vue/src/views/home/index.vue +0 -9
- package/template-vue/src/views/list/[id].vue +0 -9
- package/template-vue/src/views/list/add.vue +0 -5
- package/template-vue/src/views/list/components/demo.vue +0 -0
- package/template-vue/src/views/list/components/search-list.vue +0 -5
- package/template-vue/src/views/list/detail2-[[id]]-[[userId]].vue +0 -5
- package/template-vue/src/views/list/detail_[id]_[userId].vue +0 -5
- package/template-vue/src/views/list/edit_[id].vue +0 -5
- package/template-vue/src/views/list/index.vue +0 -5
- package/template-vue/src/views/list/modules/list-table.vue +0 -5
- package/template-vue-tsdown/_npmrc +0 -1
- package/template-vue-tsdown/_oxfmtrc.json +0 -7
- package/template-vue-tsdown/_oxlintrc.json +0 -17
- package/template-vue-tsdown/package.json +0 -64
- package/template-vue-tsdown/tsdown.config.ts +0 -16
- package/template-vue-tsdown/vite.config.ts +0 -7
- /package/{template-tsdown → template-ts-lib}/.github/workflows/release.yml +0 -0
- /package/{template-tsdown → template-ts-lib}/.vscode/extensions.json +0 -0
- /package/{template-tsdown → template-ts-lib}/.vscode/launch.json +0 -0
- /package/{template-tsdown → template-ts-lib}/.vscode/settings.json +0 -0
- /package/{template-tsdown → template-ts-lib}/_editorconfig +0 -0
- /package/{template-tsdown → template-ts-lib}/_gitattributes +0 -0
- /package/{template-tsdown → template-ts-lib}/_gitignore +0 -0
- /package/{template-tsdown → template-ts-lib}/src/index.ts +0 -0
- /package/{template-vue-tsdown → template-ubean-client}/_editorconfig +0 -0
- /package/{template-vue-tsdown → template-ubean-client}/_gitattributes +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/.github/workflows/release.yml +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/.vscode/extensions.json +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/.vscode/launch.json +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/.vscode/settings.json +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/_gitignore +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/playground/index.html +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/playground/src/App.vue +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/playground/src/index.ts +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/playground/src/style.css +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/src/demo-pkg.vue +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/src/index.ts +0 -0
- /package/{template-vue-tsdown → template-vue-lib}/tsconfig.json +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# template-ubean-client
|
|
2
|
+
|
|
3
|
+
基于 [@ubean/vue](https://github.com/soybeanjs/ubean) 的独立 SPA(纯客户端)模板:精简内核 + 文件式路由,无 SSR、无自动引入,全部显式导入。
|
|
4
|
+
|
|
5
|
+
## 使用
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm dev
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## 演示内容
|
|
13
|
+
|
|
14
|
+
- 文件式路由: `src/pages/`(动态参数 `[id=numeric]`、catch-all `[...slug]`、reuse `.reuse.ts`、特殊页 `404.vue`)
|
|
15
|
+
- 页面级 head: `definePage({ head })` 提取到 route.meta(@unhead/vue 懒加载)
|
|
16
|
+
- matcher 校验: `/users/abc` 会被守卫重定向到 404
|
|
17
|
+
- Markdown 页面: `guide.md`、`blog/[...slug].vue`(@ubean/markdown 按需加载)
|
|
18
|
+
- 路由实例: `src/main.ts` 用 vue-router 原生 `createRouter` 创建
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
pnpm-debug.log*
|
|
8
|
+
lerna-debug.log*
|
|
9
|
+
|
|
10
|
+
node_modules
|
|
11
|
+
.DS_Store
|
|
12
|
+
dist
|
|
13
|
+
dist-ssr
|
|
14
|
+
coverage
|
|
15
|
+
*.local
|
|
16
|
+
|
|
17
|
+
# ubean
|
|
18
|
+
.ubean
|
|
19
|
+
|
|
20
|
+
# Editor directories and files
|
|
21
|
+
.vscode/*
|
|
22
|
+
!.vscode/extensions.json
|
|
23
|
+
!.vscode/settings.json
|
|
24
|
+
!.vscode/launch.json
|
|
25
|
+
.idea
|
|
26
|
+
*.suo
|
|
27
|
+
*.ntvs*
|
|
28
|
+
*.njsproj
|
|
29
|
+
*.sln
|
|
30
|
+
*.sw?
|
|
31
|
+
|
|
32
|
+
.VSCodeCounter
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vp staged
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="https://r2.soybeanjs.tech/soybeanjs/logo-ubean.svg?v=202608211738" type="image/svg+xml" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>ubean-client · standalone SPA</title>
|
|
8
|
+
</head>
|
|
9
|
+
|
|
10
|
+
<body>
|
|
11
|
+
<div id="app"></div>
|
|
12
|
+
<script type="module" src="/src/main.ts"></script>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "template-ubean-client",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "vp build",
|
|
8
|
+
"cleanup": "soy cleanup",
|
|
9
|
+
"commit": "soy git-commit",
|
|
10
|
+
"dev": "vp dev",
|
|
11
|
+
"fmt": "vp fmt",
|
|
12
|
+
"lint": "vp lint --fix && pnpm lint:vue",
|
|
13
|
+
"lint:vue": "eslint . --ext .vue --fix",
|
|
14
|
+
"preview": "vp preview",
|
|
15
|
+
"typecheck": "vue-tsc --noEmit --skipLibCheck",
|
|
16
|
+
"upkg": "soy ncu"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@ubean/vue": "^0.5.1",
|
|
20
|
+
"vue": "^3.5.42",
|
|
21
|
+
"vue-router": "^5.3.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@soybeanjs/cli": "^1.8.3",
|
|
25
|
+
"@soybeanjs/eslint-config-vue": "^0.1.1",
|
|
26
|
+
"@soybeanjs/oxc-config": "^0.2.6",
|
|
27
|
+
"@types/node": "^26.5.1",
|
|
28
|
+
"@ubean/markdown": "^0.5.1",
|
|
29
|
+
"@unhead/vue": "^3.4.0",
|
|
30
|
+
"@vitejs/plugin-vue": "^6.0.8",
|
|
31
|
+
"typescript": "catalog:",
|
|
32
|
+
"vite": "catalog:",
|
|
33
|
+
"vite-plus": "catalog:",
|
|
34
|
+
"vue-tsc": "^3.3.11"
|
|
35
|
+
},
|
|
36
|
+
"packageManager": "pnpm@12.4.1"
|
|
37
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
catalog:
|
|
2
|
+
vite: npm:@voidzero-dev/vite-plus-core@latest
|
|
3
|
+
vite-plus: latest
|
|
4
|
+
typescript: npm:typescript-native-bridge@latest
|
|
5
|
+
overrides:
|
|
6
|
+
vite: 'catalog:'
|
|
7
|
+
typescript: 'catalog:'
|
|
8
|
+
shamefullyHoist: true
|
|
9
|
+
allowBuilds:
|
|
10
|
+
esbuild: false
|
|
11
|
+
minimumReleaseAge: 0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<svg width="100%" height="100%" version="1.1" viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg"
|
|
2
|
+
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<g>
|
|
4
|
+
<path
|
|
5
|
+
d="M 200,866 C 100,866 50,779.4 100,692.8 L 200,519.6 C 220,485 240,490 265,499.6 S 360,542.68 360,542.68 C 480.5,601 498,642.5 500,720 C 498,811 462,856 420,866"
|
|
6
|
+
fill="url(#LinearGradient)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
7
|
+
<path
|
|
8
|
+
d="M 420,866 C 455,861 478,846 500,827 C 614,696 615,597 500,517 C 394,444 333,374 380,207.82 L 260,415.67 C 240.22,450 254.37,465.1 275.28,481.79 S 360,542.68 360,542.68 C 480.5,601 498,642.5 500,720 C 498,811 462,856 420,866"
|
|
9
|
+
fill="url(#LinearGradient_2)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
10
|
+
<path d="M 500,517 C 394,444 333,374 380,207.82 L 400,173.2 C 367,295 421,350 603,428 C 572,440 524,474 500,517"
|
|
11
|
+
fill="url(#LinearGradient_3)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
12
|
+
<path d="M 500,827 L 660,660 C 738,589 710,482 603,428 C 572,440 524,474 500,517 C 615,597 614,696 500,827"
|
|
13
|
+
fill="url(#LinearGradient_4)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
14
|
+
<path d="M 400,173.2 C 367,295 421,350 603,428 C 690,389, 750,445 788,500 L 600,173.2 C 550,86.6 450,86.6 400,173.2"
|
|
15
|
+
fill="url(#LinearGradient_5)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
16
|
+
<path
|
|
17
|
+
d="M 500,827 L 660,660 C 738,589 710,482 603,428 C 690,389, 750,445 788,500 C 816,554 797,606 750,640 L 500,827"
|
|
18
|
+
fill="url(#LinearGradient_6)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
19
|
+
<path
|
|
20
|
+
d="M 788,500 C 816,554 797,606 750,640 L 500,827 C 497,851 513,862 540,866 L 800,866 C 900,866 950,779.4 900,692.8 L 788,500"
|
|
21
|
+
fill="url(#LinearGradient_7)" fill-rule="nonzero" opacity="1" stroke="none" />
|
|
22
|
+
<!-- bean -->
|
|
23
|
+
<g transform="translate(140, 650) scale(0.18)">
|
|
24
|
+
<path d="M539.04,146.5c-77.37,32.74-126.47,114.53-123.22,198,.61,150.92,169.47,210.6,214.99,341.13,39.71,96.62-16.62,230.68-122.31,253.16-374.89-83.15-390.44-785.49,30.54-792.29ZM564.61,948.5c68.1-32.18,118.51-95.45,132.49-169.97,41.81-214.6-158.22-254.7-215.15-413.02-31.14-86.21,21.33-198.66,114.56-214.57,129.21,21.58,214.93,148.7,230.96,272.19,7.94,38.53,21.57,75.37,34.41,112.43,75.75,198.26-76.06,438.99-297.26,412.94Z" fill="#ffffff" fill-rule="evenodd"/>
|
|
25
|
+
</g>
|
|
26
|
+
</g>
|
|
27
|
+
<defs>
|
|
28
|
+
<linearGradient gradientTransform="matrix(104.391 -73.3432 73.3432 104.391 277.441 710.122)"
|
|
29
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient" x1="0" x2="1" y1="0" y2="0">
|
|
30
|
+
<stop offset="0" stop-color="#373ebf" />
|
|
31
|
+
<stop offset="1" stop-color="#5058e6" />
|
|
32
|
+
</linearGradient>
|
|
33
|
+
<linearGradient gradientTransform="matrix(-173.747 557.324 -557.324 -173.747 508.829 258.172)"
|
|
34
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient_2" x1="0" x2="1" y1="0" y2="0">
|
|
35
|
+
<stop offset="0" stop-color="#c2d6ff" />
|
|
36
|
+
<stop offset="1" stop-color="#646cff" />
|
|
37
|
+
</linearGradient>
|
|
38
|
+
<linearGradient gradientTransform="matrix(157.951 295.666 -295.666 157.951 382.944 193.642)"
|
|
39
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient_3" x1="0" x2="1" y1="0" y2="0">
|
|
40
|
+
<stop offset="0" stop-color="#5058e6" />
|
|
41
|
+
<stop offset="1" stop-color="#373ebf" />
|
|
42
|
+
</linearGradient>
|
|
43
|
+
<linearGradient gradientTransform="matrix(-44.3023 219.578 -219.578 -44.3023 619.69 469.652)"
|
|
44
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient_4" x1="0" x2="1" y1="0" y2="0">
|
|
45
|
+
<stop offset="0" stop-color="#91a7ff" />
|
|
46
|
+
<stop offset="1" stop-color="#5058e6" />
|
|
47
|
+
</linearGradient>
|
|
48
|
+
<linearGradient gradientTransform="matrix(125.52 334.256 -334.256 125.52 539.723 235.139)"
|
|
49
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient_5" x1="0" x2="1" y1="0" y2="0">
|
|
50
|
+
<stop offset="0" stop-color="#646cff" />
|
|
51
|
+
<stop offset="1" stop-color="#c2d6ff" />
|
|
52
|
+
</linearGradient>
|
|
53
|
+
<linearGradient gradientTransform="matrix(-241.23 357.206 -357.206 -241.23 754.054 449.312)"
|
|
54
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient_6" x1="0" x2="1" y1="0" y2="0">
|
|
55
|
+
<stop offset="0" stop-color="#c2d6ff" />
|
|
56
|
+
<stop offset="1" stop-color="#646cff" />
|
|
57
|
+
</linearGradient>
|
|
58
|
+
<linearGradient gradientTransform="matrix(125.978 210.065 -210.065 125.978 596.433 613.665)"
|
|
59
|
+
gradientUnits="userSpaceOnUse" id="LinearGradient_7" x1="0" x2="1" y1="0" y2="0">
|
|
60
|
+
<stop offset="0" stop-color="#373ebf" />
|
|
61
|
+
<stop offset="1" stop-color="#5058e6" />
|
|
62
|
+
</linearGradient>
|
|
63
|
+
</defs>
|
|
64
|
+
</svg>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// 应用外壳 = 布局(精简内核无布局扫描,App.vue 直接承担)。
|
|
3
|
+
// Link/PageView 为全局组件(ubeanVue 插件注册),类型见 src/typings/globals.d.ts。
|
|
4
|
+
import { useRouter } from 'vue-router';
|
|
5
|
+
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div class="layout">
|
|
11
|
+
<header class="header">
|
|
12
|
+
<nav class="nav">
|
|
13
|
+
<Link to="/" class="logo">ubean-client</Link>
|
|
14
|
+
<div class="nav-links">
|
|
15
|
+
<Link to="/">首页</Link>
|
|
16
|
+
<Link to="/users">用户</Link>
|
|
17
|
+
<Link to="/guide">指南</Link>
|
|
18
|
+
<Link to="/blog/hello">博客</Link>
|
|
19
|
+
<Link to="/about">关于</Link>
|
|
20
|
+
<button class="back-btn" type="button" @click="router.back()">← back</button>
|
|
21
|
+
</div>
|
|
22
|
+
</nav>
|
|
23
|
+
</header>
|
|
24
|
+
<main class="main">
|
|
25
|
+
<!-- 页面渲染出口:keep-alive / transition / reload 信号都在这里生效 -->
|
|
26
|
+
<PageView />
|
|
27
|
+
</main>
|
|
28
|
+
<footer class="footer">
|
|
29
|
+
<p>standalone SPA · 只依赖 @ubean/vue(vue + vue-router)</p>
|
|
30
|
+
</footer>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style>
|
|
35
|
+
* {
|
|
36
|
+
margin: 0;
|
|
37
|
+
padding: 0;
|
|
38
|
+
box-sizing: border-box;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
body {
|
|
42
|
+
background: #fafafa;
|
|
43
|
+
color: #24292f;
|
|
44
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.layout {
|
|
48
|
+
min-height: 100vh;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.header {
|
|
54
|
+
background: linear-gradient(135deg, #42b883, #35495e);
|
|
55
|
+
padding: 0 20px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.nav {
|
|
59
|
+
max-width: 960px;
|
|
60
|
+
margin: 0 auto;
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
height: 60px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.logo {
|
|
68
|
+
color: white;
|
|
69
|
+
font-size: 1.2rem;
|
|
70
|
+
font-weight: 700;
|
|
71
|
+
text-decoration: none;
|
|
72
|
+
font-family: 'SF Mono', ui-monospace, monospace;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.nav-links {
|
|
76
|
+
display: flex;
|
|
77
|
+
gap: 0.95rem;
|
|
78
|
+
align-items: center;
|
|
79
|
+
flex-wrap: wrap;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.nav-links a {
|
|
83
|
+
color: rgba(255, 255, 255, 0.88);
|
|
84
|
+
text-decoration: none;
|
|
85
|
+
font-weight: 500;
|
|
86
|
+
font-size: 0.88rem;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.nav-links a.router-link-active {
|
|
90
|
+
color: white;
|
|
91
|
+
text-decoration: underline;
|
|
92
|
+
text-underline-offset: 6px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.back-btn {
|
|
96
|
+
background: rgba(255, 255, 255, 0.15);
|
|
97
|
+
border: 1px solid rgba(255, 255, 255, 0.35);
|
|
98
|
+
color: white;
|
|
99
|
+
border-radius: 6px;
|
|
100
|
+
padding: 4px 10px;
|
|
101
|
+
font-size: 0.8rem;
|
|
102
|
+
cursor: pointer;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.back-btn:hover {
|
|
106
|
+
background: rgba(255, 255, 255, 0.25);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.main {
|
|
110
|
+
flex: 1;
|
|
111
|
+
max-width: 960px;
|
|
112
|
+
margin: 0 auto;
|
|
113
|
+
padding: 40px 20px;
|
|
114
|
+
width: 100%;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.footer {
|
|
118
|
+
border-top: 1px solid rgba(128, 128, 128, 0.25);
|
|
119
|
+
color: gray;
|
|
120
|
+
text-align: center;
|
|
121
|
+
padding: 1.2rem;
|
|
122
|
+
font-size: 0.85rem;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* about 路由声明的页面级过渡(meta.transition: 'fade') */
|
|
126
|
+
.fade-enter-active,
|
|
127
|
+
.fade-leave-active {
|
|
128
|
+
transition: opacity 0.25s ease;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.fade-enter-from,
|
|
132
|
+
.fade-leave-to {
|
|
133
|
+
opacity: 0;
|
|
134
|
+
}
|
|
135
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createApp } from 'vue';
|
|
2
|
+
import { createRouter, createWebHistory } from 'vue-router';
|
|
3
|
+
import {
|
|
4
|
+
ubeanVue,
|
|
5
|
+
defineMatcher,
|
|
6
|
+
createMatcherGuard,
|
|
7
|
+
createPageHead,
|
|
8
|
+
setupPageHeadGuard,
|
|
9
|
+
LOADING_KEY,
|
|
10
|
+
ERROR_KEY
|
|
11
|
+
} from '@ubean/vue';
|
|
12
|
+
import { routes, loadingComponent, errorComponent } from 'virtual:ubean-vue-routes';
|
|
13
|
+
import App from './App.vue';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 独立 SPA 引导 —— @ubean/vue 精简内核 + 文件式路由(全能力):
|
|
17
|
+
* 1. 路由表来自 `virtual:ubean-vue-routes`(@ubean/vue/vite 扫描 src/pages
|
|
18
|
+
* 并编译期提取 definePage/frontmatter 生成);内核不产出 router 工厂,
|
|
19
|
+
* 直接用 vue-router 原生 createRouter 自行创建实例。
|
|
20
|
+
* 2. `app.use(ubeanVue, { routes })` 一次完成:
|
|
21
|
+
* 全局组件(Link/PageView/SlotView)注册 + keep-alive 缓存播种。
|
|
22
|
+
* 3. 动态参数 matcher:`[id=numeric]` 文件语法把 matchers 写进 route.meta,
|
|
23
|
+
* 这边注册校验函数 + 挂守卫;校验失败重定向 NotFound(404.vue)。
|
|
24
|
+
* 4. 特殊页注入协议:loading/error 组件经 LOADING_KEY/ERROR_KEY 接入
|
|
25
|
+
* PageView 的 <Suspense>/<ErrorBoundary>(框架运行时默认提供,精简内核自行 provide)。
|
|
26
|
+
* 5. 页面级 head(opt-in):@unhead/vue 为 optional peer,createPageHead
|
|
27
|
+
* 懒加载;守卫在每次导航后 push route.meta.head。
|
|
28
|
+
*
|
|
29
|
+
* 无应用工厂、无 i18n、无自动引入 —— 全部显式导入。
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// matcher 注册:`[id=numeric]` 文件语法的运行时校验函数
|
|
33
|
+
defineMatcher('numeric', value => /^\d+$/.test(value));
|
|
34
|
+
|
|
35
|
+
const router = createRouter({
|
|
36
|
+
history: createWebHistory(),
|
|
37
|
+
routes,
|
|
38
|
+
scrollBehavior(_to, _from, savedPosition) {
|
|
39
|
+
if (savedPosition) return savedPosition;
|
|
40
|
+
if (_to.hash) return { el: _to.hash, behavior: 'smooth' };
|
|
41
|
+
return { top: 0 };
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// matcher 守卫:meta.matchers 校验失败 → NotFound(/users/abc 会被拦截)
|
|
46
|
+
router.beforeEach(createMatcherGuard());
|
|
47
|
+
|
|
48
|
+
const app = createApp(App);
|
|
49
|
+
app.use(router);
|
|
50
|
+
app.use(ubeanVue, { routes });
|
|
51
|
+
|
|
52
|
+
// 特殊页注入(PageView 的 Suspense fallback / ErrorBoundary 渲染)
|
|
53
|
+
app.provide(LOADING_KEY, loadingComponent);
|
|
54
|
+
app.provide(ERROR_KEY, errorComponent);
|
|
55
|
+
|
|
56
|
+
// 页面级 head(@unhead/vue 懒加载;失败静默 —— head 是纯增强)
|
|
57
|
+
createPageHead()
|
|
58
|
+
.then(head => setupPageHeadGuard(router, head))
|
|
59
|
+
.catch(() => {
|
|
60
|
+
/* @unhead/vue 未安装时跳过 */
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
app.mount('#app');
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// 特殊页:pages/404.vue 由 vite 插件注册为 catch-all /:pathMatch(.*)*
|
|
3
|
+
import { useRoute } from 'vue-router';
|
|
4
|
+
|
|
5
|
+
const route = useRoute();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<section class="not-found">
|
|
10
|
+
<h1>404</h1>
|
|
11
|
+
<p class="mono">未匹配的路径:{{ route.fullPath }}</p>
|
|
12
|
+
<p>
|
|
13
|
+
回到
|
|
14
|
+
<Link to="/">首页</Link>
|
|
15
|
+
</p>
|
|
16
|
+
</section>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<style scoped>
|
|
20
|
+
.not-found {
|
|
21
|
+
text-align: center;
|
|
22
|
+
padding: 4rem 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
h1 {
|
|
26
|
+
font-size: 4rem;
|
|
27
|
+
color: #42b883;
|
|
28
|
+
margin-bottom: 0.5rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.mono {
|
|
32
|
+
font-family: 'SF Mono', ui-monospace, monospace;
|
|
33
|
+
color: gray;
|
|
34
|
+
margin-bottom: 1rem;
|
|
35
|
+
}
|
|
36
|
+
</style>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// 模块级挂载计数:跨组件实例持久 —— reload 会销毁并重建实例,
|
|
3
|
+
// 实例内的状态会随之重置,模块级变量才能观察"重挂载了几次"。
|
|
4
|
+
let totalMounts = 0;
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
// 过渡与重载演示 —— definePage({ transition: 'fade' }) 声明页面级过渡(CSS 在 App.vue);
|
|
9
|
+
// reload 内部:隐藏页面(实例入缓存)→ 空窗口内剪除缓存条目 →
|
|
10
|
+
// bump 渲染 key 全新挂载并重新入缓存,全程不触碰活跃页的缓存成员关系。
|
|
11
|
+
import { ref } from 'vue';
|
|
12
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
13
|
+
import {
|
|
14
|
+
definePage,
|
|
15
|
+
useReloadSignal,
|
|
16
|
+
getPageTransitionName,
|
|
17
|
+
setPageTransition,
|
|
18
|
+
clearPageTransition,
|
|
19
|
+
usePage
|
|
20
|
+
} from '@ubean/vue';
|
|
21
|
+
|
|
22
|
+
definePage({ name: 'About', cache: true, transition: 'fade' });
|
|
23
|
+
|
|
24
|
+
const reload = useReloadSignal();
|
|
25
|
+
const transitionName = getPageTransitionName();
|
|
26
|
+
const route = useRoute();
|
|
27
|
+
const router = useRouter();
|
|
28
|
+
// 精简版 usePage():仅 pageData(props/component/errors)。
|
|
29
|
+
// 精简 SPA 无工厂注入 → 共享空对象;路由态用 useRoute()。
|
|
30
|
+
const pageData = usePage();
|
|
31
|
+
|
|
32
|
+
const mountSeq = ref(++totalMounts);
|
|
33
|
+
|
|
34
|
+
async function doReload() {
|
|
35
|
+
await reload.reload('About');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function setFade() {
|
|
39
|
+
setPageTransition('fade');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function clearTransition() {
|
|
43
|
+
clearPageTransition();
|
|
44
|
+
}
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<section>
|
|
49
|
+
<h1>过渡与重载控制</h1>
|
|
50
|
+
<p class="subtitle">
|
|
51
|
+
本页路由声明
|
|
52
|
+
<code>meta.transition: 'fade'</code>
|
|
53
|
+
;全局过渡名可运行时切换。
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
<div class="card">
|
|
57
|
+
<p>
|
|
58
|
+
全局过渡名:
|
|
59
|
+
<code class="mono">{{ transitionName || '(无)' }}</code>
|
|
60
|
+
</p>
|
|
61
|
+
<div class="row">
|
|
62
|
+
<button type="button" @click="setFade">setPageTransition('fade')</button>
|
|
63
|
+
<button type="button" @click="clearTransition">clearPageTransition</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="card">
|
|
68
|
+
<p>
|
|
69
|
+
重载计数:
|
|
70
|
+
<strong>{{ reload.counter.value }}</strong>
|
|
71
|
+
</p>
|
|
72
|
+
<p>
|
|
73
|
+
重载中:
|
|
74
|
+
<strong>{{ reload.reloading.value }}</strong>
|
|
75
|
+
</p>
|
|
76
|
+
<p class="mono">本实例是 About 页第 {{ mountSeq }} 次挂载(reload 递增)</p>
|
|
77
|
+
<div class="row">
|
|
78
|
+
<button type="button" :disabled="reload.reloading.value" @click="doReload">reload.reload('About')</button>
|
|
79
|
+
<button type="button" @click="router.push('/')">router.push('/')</button>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<p class="hint">
|
|
84
|
+
<code>useRoute().fullPath</code>
|
|
85
|
+
=
|
|
86
|
+
<code class="mono">{{ route.fullPath }}</code>
|
|
87
|
+
(路由驱动);精简版
|
|
88
|
+
<code>usePage()</code>
|
|
89
|
+
仅 pageData —— component/props/errors 为
|
|
90
|
+
<code class="mono">
|
|
91
|
+
{{ String(pageData.component ?? '(空)') }}/{{ Object.keys(pageData.props ?? {}).length }}/{{
|
|
92
|
+
pageData.errors === null ? 'null' : 'map'
|
|
93
|
+
}}
|
|
94
|
+
</code>
|
|
95
|
+
(无工厂注入)。
|
|
96
|
+
</p>
|
|
97
|
+
</section>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<style scoped>
|
|
101
|
+
h1 {
|
|
102
|
+
font-size: 1.5rem;
|
|
103
|
+
margin-bottom: 0.5rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.subtitle {
|
|
107
|
+
color: gray;
|
|
108
|
+
margin-bottom: 1.25rem;
|
|
109
|
+
font-size: 0.92rem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.card {
|
|
113
|
+
border: 1px solid rgba(128, 128, 128, 0.3);
|
|
114
|
+
border-radius: 10px;
|
|
115
|
+
padding: 1rem 1.25rem;
|
|
116
|
+
margin-bottom: 1rem;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.row {
|
|
120
|
+
display: flex;
|
|
121
|
+
gap: 0.6rem;
|
|
122
|
+
flex-wrap: wrap;
|
|
123
|
+
margin-top: 0.5rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.row button {
|
|
127
|
+
cursor: pointer;
|
|
128
|
+
border: 1px solid rgba(66, 184, 131, 0.6);
|
|
129
|
+
background: transparent;
|
|
130
|
+
color: inherit;
|
|
131
|
+
border-radius: 6px;
|
|
132
|
+
padding: 5px 12px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.row button:disabled {
|
|
136
|
+
opacity: 0.5;
|
|
137
|
+
cursor: wait;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.mono {
|
|
141
|
+
font-family: 'SF Mono', ui-monospace, monospace;
|
|
142
|
+
font-size: 0.82rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.hint {
|
|
146
|
+
margin-top: 1.25rem;
|
|
147
|
+
font-size: 0.88rem;
|
|
148
|
+
color: gray;
|
|
149
|
+
line-height: 1.7;
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// catch-all:文件名 `[...slug]` → 路由 `/blog/**:slug`(零或多个参数,数组)。
|
|
3
|
+
import { computed } from 'vue';
|
|
4
|
+
import { useRoute } from 'vue-router';
|
|
5
|
+
import { definePage } from '@ubean/vue';
|
|
6
|
+
|
|
7
|
+
definePage({ name: 'BlogAllSlug' });
|
|
8
|
+
|
|
9
|
+
const route = useRoute();
|
|
10
|
+
// typed-router 生效时 route.params 是全路由参数联合,此处按本页形状窄化。
|
|
11
|
+
const params = route.params as { slug?: string[] };
|
|
12
|
+
const crumbs = computed(() => params.slug ?? []);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<section>
|
|
17
|
+
<h1>博客(catch-all)</h1>
|
|
18
|
+
<p class="subtitle">
|
|
19
|
+
路由
|
|
20
|
+
<code>/blog/**:slug</code>
|
|
21
|
+
—— 文件
|
|
22
|
+
<code>[...slug].vue</code>
|
|
23
|
+
</p>
|
|
24
|
+
<div class="card">
|
|
25
|
+
<p>
|
|
26
|
+
slug 段数:
|
|
27
|
+
<strong>{{ crumbs.length }}</strong>
|
|
28
|
+
</p>
|
|
29
|
+
<ol class="crumbs">
|
|
30
|
+
<li v-for="(c, i) in crumbs" :key="i" class="mono">{{ c }}</li>
|
|
31
|
+
</ol>
|
|
32
|
+
</div>
|
|
33
|
+
<nav class="toc">
|
|
34
|
+
<Link to="/blog">/blog</Link>
|
|
35
|
+
<Link to="/blog/vue/release-notes">/blog/vue/release-notes</Link>
|
|
36
|
+
<Link to="/blog/2026/08/kernel">/blog/2026/08/kernel</Link>
|
|
37
|
+
</nav>
|
|
38
|
+
</section>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<style scoped>
|
|
42
|
+
h1 {
|
|
43
|
+
font-size: 1.5rem;
|
|
44
|
+
margin-bottom: 0.5rem;
|
|
45
|
+
}
|
|
46
|
+
.subtitle {
|
|
47
|
+
color: gray;
|
|
48
|
+
margin-bottom: 1.25rem;
|
|
49
|
+
font-size: 0.92rem;
|
|
50
|
+
}
|
|
51
|
+
.card {
|
|
52
|
+
border: 1px solid rgba(128, 128, 128, 0.3);
|
|
53
|
+
border-radius: 10px;
|
|
54
|
+
padding: 1rem 1.25rem;
|
|
55
|
+
}
|
|
56
|
+
.crumbs {
|
|
57
|
+
margin: 0.5rem 0 0 1.2rem;
|
|
58
|
+
}
|
|
59
|
+
.mono {
|
|
60
|
+
font-family: 'SF Mono', ui-monospace, monospace;
|
|
61
|
+
}
|
|
62
|
+
.toc {
|
|
63
|
+
display: flex;
|
|
64
|
+
gap: 1rem;
|
|
65
|
+
margin-top: 1rem;
|
|
66
|
+
flex-wrap: wrap;
|
|
67
|
+
}
|
|
68
|
+
.toc a {
|
|
69
|
+
color: #42b883;
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
}
|
|
72
|
+
</style>
|