create-lumfall 1.0.0 → 1.1.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.
Files changed (82) hide show
  1. package/README.md +24 -17
  2. package/cli.js +179 -24
  3. package/package.json +5 -7
  4. package/templates/basic/README.md +0 -113
  5. package/templates/basic/app/controller/demo.js +0 -28
  6. package/templates/basic/app/extend/README.md +0 -3
  7. package/templates/basic/app/middleware/README.md +0 -2
  8. package/templates/basic/app/middleware.js +0 -13
  9. package/templates/basic/app/pages/home/entry.home.js +0 -4
  10. package/templates/basic/app/pages/home/home.vue +0 -164
  11. package/templates/basic/app/router/demo.js +0 -17
  12. package/templates/basic/app/router-schema/demo.js +0 -29
  13. package/templates/basic/app/service/demo.js +0 -51
  14. package/templates/basic/app/webpack.config.js +0 -4
  15. package/templates/basic/build.js +0 -7
  16. package/templates/basic/config/config.beta.js +0 -4
  17. package/templates/basic/config/config.default.js +0 -32
  18. package/templates/basic/config/config.local.js +0 -4
  19. package/templates/basic/config/config.prod.js +0 -6
  20. package/templates/basic/package.json +0 -31
  21. package/templates/basic/server.js +0 -26
  22. package/templates/document/README.md +0 -145
  23. package/templates/document/app/extend/README.md +0 -6
  24. package/templates/document/app/middleware/README.md +0 -2
  25. package/templates/document/app/middleware.js +0 -2
  26. package/templates/document/app/pages/docs/assets/docs-logo.svg +0 -5
  27. package/templates/document/app/pages/docs/components/doc-layout.vue +0 -144
  28. package/templates/document/app/pages/docs/components/doc-navbar.vue +0 -103
  29. package/templates/document/app/pages/docs/components/doc-search.vue +0 -131
  30. package/templates/document/app/pages/docs/components/doc-sidebar.vue +0 -29
  31. package/templates/document/app/pages/docs/components/doc-toc.vue +0 -21
  32. package/templates/document/app/pages/docs/content.js +0 -28
  33. package/templates/document/app/pages/docs/docs-config.js +0 -154
  34. package/templates/document/app/pages/docs/docs.vue +0 -24
  35. package/templates/document/app/pages/docs/entry.docs.js +0 -26
  36. package/templates/document/app/pages/docs/markdown/highlight.js +0 -30
  37. package/templates/document/app/pages/docs/markdown/index.js +0 -129
  38. package/templates/document/app/pages/docs/search.js +0 -142
  39. package/templates/document/app/pages/docs/styles/docs.less +0 -1091
  40. package/templates/document/app/pages/docs/styles/vars.less +0 -87
  41. package/templates/document/app/pages/docs/theme.js +0 -47
  42. package/templates/document/app/pages/docs/utils.js +0 -58
  43. package/templates/document/app/pages/docs/views/doc-home.vue +0 -56
  44. package/templates/document/app/pages/docs/views/doc-page.vue +0 -147
  45. package/templates/document/app/webpack.config.js +0 -15
  46. package/templates/document/build.js +0 -6
  47. package/templates/document/config/config.beta.js +0 -2
  48. package/templates/document/config/config.default.js +0 -9
  49. package/templates/document/config/config.local.js +0 -2
  50. package/templates/document/config/config.prod.js +0 -2
  51. package/templates/document/docs/advanced/dashboard.md +0 -66
  52. package/templates/document/docs/advanced/health.md +0 -72
  53. package/templates/document/docs/advanced/monitoring.md +0 -60
  54. package/templates/document/docs/advanced/security.md +0 -88
  55. package/templates/document/docs/core/app-instance.md +0 -114
  56. package/templates/document/docs/core/controller-service.md +0 -113
  57. package/templates/document/docs/core/lifecycle.md +0 -60
  58. package/templates/document/docs/core/middleware.md +0 -83
  59. package/templates/document/docs/core/plugins.md +0 -77
  60. package/templates/document/docs/core/router-schema.md +0 -102
  61. package/templates/document/docs/dsl/api-contract.md +0 -88
  62. package/templates/document/docs/dsl/extend.md +0 -311
  63. package/templates/document/docs/dsl/menu.md +0 -101
  64. package/templates/document/docs/dsl/model-project.md +0 -122
  65. package/templates/document/docs/dsl/overview.md +0 -116
  66. package/templates/document/docs/dsl/reference.md +0 -175
  67. package/templates/document/docs/dsl/schema-actions.md +0 -135
  68. package/templates/document/docs/dsl/schema.md +0 -121
  69. package/templates/document/docs/frontend/build.md +0 -118
  70. package/templates/document/docs/frontend/curl.md +0 -75
  71. package/templates/document/docs/frontend/page.md +0 -99
  72. package/templates/document/docs/frontend/widgets.md +0 -151
  73. package/templates/document/docs/guide/config.md +0 -108
  74. package/templates/document/docs/guide/deployment.md +0 -115
  75. package/templates/document/docs/guide/getting-started.md +0 -199
  76. package/templates/document/docs/guide/introduction.md +0 -55
  77. package/templates/document/docs/guide/structure.md +0 -98
  78. package/templates/document/docs/reference/commands.md +0 -70
  79. package/templates/document/docs/reference/faq.md +0 -94
  80. package/templates/document/package.json +0 -36
  81. package/templates/document/scripts/build-static.js +0 -129
  82. package/templates/document/server.js +0 -13
@@ -1,75 +0,0 @@
1
- # 请求工具 curl
2
-
3
- 框架提供统一的请求工具 `$lumfallCurl`,封装了 axios、统一响应处理、
4
- 接口签名与 project_key 透传。
5
-
6
- ## 基本用法
7
-
8
- ```js
9
- import $curl from "$lumfallCurl";
10
-
11
- const res = await $curl({
12
- url: "/api/project/product/list",
13
- method: "get", // 默认 post
14
- query: { page: "1", pageSize: "20" }, // query 参数
15
- data: { title: "x" }, // 请求体
16
- headers: {}, // 额外请求头
17
- timeout: 60000, // 默认 60000ms
18
- responseType: "json",
19
- });
20
-
21
- if (res && res.success) {
22
- console.log(res.data, res.metadata);
23
- }
24
- ```
25
-
26
- 返回值是接口的 body(`{ success, data, metadata }` 或
27
- `{ success: false, message, code }`),网络异常时返回包含错误信息的对象,
28
- **不会 reject**——业务代码统一按 `res.success` 判断。
29
-
30
- ## 自动处理的协议细节
31
-
32
- ### 接口签名
33
-
34
- 每个请求自动携带 `s_t`(毫秒时间戳)与 `s_sign`(`md5("lumfall_" + st)`),
35
- 对应服务端的 apiSignature 校验(见[安全策略](../advanced/security.md))。
36
- 注意:默认签名串是 `lumfall`,服务端配置了自定义 `secret` 时,
37
- 客户端要用同样的 secret 重新生成签名。
38
-
39
- ### project_key
40
-
41
- 页面 URL 带 `?projectKey=xxx` 时(`window.__LUMFALL__.projectKey`),
42
- 对 `/api/project/` 开头的请求自动追加 `project_key` 请求头,
43
- 对应服务端的 projectHandler 校验。
44
-
45
- ### 错误码提示
46
-
47
- `success: false` 时按 code 弹出 Arco Message 错误提示:
48
-
49
- | code | 提示 |
50
- | --- | --- |
51
- | 442 | Invalid request parameters(参数校验失败) |
52
- | 445 | Invalid request(签名校验失败) |
53
- | 446 | Required parameter is missing(缺 project_key) |
54
- | 50000 | 显示服务端 message(`this.fail` 的默认约定码) |
55
- | 其他 | Network Error |
56
-
57
- 业务想自定义失败处理时,忽略提示逻辑直接处理返回值即可。
58
-
59
- ## window.__LUMFALL__
60
-
61
- curl 依赖页面模板注入的全局数据(见[页面系统](./page.md)):
62
-
63
- ```js
64
- window.__LUMFALL__ = {
65
- name: "my-app",
66
- env: "local",
67
- options: { /* serviceStart 的 options */ },
68
- projectKey: "",
69
- };
70
- ```
71
-
72
- ## 下一步
73
-
74
- - [安全策略](../advanced/security.md):签名与 project_key 的服务端逻辑
75
- - [Dashboard 与 Model 配置](../advanced/dashboard.md):curl 在 Dashboard 页面里的使用
@@ -1,99 +0,0 @@
1
- # 页面系统
2
-
3
- 前端页面放在 `app/pages/<page-name>/` 下,由 Webpack 按 `entry.<page-name>.js`
4
- 自动发现为入口,Koa 通过 `/view/<page-name>` 渲染。
5
-
6
- ## 页面结构
7
-
8
- ```text
9
- app/pages/
10
- └── project-list/
11
- ├── entry.project-list.js # 入口文件(命名必须是 entry.<page-name>.js)
12
- └── project-list.vue # 页面组件
13
- ```
14
-
15
- 入口文件通常长这样(`$lumfallBoot` 是框架提供的启动器别名):
16
-
17
- ```js
18
- import boot from "$lumfallBoot";
19
- import Page from "./project-list.vue";
20
-
21
- boot(Page);
22
- ```
23
-
24
- 需要 vue-router 的页面(多视图),把路由数组作为第二个参数传入:
25
-
26
- ```js
27
- import boot from "$lumfallBoot";
28
- import Page from "./dashboard.vue";
29
-
30
- boot(Page, {
31
- routes: [
32
- { path: "/view/dashboard", component: () => import("./dashboard.vue") },
33
- { path: "/view/dashboard/todo", component: () => import("./todo/todo.vue") },
34
- ],
35
- });
36
- ```
37
-
38
- ::: warning 路由 base
39
- `boot` 内部用 `createWebHistory()` 创建 router(base 为 `/`),所以路由 path
40
- 要写**完整路径**(`/view/<page>/...`),且必须与框架服务端路由 `/view/:page/*`
41
- 的前缀一致,否则刷新页面会被兜底重定向。
42
- :::
43
-
44
- ## 访问与错误码
45
-
46
- - 页面访问路径:`/view/<page-name>`
47
- - 页面不存在(入口没被发现):HTTP 404 + `code 4041`
48
- - 页面存在但模板没构建出来(没执行过构建):HTTP 503 + `code 5031`
49
- - 模板渲染异常(含 `template not found`)会 302 到 `homePath`
50
-
51
- ## 用脚手架生成页面
52
-
53
- ```sh
54
- # package.json 里配置了 new-page 脚本时
55
- pnpm new-page project-list # 生成 entry.project-list.js + project-list.vue
56
- pnpm new-page report --header # 额外套 HeaderContainer 布局
57
-
58
- # 没配脚本时直接调用框架的脚手架
59
- node ./node_modules/lumfall/scripts/generate-page.js project-list
60
- ```
61
-
62
- - 页面名必须是 kebab-case(小写字母开头),已存在的目录会被拒绝
63
- - `--header` 生成的页面用框架的 `HeaderContainer` 组件做整体布局
64
-
65
- ## boot 做了什么
66
-
67
- `$lumfallBoot`(框架 `app/pages/boot.js`)依次完成:
68
-
69
- 1. `createApp(pageComponent)`
70
- 2. 注册 Arco Design Vue(含图标库)与 Pinia
71
- 3. 若传了 `routes`:创建 vue-router(history 模式),等 `router.isReady()` 后挂载;
72
- 否则直接挂载到 `#root`
73
-
74
- 所以页面里可以直接使用 `a-*` 组件、`pinia` store,无需手动注册。
75
-
76
- ## 服务端注入的全局数据
77
-
78
- 页面模板由 nunjucks 渲染,`window.__LUMFALL__` 上有服务端注入的数据:
79
-
80
- ```js
81
- window.__LUMFALL__ = {
82
- name: "my-app", // serviceStart 的 options.name
83
- env: "local", // _ENV 值
84
- options: { ... }, // serviceStart 的完整 options(JSON)
85
- projectKey: "", // URL query 里的 projectKey(Dashboard 场景)
86
- };
87
- ```
88
-
89
- ## 页面发现规则
90
-
91
- - 框架自带页面(`dashboard`、`health`)与业务页面一起被发现
92
- - **同名时业务页面覆盖框架页面**
93
- - 同一来源(framework 或 business)内出现同名入口会启动报错
94
- - 排查页面是否被发现:`app.diagnostics.getManifest().pages`
95
-
96
- ## 下一步
97
-
98
- - [前端构建](./build.md):Webpack 管线与别名
99
- - [内置组件](./widgets.md):HeaderContainer / schema 三件套
@@ -1,151 +0,0 @@
1
- # 内置组件
2
-
3
- 框架内置了一组布局与 schema 驱动的组件(Arco Design Vue 风格),
4
- 业务页面直接通过别名引用,用于快速搭建列表页 / 表单页 / 管理台。
5
-
6
- ::: tip schema 三件套与 DSL 的关系
7
- 本页讲的是**独立使用**这些组件的 props / 事件;在 Dashboard 里由
8
- [DSL 配置](../dsl/overview.md)驱动它们时,schema 的 `xxxOption`
9
- 如何拆分、按钮与动态表单如何声明,见
10
- [schema 模块 DSL](../dsl/schema.md) 与[按钮与动态表单](../dsl/schema-actions.md)。
11
- :::
12
-
13
- ## 布局组件
14
-
15
- ### HeaderContainer
16
-
17
- ```vue
18
- <template>
19
- <HeaderContainer title="商品管理">
20
- <template #menu-content>
21
- <!-- 顶部导航区(如 a-menu) -->
22
- </template>
23
- <template #setting-content>
24
- <!-- 右上设置区 -->
25
- </template>
26
- <template #main-content>
27
- <!-- 主体内容 -->
28
- </template>
29
- </HeaderContainer>
30
- </template>
31
-
32
- <script setup>
33
- import HeaderContainer from "$lumfallHeaderContainer";
34
- </script>
35
- ```
36
-
37
- - props:`title`(标题,默认配 logo)
38
- - slots:`menu-content`(顶栏中部)、`setting-content`(顶栏右侧,自带用户下拉)、
39
- `main-content`(主体)
40
-
41
- ### SiderContainer
42
-
43
- ```vue
44
- <template>
45
- <SiderContainer>
46
- <template #menu-content>
47
- <!-- 左侧菜单(如 a-menu mode="vertical") -->
48
- </template>
49
- <template #main-content>
50
- <!-- 主体内容 -->
51
- </template>
52
- </SiderContainer>
53
- </template>
54
-
55
- <script setup>
56
- import SiderContainer from "$lumfallSiderContainer";
57
- </script>
58
- ```
59
-
60
- ## Schema 三件套
61
-
62
- 一套用 JSON Schema 描述「表格 / 搜索栏 / 表单」的组件,schema 里每个字段的
63
- `option` 控制对应控件的渲染。Dashboard 的 schema 菜单模块就用它们驱动页面
64
- (见 [Dashboard 与 Model 配置](../advanced/dashboard.md))。
65
-
66
- ### SchemaTable
67
-
68
- ```vue
69
- <template>
70
- <SchemaTable
71
- :schema="tableSchema"
72
- api="/api/project/product/list"
73
- :buttons="buttons"
74
- @operate="handleOperate"
75
- />
76
- </template>
77
-
78
- <script setup>
79
- import SchemaTable from "$lumfallSchemaTable";
80
- </script>
81
- ```
82
-
83
- - props:
84
- - `schema`:字段 schema;`option.visible` 控制列显示,
85
- `option.enumList`(`[{ label, value }]`)把枚举值渲染成彩色标签,
86
- 其余 `option` 透传给 `a-table-column`
87
- - `api`:列表接口**基址**——组件实际请求 `GET <api>/list`,
88
- 分页参数 `page` / `pageSize`,期望 `{ data, metadata: { total } }` 响应结构
89
- - `apiParams`:额外查询参数
90
- - `buttons`:操作列按钮 `[{ label, eventKey, eventOption, ...aButtonConfig }]`
91
- - emits:`operate`(点操作按钮,携带 `{ btnConfig, rowData }`)
92
- - 自带分页器(10/20/50/100/200 每页),默认省略号 + tooltip 防长文本撑破列宽
93
-
94
- ### SchemaSearchBar
95
-
96
- ```vue
97
- <template>
98
- <SchemaSearchBar :schema="searchSchema" @search="onSearch" @load="onLoad" />
99
- </template>
100
-
101
- <script setup>
102
- import SchemaSearchBar from "$lumfallSchemaSearchBar";
103
- </script>
104
- ```
105
-
106
- - props:`schema`(每个字段的 `option.componentType` 支持
107
- `input` / `select` / `dynamicSelect` / `dateRange`,`option.default` 为默认值)
108
- - emits:`search`(点查询,携带表单值)、`reset`(点重置)、
109
- `load`(动态控件就绪后触发一次,携带默认值)
110
- - 字段值由各控件 `getValue()` 合并产出
111
-
112
- ### SchemaForm
113
-
114
- ```vue
115
- <template>
116
- <SchemaForm ref="formRef" :schema="formSchema" :model="formData" />
117
- <a-button type="primary" @click="submit">提交</a-button>
118
- </template>
119
-
120
- <script setup>
121
- import SchemaForm from "$lumfallSchemaForm";
122
-
123
- const formRef = ref(null);
124
-
125
- const submit = async () => {
126
- if (!formRef.value.validate()) return;
127
- const value = formRef.value.getValue();
128
- // ...提交
129
- };
130
- </script>
131
- ```
132
-
133
- - props:`schema`(字段 `option.componentType` 支持 `input` / `inputNumber` /
134
- `select`;`option.required` 必填校验;`option.visible` / `option.disabled` /
135
- `option.default` / `option.enumList`)、`model`(受控数据)
136
- - expose:`validate()`(全部表单项校验,返回布尔)、`getValue()`
137
- (合并全部字段值)
138
-
139
- ### 扩展控件
140
-
141
- schema 组件的控件类型通过配置文件注册,业务可扩展自己的控件类型:
142
- `app/pages/widgets/schema-form/form-item-config.js`、
143
- `app/pages/widgets/schema-search-bar/complex-view/search-item-config.js`、
144
- `app/pages/dashboard/complex-view/schema-view/components/component-config.js`
145
- (分别对应别名 `$businessFormItemConfig` / `$businessSearchItemConfig` /
146
- `$businessComponentConfig`,见[前端构建](./build.md))。
147
-
148
- ## 下一步
149
-
150
- - [DSL 总览](../dsl/overview.md):用配置驱动这些组件生成整站管理台
151
- - [请求工具 curl](./curl.md):组件内部如何发请求
@@ -1,108 +0,0 @@
1
- # 配置
2
-
3
- 配置放在业务根目录的 `config/` 下,按 `_ENV` 环境分层加载,**四层浅合并**,
4
- 后面的覆盖前面的同名键:
5
-
6
- ```text
7
- 框架 config.default.js → 业务 config.default.js → 框架 config.<env>.js → 业务 config.<env>.js
8
- ```
9
-
10
- 环境由环境变量 `_ENV` 决定(`local` / `beta` / `prod`,缺省 `local`),
11
- **不是 `NODE_ENV`**。`_ENV` 不存在对应的配置文件时按空对象处理,不会报错。
12
-
13
- ## 基本用法
14
-
15
- ```js
16
- // config/config.default.js
17
- module.exports = {
18
- name: "my-app",
19
- apiBasePath: "/api",
20
- security: {
21
- apiSignature: { enabled: false, maxAgeMs: 600000 },
22
- projectKey: { enabled: true, headerName: "project_key" },
23
- },
24
- };
25
- ```
26
-
27
- ```js
28
- // config/config.prod.js —— 只写需要覆盖的键
29
- module.exports = {
30
- security: {
31
- apiSignature: { enabled: true, secret: process.env.API_SIGN_SECRET },
32
- },
33
- };
34
- ```
35
-
36
- ::: warning 浅合并
37
- 合并是**一层**的浅合并(`Object.assign` 语义):对象型配置在环境文件里
38
- 覆盖时是**整键替换**,不是深合并。上例的 `security` 会整体替换 default 里的
39
- `security`,所以环境文件里要写完整的安全配置,不要只写变化的子键。
40
- :::
41
-
42
- ## 工厂形式
43
-
44
- 配置文件也可以导出工厂函数 `(app) => object`,适合需要读 `app` 信息的场景。
45
- 工厂**必须返回普通对象**,否则启动直接失败:
46
-
47
- ```js
48
- // config/config.prod.js
49
- module.exports = (app) => ({
50
- port: process.env.PORT,
51
- env: app.env.get(),
52
- });
53
- ```
54
-
55
- ## 读取配置
56
-
57
- - 服务端在**请求阶段**读 `app.config`(controller / service 基类的 `this.config`
58
- getter 是安全的)
59
- - 不要在 loader 工厂执行期或构造期读 `app.config`——`configLoader` 在 controller /
60
- service 之后才执行,那时它还不存在
61
- - 前端页面可从 `window.__LUMFALL__.options` 拿到 `serviceStart` 的入参
62
- (注意是 options,不是 config;需要下发的值请通过接口返回)
63
-
64
- ## JSON Schema 强校验
65
-
66
- 需要强约束配置时,在 `serviceStart` 传入 `configSchema`(JSON Schema,Ajv 校验)。
67
- 合并后的配置不匹配会**启动失败**,错误信息指明环境、字段路径和原因:
68
-
69
- ```js
70
- // server.js
71
- const app = serviceStart({
72
- name: "my-app",
73
- homePath: "/view/home",
74
- configSchema: {
75
- type: "object",
76
- properties: {
77
- name: { type: "string" },
78
- "security": {
79
- type: "object",
80
- properties: {
81
- apiSignature: {
82
- type: "object",
83
- properties: {
84
- enabled: { type: "boolean" },
85
- secret: { type: "string" },
86
- },
87
- required: ["enabled"],
88
- },
89
- },
90
- },
91
- },
92
- required: ["name"],
93
- },
94
- });
95
- ```
96
-
97
- 校验失败示例:
98
-
99
- ```text
100
- Error: [config] merged configuration for "prod" is invalid: /security/apiSignature
101
- should have required property 'enabled'
102
- ```
103
-
104
- ## 生产环境建议
105
-
106
- - 密钥、连接串走环境变量(`process.env.XXX`)或配置中心,不要提交进仓库
107
- - 开启 `configSchema`,把「配置写错」从运行时问题提前到启动失败
108
- - 敏感配置不要通过接口原样下发给前端
@@ -1,115 +0,0 @@
1
- # 构建与部署
2
-
3
- ## 两种构建
4
-
5
- `frontendBuild(_ENV)` 只认两个值,其他值什么都不做(所以 `_ENV=beta` 没有 dev 构建):
6
-
7
- | `_ENV` | 行为 |
8
- | --- | --- |
9
- | `local` | 启动 Webpack dev server(Express,默认 `127.0.0.1:9002`),HMR 热更新,内存编译 |
10
- | `prod` | 完整产物构建到 `app/public/dist/prod/`,同时把每个页面的模板写到 `app/public/dist/entry.<name>.tpl` |
11
-
12
- ## 产物结构
13
-
14
- `_ENV=prod node build.js` 之后:
15
-
16
- ```text
17
- app/public/dist/
18
- ├── dev/ # _ENV=local 构建产物(含 dev/entry.<page>.tpl 模板)
19
- └── prod/
20
- ├── js/
21
- │ ├── runtime~entry.<page>_*.bundle.js
22
- │ ├── vendor_*.bundle.js # node_modules 第三方库
23
- │ ├── common_*.bundle.js # 被 ≥2 个入口引用的业务公共代码
24
- │ └── entry.<page>_*.bundle.js
25
- ├── css/
26
- │ ├── vendor_*.bundle.css
27
- │ └── ...
28
- └── entry.<page>.tpl # 页面模板(Koa 用 nunjucks 渲染,按模式分目录)
29
- ```
30
-
31
- - 分包策略(vendor / common / runtime)目的是让第三方与公共代码的长缓存稳定,
32
- 业务代码改动不影响 vendor 的 hash
33
- - 生产构建会先清空 `app/public/dist/` 再输出
34
- - 模板里的资源以 `/dist/prod/` 为 publicPath,由 Koa 的静态目录(`app/public`)直接服务
35
- - 页面模板按模式分目录(lumfall ≥ 1.2.0):dev 构建写 `dist/dev/`、prod 构建写 `dist/prod/`,服务按 `_ENV` 渲染对应目录——模式不匹配时返回 503 并提示应执行的构建命令,不会白屏
36
-
37
- ::: tip 模式不匹配时的表现(lumfall ≥ 1.2.0)
38
- dev 与 prod 构建的模板按模式分目录存放,服务只渲染 `_ENV` 对应目录的模板。
39
- 跑过 dev 构建后直接以 prod 模式启动(没执行 `build:prod`),页面请求会返回
40
- 503(code 5031)并附上应执行的构建命令——按提示重新构建即可,不会再出现
41
- 无报错的白屏。
42
- :::
43
-
44
- ## 本地开发
45
-
46
- ```sh
47
- pnpm start:dev
48
- # = concurrently "pnpm build:dev" "pnpm dev"
49
- ```
50
-
51
- - Webpack dev server 监听 `127.0.0.1:9002`,负责产出 JS/CSS 与 HMR 长连接
52
- - Koa 服务监听 `0.0.0.0:3000`,页面模板在编译完成后写到磁盘
53
- (`app/public/dist/entry.<name>.tpl`),模板里的资源 URL 指向 dev server
54
- - 页面访问走 Koa(`http://localhost:3000/view/<name>`),不要直接访问 9002
55
-
56
- `PORT` / `IP` 环境变量可以覆盖 Koa 监听地址:
57
-
58
- ```sh
59
- PORT=8080 IP=127.0.0.1 _ENV=local node server.js
60
- ```
61
-
62
- ## 生产部署
63
-
64
- ```sh
65
- pnpm install
66
- _ENV=prod node build.js
67
- _ENV=prod node server.js
68
- ```
69
-
70
- 通常再配一个进程守护(pm2 / systemd / 容器):
71
-
72
- ```sh
73
- # pm2 示例
74
- pm2 start server.js --name my-app --env _ENV=prod
75
- ```
76
-
77
- - 服务器只需要 `server.js` + 构建产物;`build.js` 可以在 CI 里执行
78
- - 健康检查接入负载均衡:存活探针用 `/health/live`,就绪探针用 `/health/ready`
79
- (详见 [健康检查](../advanced/health.md))
80
- - 日志输出在工作区 `logs/` 目录(log4js),注意持久化或采集
81
-
82
- ## 端口与地址速记
83
-
84
- | 变量 | 默认 | 说明 |
85
- | --- | --- | --- |
86
- | `PORT` | 3000 | Koa 服务端口 |
87
- | `IP` | 0.0.0.0 | Koa 监听地址 |
88
- | dev server | 127.0.0.1:9002 | Webpack dev server(HMR),端口写在框架 `webpack.dev.js` |
89
-
90
- ## 静态托管(Vercel / Netlify / Nginx)
91
-
92
- 不依赖 `/api` 的纯前端项目(页面数据都在构建期打包,比如文档站)可以脱离
93
- Koa 以**纯静态站点**部署,不需要 Node 进程:
94
-
95
- 1. 正常执行 `_ENV=prod` 构建,产物为 `app/public/dist/` 下的
96
- `entry.<page>.tpl`(页面外壳)与 `prod/`(带 hash 的静态资源)
97
- 2. 把 `.tpl` 里的 `window.__LUMFALL__` 占位符替换为静态值,另存为 `app.html`
98
- 3. 配置 SPA rewrite:`/view/<page>` 与 `/view/<page>/:path*` → `app.html`;
99
- 其余静态资源按原路径放行(模板里资源 URL 是 `/dist/prod/...` 绝对路径,
100
- 保持目录结构即可)
101
- 4. 原来的 302 兜底用一个静态跳转页替代(`/` → 目标页面)
102
-
103
- 完整可运行的参考实现见 `lumfall-document/` 的 `scripts/build-static.js`
104
- (`pnpm build:static` 一键产出 `dist-static/`,含 vercel.json rewrite 与
105
- 长缓存头,可直接部署 Vercel)。
106
-
107
- ::: tip 什么时候可以静态托管
108
- 页面只用「构建期已知的数据」——文档站、纯展示页、营销页。只要页面运行时
109
- 会调 `/api`(如 Dashboard、业务页面),就需要 Koa 服务,走上面的服务端部署。
110
- :::
111
-
112
- ## 下一步
113
-
114
- - [app 对象与启动流程](../core/app-instance.md):理解启动时发生了什么
115
- - [健康检查](../advanced/health.md):接入业务依赖探针