@qlover/create-app 1.1.0 → 2.0.0

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 (556) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/index.cjs +9 -9
  3. package/dist/index.js +9 -9
  4. package/package.json +3 -2
  5. package/dist/configs/_common/.editorconfig +0 -23
  6. package/dist/configs/_common/.env.template +0 -13
  7. package/dist/configs/_common/.gitattributes +0 -2
  8. package/dist/configs/_common/.github/workflows/general-check.yml +0 -41
  9. package/dist/configs/_common/.github/workflows/release.yml +0 -81
  10. package/dist/configs/_common/.gitignore.template +0 -64
  11. package/dist/configs/_common/.husky/commit-msg +0 -3
  12. package/dist/configs/_common/.husky/pre-commit +0 -3
  13. package/dist/configs/_common/.prettierignore +0 -17
  14. package/dist/configs/_common/.prettierrc.js +0 -7
  15. package/dist/configs/_common/.vscode/extensions.json +0 -9
  16. package/dist/configs/_common/.vscode/react.code-snippets +0 -19
  17. package/dist/configs/_common/.vscode/settings.json +0 -16
  18. package/dist/configs/_common/commitlint.config.js +0 -10
  19. package/dist/configs/_common/package.json.template +0 -75
  20. package/dist/configs/node-lib/eslint.config.js +0 -50
  21. package/dist/templates/next-app/.env.template +0 -25
  22. package/dist/templates/next-app/.prettierignore +0 -58
  23. package/dist/templates/next-app/README.en.md +0 -130
  24. package/dist/templates/next-app/README.md +0 -130
  25. package/dist/templates/next-app/config/IOCIdentifier.ts +0 -74
  26. package/dist/templates/next-app/config/Identifier/api.ts +0 -41
  27. package/dist/templates/next-app/config/Identifier/common/admint.table.ts +0 -69
  28. package/dist/templates/next-app/config/Identifier/common/common.ts +0 -90
  29. package/dist/templates/next-app/config/Identifier/common/index.ts +0 -3
  30. package/dist/templates/next-app/config/Identifier/common/validators.ts +0 -34
  31. package/dist/templates/next-app/config/Identifier/index.ts +0 -3
  32. package/dist/templates/next-app/config/Identifier/pages/index.ts +0 -7
  33. package/dist/templates/next-app/config/Identifier/pages/page.about.ts +0 -20
  34. package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +0 -27
  35. package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +0 -266
  36. package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +0 -293
  37. package/dist/templates/next-app/config/Identifier/pages/page.home.ts +0 -56
  38. package/dist/templates/next-app/config/Identifier/pages/page.login.ts +0 -159
  39. package/dist/templates/next-app/config/Identifier/pages/page.register.ts +0 -177
  40. package/dist/templates/next-app/config/adminNavs.ts +0 -19
  41. package/dist/templates/next-app/config/common.ts +0 -43
  42. package/dist/templates/next-app/config/cookies.ts +0 -23
  43. package/dist/templates/next-app/config/i18n/AboutI18n.ts +0 -14
  44. package/dist/templates/next-app/config/i18n/HomeI18n.ts +0 -24
  45. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +0 -51
  46. package/dist/templates/next-app/config/i18n/admin18n.ts +0 -75
  47. package/dist/templates/next-app/config/i18n/i18nConfig.ts +0 -16
  48. package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +0 -36
  49. package/dist/templates/next-app/config/i18n/index.ts +0 -7
  50. package/dist/templates/next-app/config/i18n/loginI18n.ts +0 -50
  51. package/dist/templates/next-app/config/i18n/register18n.ts +0 -44
  52. package/dist/templates/next-app/config/route.ts +0 -9
  53. package/dist/templates/next-app/config/theme.ts +0 -28
  54. package/dist/templates/next-app/docs/en/api.md +0 -387
  55. package/dist/templates/next-app/docs/en/component.md +0 -544
  56. package/dist/templates/next-app/docs/en/database.md +0 -496
  57. package/dist/templates/next-app/docs/en/development-guide.md +0 -727
  58. package/dist/templates/next-app/docs/en/env.md +0 -563
  59. package/dist/templates/next-app/docs/en/i18n.md +0 -287
  60. package/dist/templates/next-app/docs/en/index.md +0 -165
  61. package/dist/templates/next-app/docs/en/page.md +0 -457
  62. package/dist/templates/next-app/docs/en/project-structure.md +0 -176
  63. package/dist/templates/next-app/docs/en/router.md +0 -427
  64. package/dist/templates/next-app/docs/en/theme.md +0 -532
  65. package/dist/templates/next-app/docs/en/validator.md +0 -478
  66. package/dist/templates/next-app/docs/zh/api.md +0 -387
  67. package/dist/templates/next-app/docs/zh/component.md +0 -544
  68. package/dist/templates/next-app/docs/zh/database.md +0 -496
  69. package/dist/templates/next-app/docs/zh/development-guide.md +0 -727
  70. package/dist/templates/next-app/docs/zh/env.md +0 -563
  71. package/dist/templates/next-app/docs/zh/i18n.md +0 -287
  72. package/dist/templates/next-app/docs/zh/index.md +0 -165
  73. package/dist/templates/next-app/docs/zh/page.md +0 -457
  74. package/dist/templates/next-app/docs/zh/project-structure.md +0 -176
  75. package/dist/templates/next-app/docs/zh/router.md +0 -427
  76. package/dist/templates/next-app/docs/zh/theme.md +0 -532
  77. package/dist/templates/next-app/docs/zh/validator.md +0 -476
  78. package/dist/templates/next-app/eslint.config.mjs +0 -285
  79. package/dist/templates/next-app/make/generateLocales.ts +0 -32
  80. package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +0 -15
  81. package/dist/templates/next-app/migrations/schema/UserSchema.ts +0 -38
  82. package/dist/templates/next-app/migrations/sql/1694244000000.sql +0 -21
  83. package/dist/templates/next-app/next.config.ts +0 -25
  84. package/dist/templates/next-app/package.json +0 -87
  85. package/dist/templates/next-app/postcss.config.mjs +0 -5
  86. package/dist/templates/next-app/public/favicon.ico +0 -0
  87. package/dist/templates/next-app/public/file.svg +0 -1
  88. package/dist/templates/next-app/public/globe.svg +0 -1
  89. package/dist/templates/next-app/public/locales/en.json +0 -182
  90. package/dist/templates/next-app/public/locales/zh.json +0 -182
  91. package/dist/templates/next-app/public/next.svg +0 -1
  92. package/dist/templates/next-app/public/vercel.svg +0 -1
  93. package/dist/templates/next-app/public/window.svg +0 -1
  94. package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +0 -37
  95. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +0 -42
  96. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +0 -153
  97. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +0 -20
  98. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +0 -67
  99. package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +0 -18
  100. package/dist/templates/next-app/src/app/[locale]/auth/login/LoginForm.tsx +0 -126
  101. package/dist/templates/next-app/src/app/[locale]/auth/login/page.tsx +0 -90
  102. package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +0 -8
  103. package/dist/templates/next-app/src/app/[locale]/auth/register/RegisterForm.tsx +0 -197
  104. package/dist/templates/next-app/src/app/[locale]/auth/register/page.tsx +0 -90
  105. package/dist/templates/next-app/src/app/[locale]/layout.tsx +0 -63
  106. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +0 -24
  107. package/dist/templates/next-app/src/app/[locale]/page.tsx +0 -98
  108. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +0 -13
  109. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +0 -13
  110. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +0 -20
  111. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +0 -13
  112. package/dist/templates/next-app/src/app/api/admin/users/route.ts +0 -20
  113. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +0 -32
  114. package/dist/templates/next-app/src/app/api/auth/callback/route.ts +0 -11
  115. package/dist/templates/next-app/src/app/api/callback/route.ts +0 -49
  116. package/dist/templates/next-app/src/app/api/locales/json/route.ts +0 -33
  117. package/dist/templates/next-app/src/app/api/user/login/route.ts +0 -10
  118. package/dist/templates/next-app/src/app/api/user/logout/route.ts +0 -8
  119. package/dist/templates/next-app/src/app/api/user/register/route.ts +0 -11
  120. package/dist/templates/next-app/src/app/manifest.ts +0 -16
  121. package/dist/templates/next-app/src/app/robots.txt +0 -2
  122. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +0 -28
  123. package/dist/templates/next-app/src/base/cases/AppConfig.ts +0 -40
  124. package/dist/templates/next-app/src/base/cases/Datetime.ts +0 -18
  125. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +0 -57
  126. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -116
  127. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +0 -45
  128. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +0 -32
  129. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +0 -77
  130. package/dist/templates/next-app/src/base/cases/ResourceState.ts +0 -17
  131. package/dist/templates/next-app/src/base/cases/RouterService.ts +0 -52
  132. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -73
  133. package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +0 -53
  134. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -212
  135. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -26
  136. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +0 -36
  137. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +0 -27
  138. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -25
  139. package/dist/templates/next-app/src/base/port/IOCInterface.ts +0 -33
  140. package/dist/templates/next-app/src/base/port/RouterInterface.ts +0 -11
  141. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +0 -25
  142. package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +0 -8
  143. package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +0 -26
  144. package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +0 -42
  145. package/dist/templates/next-app/src/base/services/AppApiRequester.ts +0 -67
  146. package/dist/templates/next-app/src/base/services/AppUserGateway.ts +0 -110
  147. package/dist/templates/next-app/src/base/services/I18nService.ts +0 -87
  148. package/dist/templates/next-app/src/base/services/ResourceService.ts +0 -139
  149. package/dist/templates/next-app/src/base/services/UserService.ts +0 -68
  150. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +0 -106
  151. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +0 -87
  152. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +0 -110
  153. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +0 -52
  154. package/dist/templates/next-app/src/base/types/AppPageRouter.ts +0 -12
  155. package/dist/templates/next-app/src/base/types/PagesRouter.ts +0 -9
  156. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +0 -76
  157. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +0 -125
  158. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -50
  159. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  160. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +0 -18
  161. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +0 -68
  162. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -100
  163. package/dist/templates/next-app/src/core/globals.ts +0 -28
  164. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +0 -80
  165. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +0 -66
  166. package/dist/templates/next-app/src/i18n/loadMessages.ts +0 -103
  167. package/dist/templates/next-app/src/i18n/request.ts +0 -31
  168. package/dist/templates/next-app/src/i18n/routing.ts +0 -35
  169. package/dist/templates/next-app/src/lib/supabase/client.ts +0 -8
  170. package/dist/templates/next-app/src/lib/supabase/conts.ts +0 -2
  171. package/dist/templates/next-app/src/lib/supabase/proxy.ts +0 -84
  172. package/dist/templates/next-app/src/lib/supabase/server.ts +0 -38
  173. package/dist/templates/next-app/src/pages/[locale]/about.tsx +0 -61
  174. package/dist/templates/next-app/src/pages/_app.tsx +0 -50
  175. package/dist/templates/next-app/src/pages/_document.tsx +0 -13
  176. package/dist/templates/next-app/src/proxy.ts +0 -33
  177. package/dist/templates/next-app/src/server/AppErrorApi.ts +0 -10
  178. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +0 -110
  179. package/dist/templates/next-app/src/server/AppSuccessApi.ts +0 -7
  180. package/dist/templates/next-app/src/server/NextApiServer.ts +0 -61
  181. package/dist/templates/next-app/src/server/PagesRouteParams.ts +0 -145
  182. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +0 -18
  183. package/dist/templates/next-app/src/server/ServerAuth.ts +0 -81
  184. package/dist/templates/next-app/src/server/SupabaseBridge.ts +0 -262
  185. package/dist/templates/next-app/src/server/UserCredentialToken.ts +0 -53
  186. package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +0 -86
  187. package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +0 -42
  188. package/dist/templates/next-app/src/server/controllers/LocalesController.ts +0 -36
  189. package/dist/templates/next-app/src/server/controllers/UserController.ts +0 -91
  190. package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +0 -8
  191. package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +0 -21
  192. package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +0 -11
  193. package/dist/templates/next-app/src/server/port/CrentialTokenInterface.ts +0 -5
  194. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +0 -36
  195. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +0 -12
  196. package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +0 -10
  197. package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +0 -43
  198. package/dist/templates/next-app/src/server/port/PaginationInterface.ts +0 -6
  199. package/dist/templates/next-app/src/server/port/RouteParamsnHandlerInterface.ts +0 -18
  200. package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +0 -15
  201. package/dist/templates/next-app/src/server/port/ServerInterface.ts +0 -23
  202. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +0 -15
  203. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +0 -14
  204. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +0 -23
  205. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -216
  206. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -102
  207. package/dist/templates/next-app/src/server/services/AIService.ts +0 -45
  208. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +0 -21
  209. package/dist/templates/next-app/src/server/services/AdminLocalesService.ts +0 -20
  210. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +0 -137
  211. package/dist/templates/next-app/src/server/services/ApiUserService.ts +0 -29
  212. package/dist/templates/next-app/src/server/services/UserService.ts +0 -134
  213. package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +0 -6
  214. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -145
  215. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -82
  216. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +0 -70
  217. package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +0 -68
  218. package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  219. package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  220. package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +0 -3
  221. package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  222. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +0 -4
  223. package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  224. package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  225. package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +0 -3
  226. package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  227. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +0 -34
  228. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  229. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  230. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  231. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  232. package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +0 -44
  233. package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +0 -43
  234. package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +0 -3
  235. package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +0 -43
  236. package/dist/templates/next-app/src/styles/css/index.css +0 -6
  237. package/dist/templates/next-app/src/styles/css/page.css +0 -20
  238. package/dist/templates/next-app/src/styles/css/scrollbar.css +0 -34
  239. package/dist/templates/next-app/src/styles/css/tailwind.css +0 -5
  240. package/dist/templates/next-app/src/styles/css/themes/_default.css +0 -30
  241. package/dist/templates/next-app/src/styles/css/themes/dark.css +0 -30
  242. package/dist/templates/next-app/src/styles/css/themes/index.css +0 -3
  243. package/dist/templates/next-app/src/styles/css/themes/pink.css +0 -30
  244. package/dist/templates/next-app/src/styles/css/zIndex.css +0 -9
  245. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +0 -150
  246. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +0 -39
  247. package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +0 -64
  248. package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +0 -36
  249. package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +0 -42
  250. package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +0 -118
  251. package/dist/templates/next-app/src/uikit/components/FeatureItem.tsx +0 -13
  252. package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +0 -34
  253. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +0 -50
  254. package/dist/templates/next-app/src/uikit/components/With.tsx +0 -17
  255. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +0 -62
  256. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -28
  257. package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +0 -6
  258. package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +0 -29
  259. package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +0 -17
  260. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +0 -105
  261. package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +0 -20
  262. package/dist/templates/next-app/src/uikit/components-app/LanguageSwitcher.tsx +0 -75
  263. package/dist/templates/next-app/src/uikit/components-app/LogoutButton.tsx +0 -45
  264. package/dist/templates/next-app/src/uikit/components-app/ThemeSwitcher.tsx +0 -144
  265. package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +0 -98
  266. package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +0 -93
  267. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +0 -25
  268. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +0 -22
  269. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +0 -37
  270. package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +0 -32
  271. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +0 -11
  272. package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +0 -8
  273. package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +0 -42
  274. package/dist/templates/next-app/tailwind.config.ts +0 -8
  275. package/dist/templates/next-app/tsconfig.json +0 -39
  276. package/dist/templates/node-lib/__tests__/readJson.test.ts +0 -26
  277. package/dist/templates/node-lib/bin/test.js +0 -30
  278. package/dist/templates/node-lib/package.json +0 -66
  279. package/dist/templates/node-lib/rollup.config.js +0 -79
  280. package/dist/templates/node-lib/src/index.ts +0 -7
  281. package/dist/templates/node-lib/src/readJson.ts +0 -12
  282. package/dist/templates/node-lib/tsconfig.json +0 -23
  283. package/dist/templates/pack-app/README.md +0 -108
  284. package/dist/templates/pack-app/eslint.config.js +0 -97
  285. package/dist/templates/pack-app/fe-config.json +0 -35
  286. package/dist/templates/pack-app/package.json +0 -86
  287. package/dist/templates/pack-app/pnpm-workspace.yaml +0 -2
  288. package/dist/templates/pack-app/tsconfig.json +0 -9
  289. package/dist/templates/pack-app/tsconfig.test.json +0 -10
  290. package/dist/templates/pack-app/vite.config.ts +0 -14
  291. package/dist/templates/react-app/.env.template +0 -22
  292. package/dist/templates/react-app/.prettierignore +0 -17
  293. package/dist/templates/react-app/README.en.md +0 -274
  294. package/dist/templates/react-app/README.md +0 -273
  295. package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +0 -16
  296. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfig.ts +0 -48
  297. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +0 -17
  298. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +0 -14
  299. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +0 -38
  300. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +0 -53
  301. package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +0 -46
  302. package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +0 -12
  303. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +0 -96
  304. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +0 -110
  305. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +0 -55
  306. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +0 -74
  307. package/dist/templates/react-app/__tests__/setup/index.ts +0 -1
  308. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +0 -64
  309. package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +0 -115
  310. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +0 -288
  311. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +0 -226
  312. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +0 -178
  313. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +0 -181
  314. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +0 -61
  315. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +0 -177
  316. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +0 -191
  317. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +0 -245
  318. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +0 -240
  319. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +0 -242
  320. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +0 -135
  321. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +0 -74
  322. package/dist/templates/react-app/__tests__/src/main.test.tsx +0 -46
  323. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +0 -97
  324. package/dist/templates/react-app/__tests__/src/uikit/components/chatMessage/ChatRoot.test.tsx +0 -274
  325. package/dist/templates/react-app/config/IOCIdentifier.ts +0 -91
  326. package/dist/templates/react-app/config/Identifier/common/common.error.ts +0 -34
  327. package/dist/templates/react-app/config/Identifier/common/common.ts +0 -62
  328. package/dist/templates/react-app/config/Identifier/common/index.ts +0 -2
  329. package/dist/templates/react-app/config/Identifier/components/component.chatMessage.ts +0 -56
  330. package/dist/templates/react-app/config/Identifier/components/component.messageBaseList.ts +0 -103
  331. package/dist/templates/react-app/config/Identifier/index.ts +0 -2
  332. package/dist/templates/react-app/config/Identifier/pages/index.ts +0 -9
  333. package/dist/templates/react-app/config/Identifier/pages/page.about.ts +0 -189
  334. package/dist/templates/react-app/config/Identifier/pages/page.executor.ts +0 -275
  335. package/dist/templates/react-app/config/Identifier/pages/page.home.ts +0 -71
  336. package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +0 -102
  337. package/dist/templates/react-app/config/Identifier/pages/page.jsonStorage.ts +0 -77
  338. package/dist/templates/react-app/config/Identifier/pages/page.login.ts +0 -162
  339. package/dist/templates/react-app/config/Identifier/pages/page.message.ts +0 -20
  340. package/dist/templates/react-app/config/Identifier/pages/page.register.ts +0 -159
  341. package/dist/templates/react-app/config/Identifier/pages/page.request.ts +0 -169
  342. package/dist/templates/react-app/config/app.router.ts +0 -338
  343. package/dist/templates/react-app/config/common.ts +0 -85
  344. package/dist/templates/react-app/config/feapi.mock.json +0 -34
  345. package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +0 -53
  346. package/dist/templates/react-app/config/i18n/aboutI18n.ts +0 -42
  347. package/dist/templates/react-app/config/i18n/chatMessageI18n.ts +0 -17
  348. package/dist/templates/react-app/config/i18n/executorI18n.ts +0 -51
  349. package/dist/templates/react-app/config/i18n/homeI18n.ts +0 -24
  350. package/dist/templates/react-app/config/i18n/i18nConfig.ts +0 -30
  351. package/dist/templates/react-app/config/i18n/identifiter18n.ts +0 -30
  352. package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +0 -27
  353. package/dist/templates/react-app/config/i18n/login18n.ts +0 -42
  354. package/dist/templates/react-app/config/i18n/messageBaseListI18n.ts +0 -22
  355. package/dist/templates/react-app/config/i18n/messageI18n.ts +0 -14
  356. package/dist/templates/react-app/config/i18n/notFoundI18n.ts +0 -34
  357. package/dist/templates/react-app/config/i18n/register18n.ts +0 -40
  358. package/dist/templates/react-app/config/i18n/request18n.ts +0 -41
  359. package/dist/templates/react-app/config/theme.ts +0 -21
  360. package/dist/templates/react-app/docs/en/bootstrap.md +0 -1891
  361. package/dist/templates/react-app/docs/en/components/chat-message-component.md +0 -320
  362. package/dist/templates/react-app/docs/en/components/chat-message-refactor.md +0 -283
  363. package/dist/templates/react-app/docs/en/components/message-base-list-component.md +0 -171
  364. package/dist/templates/react-app/docs/en/development-guide.md +0 -1199
  365. package/dist/templates/react-app/docs/en/env.md +0 -1336
  366. package/dist/templates/react-app/docs/en/global.md +0 -509
  367. package/dist/templates/react-app/docs/en/i18n.md +0 -979
  368. package/dist/templates/react-app/docs/en/index.md +0 -802
  369. package/dist/templates/react-app/docs/en/ioc.md +0 -1365
  370. package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +0 -321
  371. package/dist/templates/react-app/docs/en/playwright/index.md +0 -19
  372. package/dist/templates/react-app/docs/en/playwright/installation-summary.md +0 -332
  373. package/dist/templates/react-app/docs/en/playwright/overview.md +0 -222
  374. package/dist/templates/react-app/docs/en/playwright/quickstart.md +0 -325
  375. package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +0 -340
  376. package/dist/templates/react-app/docs/en/playwright/setup-complete.md +0 -290
  377. package/dist/templates/react-app/docs/en/playwright/testing-guide.md +0 -565
  378. package/dist/templates/react-app/docs/en/request.md +0 -423
  379. package/dist/templates/react-app/docs/en/router.md +0 -404
  380. package/dist/templates/react-app/docs/en/store.md +0 -1331
  381. package/dist/templates/react-app/docs/en/test-guide.md +0 -976
  382. package/dist/templates/react-app/docs/en/theme.md +0 -424
  383. package/dist/templates/react-app/docs/en/typescript-guide.md +0 -473
  384. package/dist/templates/react-app/docs/en/why-no-globals.md +0 -797
  385. package/dist/templates/react-app/docs/zh/bootstrap.md +0 -1891
  386. package/dist/templates/react-app/docs/zh/components/chat-message-component.md +0 -320
  387. package/dist/templates/react-app/docs/zh/components/chat-message-refactor.md +0 -283
  388. package/dist/templates/react-app/docs/zh/components/message-base-list-component.md +0 -171
  389. package/dist/templates/react-app/docs/zh/development-guide.md +0 -1199
  390. package/dist/templates/react-app/docs/zh/env.md +0 -1336
  391. package/dist/templates/react-app/docs/zh/global.md +0 -511
  392. package/dist/templates/react-app/docs/zh/i18n.md +0 -979
  393. package/dist/templates/react-app/docs/zh/index.md +0 -786
  394. package/dist/templates/react-app/docs/zh/ioc.md +0 -1364
  395. package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +0 -321
  396. package/dist/templates/react-app/docs/zh/playwright/index.md +0 -19
  397. package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +0 -332
  398. package/dist/templates/react-app/docs/zh/playwright/overview.md +0 -222
  399. package/dist/templates/react-app/docs/zh/playwright/quickstart.md +0 -325
  400. package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +0 -340
  401. package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +0 -290
  402. package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +0 -565
  403. package/dist/templates/react-app/docs/zh/request.md +0 -427
  404. package/dist/templates/react-app/docs/zh/router.md +0 -408
  405. package/dist/templates/react-app/docs/zh/store.md +0 -1329
  406. package/dist/templates/react-app/docs/zh/test-guide.md +0 -976
  407. package/dist/templates/react-app/docs/zh/theme.md +0 -424
  408. package/dist/templates/react-app/docs/zh/typescript-guide.md +0 -473
  409. package/dist/templates/react-app/docs/zh/why-no-globals.md +0 -797
  410. package/dist/templates/react-app/e2e/App.spec.ts +0 -319
  411. package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +0 -40
  412. package/dist/templates/react-app/e2e/main.spec.ts +0 -20
  413. package/dist/templates/react-app/e2e/utils/test-helpers.ts +0 -19
  414. package/dist/templates/react-app/eslint.config.mjs +0 -325
  415. package/dist/templates/react-app/index.html +0 -13
  416. package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +0 -26
  417. package/dist/templates/react-app/package.json +0 -125
  418. package/dist/templates/react-app/playwright.config.ts +0 -79
  419. package/dist/templates/react-app/postcss.config.js +0 -5
  420. package/dist/templates/react-app/public/locales/en/common.json +0 -235
  421. package/dist/templates/react-app/public/locales/zh/common.json +0 -235
  422. package/dist/templates/react-app/public/logo.svg +0 -1
  423. package/dist/templates/react-app/public/router-root/logo.svg +0 -1
  424. package/dist/templates/react-app/src/App.tsx +0 -35
  425. package/dist/templates/react-app/src/assets/react.svg +0 -1
  426. package/dist/templates/react-app/src/base/apis/AiApi.ts +0 -68
  427. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +0 -29
  428. package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +0 -14
  429. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +0 -86
  430. package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +0 -21
  431. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +0 -142
  432. package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +0 -14
  433. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +0 -86
  434. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +0 -70
  435. package/dist/templates/react-app/src/base/cases/AppConfig.ts +0 -123
  436. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +0 -115
  437. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +0 -64
  438. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +0 -45
  439. package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +0 -23
  440. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +0 -55
  441. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +0 -80
  442. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +0 -40
  443. package/dist/templates/react-app/src/base/cases/ResourceState.ts +0 -23
  444. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +0 -173
  445. package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +0 -26
  446. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +0 -23
  447. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +0 -10
  448. package/dist/templates/react-app/src/base/port/IOCInterface.ts +0 -38
  449. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +0 -21
  450. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +0 -23
  451. package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +0 -5
  452. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +0 -29
  453. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +0 -20
  454. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +0 -61
  455. package/dist/templates/react-app/src/base/services/I18nService.ts +0 -146
  456. package/dist/templates/react-app/src/base/services/IdentifierService.ts +0 -162
  457. package/dist/templates/react-app/src/base/services/RouteService.ts +0 -115
  458. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +0 -45
  459. package/dist/templates/react-app/src/base/services/UserService.ts +0 -88
  460. package/dist/templates/react-app/src/base/types/Page.ts +0 -47
  461. package/dist/templates/react-app/src/base/types/deprecated-antd.d.ts +0 -60
  462. package/dist/templates/react-app/src/base/types/global.d.ts +0 -8
  463. package/dist/templates/react-app/src/core/IOC.ts +0 -28
  464. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +0 -108
  465. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -54
  466. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  467. package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +0 -14
  468. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +0 -28
  469. package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +0 -47
  470. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +0 -142
  471. package/dist/templates/react-app/src/core/globals.ts +0 -47
  472. package/dist/templates/react-app/src/main.tsx +0 -19
  473. package/dist/templates/react-app/src/pages/404.tsx +0 -19
  474. package/dist/templates/react-app/src/pages/500.tsx +0 -18
  475. package/dist/templates/react-app/src/pages/NoRouteFound.tsx +0 -5
  476. package/dist/templates/react-app/src/pages/auth/Layout.tsx +0 -27
  477. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +0 -166
  478. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +0 -226
  479. package/dist/templates/react-app/src/pages/base/AboutPage.tsx +0 -123
  480. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +0 -467
  481. package/dist/templates/react-app/src/pages/base/HomePage.tsx +0 -81
  482. package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +0 -117
  483. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +0 -132
  484. package/dist/templates/react-app/src/pages/base/Layout.tsx +0 -20
  485. package/dist/templates/react-app/src/pages/base/MessagePage.tsx +0 -71
  486. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +0 -18
  487. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +0 -193
  488. package/dist/templates/react-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  489. package/dist/templates/react-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  490. package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +0 -3
  491. package/dist/templates/react-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  492. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +0 -4
  493. package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  494. package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  495. package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +0 -3
  496. package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  497. package/dist/templates/react-app/src/styles/css/antd-themes/no-context.css +0 -34
  498. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  499. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  500. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  501. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  502. package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +0 -44
  503. package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +0 -43
  504. package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +0 -3
  505. package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +0 -43
  506. package/dist/templates/react-app/src/styles/css/index.css +0 -6
  507. package/dist/templates/react-app/src/styles/css/page.css +0 -20
  508. package/dist/templates/react-app/src/styles/css/scrollbar.css +0 -34
  509. package/dist/templates/react-app/src/styles/css/tailwind.css +0 -5
  510. package/dist/templates/react-app/src/styles/css/themes/_default.css +0 -30
  511. package/dist/templates/react-app/src/styles/css/themes/dark.css +0 -30
  512. package/dist/templates/react-app/src/styles/css/themes/index.css +0 -3
  513. package/dist/templates/react-app/src/styles/css/themes/pink.css +0 -30
  514. package/dist/templates/react-app/src/styles/css/zIndex.css +0 -9
  515. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +0 -72
  516. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +0 -41
  517. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +0 -22
  518. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +0 -136
  519. package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +0 -35
  520. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +0 -51
  521. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +0 -44
  522. package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +0 -21
  523. package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +0 -18
  524. package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +0 -11
  525. package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +0 -62
  526. package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +0 -38
  527. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +0 -78
  528. package/dist/templates/react-app/src/uikit/components/Loading.tsx +0 -49
  529. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +0 -43
  530. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +0 -58
  531. package/dist/templates/react-app/src/uikit/components/MessageBaseList.tsx +0 -258
  532. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +0 -19
  533. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +0 -137
  534. package/dist/templates/react-app/src/uikit/components/With.tsx +0 -17
  535. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatMessageBridge.ts +0 -205
  536. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatRoot.tsx +0 -21
  537. package/dist/templates/react-app/src/uikit/components/chatMessage/FocusBar.tsx +0 -108
  538. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageApi.ts +0 -282
  539. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageItem.tsx +0 -102
  540. package/dist/templates/react-app/src/uikit/components/chatMessage/MessagesList.tsx +0 -86
  541. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +0 -42
  542. package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +0 -13
  543. package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +0 -26
  544. package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +0 -25
  545. package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +0 -35
  546. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +0 -21
  547. package/dist/templates/react-app/src/uikit/hooks/useRouterI18nGuard.ts +0 -25
  548. package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -31
  549. package/dist/templates/react-app/src/vite-env.d.ts +0 -1
  550. package/dist/templates/react-app/tailwind.config.js +0 -4
  551. package/dist/templates/react-app/tsconfig.app.json +0 -36
  552. package/dist/templates/react-app/tsconfig.e2e.json +0 -24
  553. package/dist/templates/react-app/tsconfig.json +0 -22
  554. package/dist/templates/react-app/tsconfig.node.json +0 -27
  555. package/dist/templates/react-app/tsconfig.test.json +0 -18
  556. package/dist/templates/react-app/vite.config.ts +0 -144
@@ -1,1891 +0,0 @@
1
- # Bootstrap 启动器
2
-
3
- ## 📋 目录
4
-
5
- - [什么是 Bootstrap](#-什么是-bootstrap)
6
- - [为什么需要 Bootstrap](#-为什么需要-bootstrap)
7
- - [核心概念](#-核心概念)
8
- - [工作流程](#-工作流程)
9
- - [项目中的实现](#-项目中的实现)
10
- - [插件系统](#-插件系统)
11
- - [实战示例](#-实战示例)
12
- - [测试:Bootstrap 的核心优势](#-测试bootstrap-的核心优势)
13
- - [最佳实践](#-最佳实践)
14
- - [常见问题](#-常见问题)
15
-
16
- ---
17
-
18
- ## 🎯 什么是 Bootstrap
19
-
20
- Bootstrap(启动器)是应用程序的**初始化管理器**,负责在应用渲染前执行所有必要的初始化逻辑。
21
-
22
- ### 核心职责
23
-
24
- ```
25
- ┌──────────────────────────────────────────────────┐
26
- │ Bootstrap 启动器 │
27
- │ ┌────────────────────────────────────────────┐ │
28
- │ │ 1. 创建 IOC 容器 │ │
29
- │ │ 2. 注入环境变量 │ │
30
- │ │ 3. 封装全局变量 │ │
31
- │ │ 4. 注册业务插件 │ │
32
- │ │ 5. 执行初始化逻辑 │ │
33
- │ └────────────────────────────────────────────┘ │
34
- └──────────────────────────────────────────────────┘
35
-
36
- 应用开始渲染
37
- ```
38
-
39
- ### 类比理解
40
-
41
- 就像电脑开机时需要:
42
-
43
- - ✅ 加载驱动程序
44
- - ✅ 启动系统服务
45
- - ✅ 检查硬件状态
46
- - ✅ 初始化用户环境
47
-
48
- Bootstrap 在应用启动时做类似的事情:
49
-
50
- - ✅ 初始化 IOC 容器(依赖管理)
51
- - ✅ 注入环境配置
52
- - ✅ 封装浏览器 API
53
- - ✅ 执行业务初始化(用户认证、API 配置等)
54
-
55
- ---
56
-
57
- ## 🤔 为什么需要 Bootstrap
58
-
59
- ### 问题:传统方式的痛点
60
-
61
- #### 示例 1:组件中混杂初始化逻辑
62
-
63
- ```typescript
64
- // ❌ 传统方式:在组件中处理初始化
65
- function App() {
66
- const [loading, setLoading] = useState(true);
67
- const [user, setUser] = useState(null);
68
- const [error, setError] = useState(null);
69
-
70
- useEffect(() => {
71
- // 初始化逻辑混在组件中
72
- fetchUserInfo()
73
- .then(user => {
74
- setUser(user);
75
- // 还要检查权限
76
- if (!user.hasPermission) {
77
- window.location.href = '/login';
78
- }
79
- })
80
- .catch(error => {
81
- setError(error);
82
- })
83
- .finally(() => {
84
- setLoading(false);
85
- });
86
- }, []);
87
-
88
- if (loading) return <div>Loading...</div>;
89
- if (error) return <div>Error: {error.message}</div>;
90
-
91
- return <Router />;
92
- }
93
- ```
94
-
95
- **问题:**
96
-
97
- - 😰 **组件职责过重**:UI 组件不应该处理业务初始化
98
- - 😰 **状态管理复杂**:需要管理多个状态(loading、user、error)
99
- - 😰 **难以测试**:初始化逻辑和 UI 逻辑耦合
100
- - 😰 **难以复用**:初始化逻辑无法在其他项目中复用
101
- - 😰 **维护困难**:业务逻辑变化会影响组件结构
102
-
103
- #### 示例 2:多条件初始化
104
-
105
- ```typescript
106
- // ❌ 更复杂的场景:多个初始化步骤
107
- function App() {
108
- const [loading, setLoading] = useState(true);
109
- const [userInfo, setUserInfo] = useState(null);
110
- const [permissions, setPermissions] = useState([]);
111
- const [i18nLoaded, setI18nLoaded] = useState(false);
112
- const [apiConfigured, setApiConfigured] = useState(false);
113
- const location = useLocation();
114
-
115
- useEffect(() => {
116
- const init = async () => {
117
- try {
118
- // 步骤 1:配置 API
119
- await configureAPI();
120
- setApiConfigured(true);
121
-
122
- // 步骤 2:加载国际化
123
- await loadI18n();
124
- setI18nLoaded(true);
125
-
126
- // 步骤 3:检查用户认证
127
- if (location.pathname !== '/login') {
128
- const user = await fetchUserInfo();
129
- setUserInfo(user);
130
-
131
- // 步骤 4:加载权限
132
- const perms = await fetchPermissions(user.id);
133
- setPermissions(perms);
134
-
135
- // 步骤 5:权限检查
136
- if (!hasRequiredPermission(perms, location.pathname)) {
137
- window.location.href = '/403';
138
- return;
139
- }
140
- }
141
- } catch (error) {
142
- console.error('Initialization failed:', error);
143
- window.location.href = '/error';
144
- } finally {
145
- setLoading(false);
146
- }
147
- };
148
-
149
- init();
150
- }, [location.pathname]);
151
-
152
- // 还要处理各种加载状态...
153
- if (loading || !apiConfigured || !i18nLoaded) {
154
- return <LoadingScreen />;
155
- }
156
-
157
- return <Router />;
158
- }
159
- ```
160
-
161
- **问题进一步恶化:**
162
-
163
- - 😰😰😰 **状态爆炸**:需要管理多个初始化状态
164
- - 😰😰😰 **难以扩展**:添加新的初始化步骤会让代码更复杂
165
- - 😰😰😰 **错误处理复杂**:每一步都可能失败,需要大量错误处理代码
166
- - 😰😰😰 **依赖关系隐式**:步骤之间的依赖关系不清晰
167
-
168
- ### 解决方案:使用 Bootstrap
169
-
170
- ```typescript
171
- // ✅ 使用 Bootstrap:组件变得简洁
172
- function App() {
173
- return (
174
- <BootstrapsProvider>
175
- <ComboProvider themeConfig={themeConfig}>
176
- <AppRouterProvider pages={allPages} />
177
- </ComboProvider>
178
- </BootstrapsProvider>
179
- );
180
- }
181
-
182
- // 所有初始化逻辑在 Bootstrap 中处理
183
- const bootstrap = new Bootstrap({
184
- root: window,
185
- logger,
186
- ioc: { manager: IOC, register: new IocRegisterImpl({ pathname, appConfig }) },
187
- envOptions: { /* 环境变量配置 */ },
188
- globalOptions: { /* 全局变量配置 */ }
189
- });
190
-
191
- // 注册初始化插件
192
- bootstrap.use([
193
- IOC(I18nService), // 国际化服务
194
- new UserApiBootstrap(), // 用户 API 配置
195
- new FeApiBootstrap(), // 业务 API 配置
196
- IOC(UserService) // 用户认证服务
197
- ]);
198
-
199
- // 启动应用
200
- await bootstrap.initialize();
201
- await bootstrap.start();
202
- ```
203
-
204
- **优势:**
205
-
206
- - ✅ **组件职责清晰**:UI 组件只负责渲染
207
- - ✅ **逻辑分离**:初始化逻辑独立于 UI
208
- - ✅ **易于测试**:可以独立测试每个初始化步骤
209
- - ✅ **易于扩展**:添加新的初始化步骤只需添加新插件
210
- - ✅ **易于复用**:同一套初始化逻辑可以在不同项目中使用
211
-
212
- ---
213
-
214
- ## 💡 核心概念
215
-
216
- ### 1. 插件化架构
217
-
218
- Bootstrap 采用插件化设计,每个插件负责一个特定的初始化任务。
219
-
220
- ```typescript
221
- // 插件接口
222
- export interface BootstrapExecutorPlugin {
223
- readonly pluginName: string;
224
-
225
- // 在初始化前执行
226
- onBefore?(context: BootstrapContext): void | Promise<void>;
227
-
228
- // 在初始化时执行
229
- onExecute?(context: BootstrapContext): void | Promise<void>;
230
-
231
- // 在初始化后执行
232
- onAfter?(context: BootstrapContext): void | Promise<void>;
233
-
234
- // 错误处理
235
- onError?(error: Error, context: BootstrapContext): void | Promise<void>;
236
- }
237
- ```
238
-
239
- ### 2. 生命周期
240
-
241
- ```
242
- ┌────────────────────────────────────────────────┐
243
- │ Bootstrap 生命周期 │
244
- │ │
245
- │ initialize() │
246
- │ ├── 创建 IOC 容器 │
247
- │ ├── 注入环境变量 │
248
- │ └── 封装全局变量 │
249
- │ │
250
- │ start() │
251
- │ ├── onBefore: 前置初始化 │
252
- │ │ ├── 配置 API │
253
- │ │ ├── 加载国际化 │
254
- │ │ └── 检查用户认证 │
255
- │ │ │
256
- │ ├── onExecute: 执行主逻辑 │
257
- │ │ └── 执行业务初始化 │
258
- │ │ │
259
- │ ├── onAfter: 后置处理 │
260
- │ │ └── 清理资源、记录日志 │
261
- │ │ │
262
- │ └── onError: 错误处理 │
263
- │ └── 错误捕获和处理 │
264
- └────────────────────────────────────────────────┘
265
- ```
266
-
267
- ### 3. 依赖注入
268
-
269
- Bootstrap 与 IOC 容器深度集成,所有插件都可以通过依赖注入获取服务。
270
-
271
- ```typescript
272
- @injectable()
273
- export class UserService implements ExecutorPlugin {
274
- readonly pluginName = 'UserService';
275
-
276
- constructor(
277
- @inject(UserApi) private api: UserApi,
278
- @inject(IOCIdentifier.AppConfig) private config: AppConfig,
279
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage
280
- ) {}
281
-
282
- async onBefore(): Promise<void> {
283
- // 使用注入的依赖执行初始化
284
- const token = this.storage.getItem('token');
285
- if (token) {
286
- await this.api.getUserInfo(token);
287
- }
288
- }
289
- }
290
- ```
291
-
292
- ---
293
-
294
- ## 🔄 工作流程
295
-
296
- ### 完整流程图
297
-
298
- ```
299
- ┌─────────────────────────────────────────────────────────────┐
300
- │ 1. main.tsx: 应用入口 │
301
- │ BootstrapClient.main({ root: window, bootHref, ioc }) │
302
- └────────────────────┬────────────────────────────────────────┘
303
-
304
- ┌─────────────────────────────────────────────────────────────┐
305
- │ 2. BootstrapClient: 创建 Bootstrap 实例 │
306
- │ - 创建 IOC 容器 │
307
- │ - 配置环境变量注入 │
308
- │ - 配置全局变量封装 │
309
- └────────────────────┬────────────────────────────────────────┘
310
-
311
- ┌─────────────────────────────────────────────────────────────┐
312
- │ 3. Bootstrap.initialize(): 初始化 │
313
- │ ✅ IOC 容器初始化 │
314
- │ ✅ 环境变量注入到 AppConfig │
315
- │ ✅ 全局变量封装(localStorage、window 等) │
316
- └────────────────────┬────────────────────────────────────────┘
317
-
318
- ┌─────────────────────────────────────────────────────────────┐
319
- │ 4. BootstrapsRegistry: 注册业务插件 │
320
- │ - I18nService: 国际化服务 │
321
- │ - UserApiBootstrap: 用户 API 配置 │
322
- │ - FeApiBootstrap: 业务 API 配置 │
323
- │ - UserService: 用户认证服务 │
324
- └────────────────────┬────────────────────────────────────────┘
325
-
326
- ┌─────────────────────────────────────────────────────────────┐
327
- │ 5. Bootstrap.start(): 启动 │
328
- │ ↓ │
329
- │ onBefore 阶段: │
330
- │ ├── I18nService.onBefore() → 加载翻译资源 │
331
- │ ├── UserApiBootstrap.onBefore() → 配置 API 插件 │
332
- │ ├── FeApiBootstrap.onBefore() → 配置业务 API │
333
- │ └── UserService.onBefore() → 检查用户认证 │
334
- │ ↓ │
335
- │ onExecute 阶段: │
336
- │ └── 执行插件主逻辑 │
337
- │ ↓ │
338
- │ onAfter 阶段: │
339
- │ └── 清理和日志记录 │
340
- └────────────────────┬────────────────────────────────────────┘
341
-
342
- ┌─────────────────────────────────────────────────────────────┐
343
- │ 6. React 渲染 │
344
- │ ReactDOM.render(<App />) │
345
- └─────────────────────────────────────────────────────────────┘
346
- ```
347
-
348
- ---
349
-
350
- ## 🛠️ 项目中的实现
351
-
352
- ### 文件结构
353
-
354
- ```
355
- src/
356
- ├── main.tsx # 应用入口
357
- ├── core/
358
- │ ├── bootstraps/
359
- │ │ ├── BootstrapClient.ts # Bootstrap 启动器
360
- │ │ ├── BootstrapsRegistry.ts # 插件注册器
361
- │ │ ├── PrintBootstrap.ts # 打印日志插件
362
- │ │ └── IocIdentifierTest.ts # IOC 测试插件
363
- │ ├── globals.ts # 全局变量封装
364
- │ └── clientIoc/
365
- │ ├── ClientIOC.ts # IOC 容器
366
- │ └── ClientIOCRegister.ts # IOC 注册器
367
- ├── base/
368
- │ ├── services/
369
- │ │ ├── UserService.ts # 用户服务(插件)
370
- │ │ └── I18nService.ts # 国际化服务(插件)
371
- │ └── apis/
372
- │ ├── userApi/
373
- │ │ └── UserApiBootstrap.ts # 用户 API 配置插件
374
- │ └── feApi/
375
- │ └── FeApiBootstrap.ts # 业务 API 配置插件
376
- └── uikit/
377
- └── components/
378
- └── BootstrapsProvider.tsx # Bootstrap Provider
379
- ```
380
-
381
- ### 1. 入口文件:main.tsx
382
-
383
- ```typescript
384
- // src/main.tsx
385
- import 'reflect-metadata';
386
- import { StrictMode } from 'react';
387
- import { createRoot } from 'react-dom/client';
388
- import App from './App.tsx';
389
- import { BootstrapClient } from './core/bootstraps/BootstrapClient';
390
- import { clientIOC } from './core/clientIoc/ClientIOC.ts';
391
-
392
- // 🚀 启动 Bootstrap
393
- BootstrapClient.main({
394
- root: window, // 注入浏览器环境
395
- bootHref: window.location.href, // 注入启动 URL
396
- ioc: clientIOC // 注入 IOC 容器
397
- });
398
-
399
- // 渲染 React 应用
400
- createRoot(document.getElementById('root')!).render(
401
- <StrictMode>
402
- <App />
403
- </StrictMode>
404
- );
405
- ```
406
-
407
- ### 2. Bootstrap 启动器:BootstrapClient.ts
408
-
409
- ```typescript
410
- // src/core/bootstraps/BootstrapClient.ts
411
- import { Bootstrap } from '@qlover/corekit-bridge';
412
- import { envBlackList, envPrefix, browserGlobalsName } from '@config/common';
413
- import * as globals from '../globals';
414
- import { BootstrapsRegistry } from './BootstrapsRegistry';
415
-
416
- export class BootstrapClient {
417
- static async main(args: BootstrapClientArgs): Promise<BootstrapClientArgs> {
418
- const { root, bootHref, ioc, iocRegister } = args;
419
- const { logger, appConfig } = globals;
420
-
421
- // 1️⃣ 创建 IOC 容器
422
- const IOC = ioc.create({
423
- pathname: bootHref,
424
- appConfig: appConfig
425
- });
426
-
427
- // 2️⃣ 创建 Bootstrap 实例
428
- const bootstrap = new Bootstrap({
429
- root,
430
- logger,
431
- // IOC 容器配置
432
- ioc: {
433
- manager: IOC,
434
- register: iocRegister
435
- },
436
- // 环境变量注入配置
437
- envOptions: {
438
- target: appConfig, // 注入到 AppConfig
439
- source: Object.assign({}, import.meta.env, {
440
- [envPrefix + 'BOOT_HREF']: bootHref // 添加启动 URL
441
- }),
442
- prefix: envPrefix, // 环境变量前缀
443
- blackList: envBlackList // 黑名单
444
- },
445
- // 全局变量封装配置
446
- globalOptions: {
447
- sources: globals, // 封装的全局变量
448
- target: browserGlobalsName // 挂载目标
449
- }
450
- });
451
-
452
- try {
453
- logger.info('bootstrap start...');
454
-
455
- // 3️⃣ 初始化 Bootstrap
456
- await bootstrap.initialize();
457
-
458
- // 4️⃣ 注册业务插件
459
- const bootstrapsRegistry = new BootstrapsRegistry(IOC);
460
-
461
- // 5️⃣ 启动应用
462
- await bootstrap.use(bootstrapsRegistry.register()).start();
463
-
464
- logger.info('bootstrap completed successfully');
465
- } catch (error) {
466
- logger.error(`${appConfig.appName} startup error:`, error);
467
- }
468
-
469
- return args;
470
- }
471
- }
472
- ```
473
-
474
- **关键步骤解析:**
475
-
476
- 1. **创建 IOC 容器** - 统一管理所有依赖
477
- 2. **创建 Bootstrap 实例** - 配置初始化参数
478
- 3. **初始化** - 执行 IOC、环境变量、全局变量的初始化
479
- 4. **注册插件** - 添加业务初始化逻辑
480
- 5. **启动** - 执行所有插件的生命周期方法
481
-
482
- ### 3. 插件注册器:BootstrapsRegistry.ts
483
-
484
- ```typescript
485
- // src/core/bootstraps/BootstrapsRegistry.ts
486
- import { IOCIdentifier } from '@config/IOCIdentifier';
487
- import { UserApiBootstarp } from '@/base/apis/userApi/UserApiBootstarp';
488
- import { FeApiBootstarp } from '@/base/apis/feApi/FeApiBootstarp';
489
- import { AiApiBootstarp } from '@/base/apis/AiApi';
490
-
491
- export class BootstrapsRegistry {
492
- constructor(
493
- protected IOC: IOCFunctionInterface<IOCIdentifierMap, IOCContainerInterface>
494
- ) {}
495
-
496
- get appConfig(): EnvConfigInterface {
497
- return this.IOC(IOCIdentifier.AppConfig);
498
- }
499
-
500
- /**
501
- * 注册所有业务插件
502
- */
503
- register(): BootstrapExecutorPlugin[] {
504
- const IOC = this.IOC;
505
-
506
- const bootstrapList = [
507
- // 1. 国际化服务(需要最先初始化)
508
- IOC(IOCIdentifier.I18nServiceInterface),
509
-
510
- // 2. API 配置插件
511
- new UserApiBootstarp(), // 用户 API
512
- new FeApiBootstarp(), // 业务 API
513
- AiApiBootstarp, // AI API
514
-
515
- // 3. 其他插件
516
- IOC(IOCIdentifier.I18nKeyErrorPlugin),
517
- IOC(IOCIdentifier.ProcesserExecutorInterface)
518
- ];
519
-
520
- // 开发环境:添加调试插件
521
- if (!this.appConfig.isProduction) {
522
- bootstrapList.push(printBootstrap);
523
- }
524
-
525
- return bootstrapList;
526
- }
527
- }
528
- ```
529
-
530
- **插件顺序很重要:**
531
-
532
- - ✅ 国际化服务最先初始化(其他插件可能需要翻译)
533
- - ✅ API 配置在业务逻辑之前
534
- - ✅ 开发工具仅在开发环境加载
535
-
536
- ---
537
-
538
- ## 🔌 插件系统
539
-
540
- ### 插件类型
541
-
542
- #### 1. 服务类插件(通过 IOC 注入)
543
-
544
- ```typescript
545
- // src/base/services/I18nService.ts
546
- @injectable()
547
- export class I18nService implements ExecutorPlugin {
548
- readonly pluginName = 'I18nService';
549
-
550
- constructor(@inject(IOCIdentifier.AppConfig) private config: AppConfig) {}
551
-
552
- /**
553
- * 在 Bootstrap 启动前加载翻译资源
554
- */
555
- async onBefore(): Promise<void> {
556
- await i18next.init({
557
- lng: this.config.defaultLanguage,
558
- fallbackLng: 'en',
559
- resources: this.loadResources()
560
- });
561
- }
562
-
563
- private loadResources() {
564
- // 加载翻译资源
565
- return {
566
- /* ... */
567
- };
568
- }
569
- }
570
-
571
- // 注册方式
572
- bootstrap.use([
573
- IOC(IOCIdentifier.I18nServiceInterface) // 从 IOC 容器获取
574
- ]);
575
- ```
576
-
577
- #### 2. 配置类插件(独立实例)
578
-
579
- ```typescript
580
- // src/base/apis/userApi/UserApiBootstrap.ts
581
- export class UserApiBootstarp implements BootstrapExecutorPlugin {
582
- readonly pluginName = 'UserApiBootstarp';
583
-
584
- /**
585
- * 配置 User API 的插件
586
- */
587
- onBefore({ parameters: { ioc } }: BootstrapContext): void {
588
- const userApi = ioc.get<UserApi>(UserApi);
589
-
590
- // 添加 URL 处理插件
591
- userApi.usePlugin(new FetchURLPlugin());
592
-
593
- // 添加 Mock 插件(开发环境)
594
- userApi.usePlugin(ioc.get(IOCIdentifier.ApiMockPlugin));
595
-
596
- // 添加请求日志插件
597
- userApi.usePlugin(ioc.get(RequestLogger));
598
- }
599
- }
600
-
601
- // 注册方式
602
- bootstrap.use([
603
- new UserApiBootstarp() // 直接创建实例
604
- ]);
605
- ```
606
-
607
- #### 3. 业务逻辑插件
608
-
609
- ```typescript
610
- // src/base/services/UserService.ts
611
- @injectable()
612
- export class UserService
613
- extends UserAuthService<UserInfo>
614
- implements ExecutorPlugin
615
- {
616
- readonly pluginName = 'UserService';
617
-
618
- constructor(
619
- @inject(IOCIdentifier.RouteServiceInterface)
620
- protected routerService: RouteServiceInterface,
621
- @inject(UserApi)
622
- userApi: UserAuthApiInterface<UserInfo>,
623
- @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
624
- @inject(IOCIdentifier.LocalStorageEncrypt)
625
- storage: SyncStorageInterface<string, string>
626
- ) {
627
- super(userApi, {
628
- userStorage: {
629
- key: appConfig.userInfoStorageKey,
630
- storage: storage
631
- },
632
- credentialStorage: {
633
- key: appConfig.userTokenStorageKey,
634
- storage: storage
635
- }
636
- });
637
- }
638
-
639
- /**
640
- * 在应用启动时检查用户认证状态
641
- */
642
- async onBefore(): Promise<void> {
643
- // 如果已登录,直接返回
644
- if (this.isAuthenticated()) {
645
- return;
646
- }
647
-
648
- // 尝试从存储中恢复用户信息
649
- const userToken = this.getToken();
650
- if (!userToken) {
651
- throw new AppError('NO_USER_TOKEN');
652
- }
653
-
654
- // 获取用户信息
655
- await this.userInfo();
656
- }
657
-
658
- getToken(): string | null {
659
- return this.credential();
660
- }
661
- }
662
- ```
663
-
664
- ### 插件生命周期详解
665
-
666
- ```typescript
667
- export interface BootstrapExecutorPlugin {
668
- readonly pluginName: string;
669
-
670
- /**
671
- * onBefore: 在初始化前执行
672
- *
673
- * 适用场景:
674
- * - 配置 API 客户端
675
- * - 加载资源(翻译、主题等)
676
- * - 检查用户认证
677
- * - 初始化第三方库
678
- */
679
- onBefore?(context: BootstrapContext): void | Promise<void>;
680
-
681
- /**
682
- * onExecute: 在初始化时执行
683
- *
684
- * 适用场景:
685
- * - 执行主要业务逻辑
686
- * - 启动后台任务
687
- */
688
- onExecute?(context: BootstrapContext): void | Promise<void>;
689
-
690
- /**
691
- * onAfter: 在初始化后执行
692
- *
693
- * 适用场景:
694
- * - 清理临时资源
695
- * - 记录启动日志
696
- * - 发送统计数据
697
- */
698
- onAfter?(context: BootstrapContext): void | Promise<void>;
699
-
700
- /**
701
- * onError: 错误处理
702
- *
703
- * 适用场景:
704
- * - 捕获插件错误
705
- * - 错误日志记录
706
- * - 错误恢复
707
- */
708
- onError?(error: Error, context: BootstrapContext): void | Promise<void>;
709
- }
710
- ```
711
-
712
- ---
713
-
714
- ## 🎯 实战示例
715
-
716
- ### 示例 1:国际化插件
717
-
718
- ```typescript
719
- // src/base/services/I18nService.ts
720
- import i18next from 'i18next';
721
- import { injectable, inject } from 'inversify';
722
- import { IOCIdentifier } from '@config/IOCIdentifier';
723
- import type { AppConfig } from '@/base/cases/AppConfig';
724
-
725
- @injectable()
726
- export class I18nService implements ExecutorPlugin {
727
- readonly pluginName = 'I18nService';
728
-
729
- constructor(@inject(IOCIdentifier.AppConfig) private config: AppConfig) {}
730
-
731
- async onBefore(): Promise<void> {
732
- // 加载翻译资源
733
- const resources = this.loadAllResources();
734
-
735
- // 初始化 i18next
736
- await i18next.init({
737
- lng: this.config.defaultLanguage || 'zh',
738
- fallbackLng: 'en',
739
- resources,
740
- interpolation: {
741
- escapeValue: false
742
- }
743
- });
744
-
745
- console.log('✅ I18n initialized:', i18next.language);
746
- }
747
-
748
- private loadAllResources() {
749
- // 从配置文件加载所有翻译资源
750
- return {
751
- zh: {
752
- translation: require('@config/i18n/zh').default
753
- },
754
- en: {
755
- translation: require('@config/i18n/en').default
756
- }
757
- };
758
- }
759
-
760
- t(key: string, options?: any): string {
761
- return i18next.t(key, options);
762
- }
763
- }
764
- ```
765
-
766
- ### 示例 2:API 配置插件
767
-
768
- ```typescript
769
- // src/base/apis/feApi/FeApiBootstrap.ts
770
- export class FeApiBootstarp implements BootstrapExecutorPlugin {
771
- readonly pluginName = 'FeApiBootstarp';
772
-
773
- onBefore({ parameters: { ioc } }: BootstrapContext): void {
774
- const feApi = ioc.get<FeApi>(FeApi);
775
- const appConfig = ioc.get<AppConfig>(IOCIdentifier.AppConfig);
776
-
777
- // 1. 配置基础 URL
778
- feApi.setBaseURL(appConfig.apiBaseUrl);
779
-
780
- // 2. 添加认证插件
781
- feApi.usePlugin(
782
- new AuthTokenPlugin({
783
- getToken: () => {
784
- const storage = ioc.get(IOCIdentifier.LocalStorageEncrypt);
785
- return storage.getItem('token');
786
- }
787
- })
788
- );
789
-
790
- // 3. 添加错误处理插件
791
- feApi.usePlugin(
792
- new ErrorHandlerPlugin({
793
- onError: (error) => {
794
- if (error.status === 401) {
795
- // 未授权,跳转登录
796
- const router = ioc.get(IOCIdentifier.RouteServiceInterface);
797
- router.push('/login');
798
- }
799
- }
800
- })
801
- );
802
-
803
- // 4. 添加请求日志插件(开发环境)
804
- if (!appConfig.isProduction) {
805
- feApi.usePlugin(new RequestLoggerPlugin());
806
- }
807
- }
808
- }
809
- ```
810
-
811
- ### 示例 3:用户认证插件
812
-
813
- ```typescript
814
- // src/base/services/UserService.ts
815
- @injectable()
816
- export class UserService
817
- extends UserAuthService<UserInfo>
818
- implements ExecutorPlugin
819
- {
820
- readonly pluginName = 'UserService';
821
-
822
- constructor(
823
- @inject(IOCIdentifier.RouteServiceInterface)
824
- protected routerService: RouteServiceInterface,
825
- @inject(UserApi) userApi: UserAuthApiInterface<UserInfo>,
826
- @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
827
- @inject(IOCIdentifier.LocalStorageEncrypt) storage: SyncStorageInterface
828
- ) {
829
- super(userApi, {
830
- userStorage: {
831
- key: appConfig.userInfoStorageKey,
832
- storage: storage
833
- },
834
- credentialStorage: {
835
- key: appConfig.userTokenStorageKey,
836
- storage: storage
837
- }
838
- });
839
- }
840
-
841
- /**
842
- * 在应用启动时自动恢复用户登录状态
843
- */
844
- async onBefore(): Promise<void> {
845
- try {
846
- // 检查是否在登录页
847
- if (this.routerService.isLoginPage()) {
848
- return;
849
- }
850
-
851
- // 如果已经有用户信息,直接返回
852
- if (this.isAuthenticated()) {
853
- console.log('✅ User already authenticated');
854
- return;
855
- }
856
-
857
- // 尝试从存储中恢复 token
858
- const token = this.getToken();
859
- if (!token) {
860
- // 没有 token,跳转登录
861
- throw new AppError('NO_USER_TOKEN');
862
- }
863
-
864
- // 使用 token 获取用户信息
865
- const userInfo = await this.userInfo();
866
- console.log('✅ User authenticated:', userInfo.name);
867
- } catch (error) {
868
- // 认证失败,清理存储并跳转登录
869
- this.clearAuth();
870
- this.routerService.push('/login');
871
- console.log('❌ User authentication failed, redirecting to login');
872
- }
873
- }
874
-
875
- getToken(): string | null {
876
- return this.credential();
877
- }
878
-
879
- private clearAuth() {
880
- this.setCredential(null);
881
- this.setUser(null);
882
- }
883
- }
884
- ```
885
-
886
- ### 示例 4:开发工具插件
887
-
888
- ```typescript
889
- // src/core/bootstraps/PrintBootstrap.ts
890
- export const printBootstrap: BootstrapExecutorPlugin = {
891
- pluginName: 'PrintBootstrap',
892
-
893
- onAfter({ parameters: { logger, ioc } }: BootstrapContext): void {
894
- const appConfig = ioc.get<AppConfig>(IOCIdentifier.AppConfig);
895
-
896
- // 打印应用信息
897
- logger.info('🚀 Application started successfully!');
898
- logger.info('📦 App Name:', appConfig.appName);
899
- logger.info('🌍 Environment:', appConfig.env);
900
- logger.info('🔗 API Base URL:', appConfig.apiBaseUrl);
901
-
902
- // 打印已注册的服务
903
- logger.info('📋 Registered Services:');
904
- logger.info(' - UserService');
905
- logger.info(' - I18nService');
906
- logger.info(' - RouteService');
907
-
908
- // 打印警告(如果有)
909
- if (!appConfig.isProduction && appConfig.mockEnabled) {
910
- logger.warn('⚠️ Mock API is enabled');
911
- }
912
- }
913
- };
914
- ```
915
-
916
- ---
917
-
918
- ## 🧪 测试:Bootstrap 的核心优势
919
-
920
- ### 为什么测试如此重要?
921
-
922
- Bootstrap 架构的一个**最重要的优势**就是**可测试性**。通过分离初始化逻辑和 UI,我们可以:
923
-
924
- - ✅ 独立测试每个插件
925
- - ✅ 轻松 mock 依赖
926
- - ✅ 快速运行测试(不需要渲染 UI)
927
- - ✅ 提高测试覆盖率
928
-
929
- ### 传统方式 vs Bootstrap 方式
930
-
931
- #### ❌ 传统方式:组件中混杂初始化逻辑
932
-
933
- ```typescript
934
- // ❌ 传统组件:难以测试
935
- function App() {
936
- const [loading, setLoading] = useState(true);
937
- const [user, setUser] = useState(null);
938
- const [i18nReady, setI18nReady] = useState(false);
939
- const [error, setError] = useState(null);
940
-
941
- useEffect(() => {
942
- const init = async () => {
943
- try {
944
- // 1. 初始化国际化
945
- await i18next.init({
946
- lng: 'zh',
947
- resources: { /* ... */ }
948
- });
949
- setI18nReady(true);
950
-
951
- // 2. 配置 API
952
- api.setBaseURL('https://api.example.com');
953
- api.usePlugin(new AuthPlugin());
954
-
955
- // 3. 检查用户认证
956
- const token = localStorage.getItem('token');
957
- if (token) {
958
- const userInfo = await fetch('/api/user', {
959
- headers: { Authorization: `Bearer ${token}` }
960
- }).then(res => res.json());
961
- setUser(userInfo);
962
- }
963
- } catch (err) {
964
- setError(err);
965
- } finally {
966
- setLoading(false);
967
- }
968
- };
969
-
970
- init();
971
- }, []);
972
-
973
- if (loading) return <div>Loading...</div>;
974
- if (error) return <div>Error: {error.message}</div>;
975
-
976
- return <Router />;
977
- }
978
- ```
979
-
980
- **测试代码(传统方式):😰😰😰 非常困难**
981
-
982
- ```typescript
983
- // ❌ 传统方式的测试:充满技巧和 hack
984
- import { describe, it, expect, vi, beforeEach } from 'vitest';
985
- import { render, screen, waitFor } from '@testing-library/react';
986
- import App from './App';
987
-
988
- describe('App (Traditional)', () => {
989
- beforeEach(() => {
990
- // 😰 需要 mock 全局变量
991
- global.localStorage = {
992
- getItem: vi.fn(),
993
- setItem: vi.fn(),
994
- removeItem: vi.fn(),
995
- clear: vi.fn()
996
- };
997
-
998
- // 😰 需要 mock fetch
999
- global.fetch = vi.fn();
1000
-
1001
- // 😰 需要 mock i18next
1002
- vi.mock('i18next', () => ({
1003
- init: vi.fn().mockResolvedValue(undefined),
1004
- t: vi.fn(key => key)
1005
- }));
1006
- });
1007
-
1008
- it('should initialize and load user', async () => {
1009
- // 😰 设置复杂的 mock
1010
- vi.mocked(localStorage.getItem).mockReturnValue('mock-token');
1011
- vi.mocked(fetch).mockResolvedValueOnce({
1012
- ok: true,
1013
- json: async () => ({ id: '1', name: 'John' })
1014
- });
1015
-
1016
- render(<App />);
1017
-
1018
- // 😰 需要等待多个异步操作
1019
- await waitFor(() => {
1020
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
1021
- }, { timeout: 3000 });
1022
-
1023
- // 😰 难以验证中间状态
1024
- expect(fetch).toHaveBeenCalledWith('/api/user', expect.any(Object));
1025
- });
1026
-
1027
- it('should handle error', async () => {
1028
- // 😰 每个测试都需要重新设置 mock
1029
- vi.mocked(fetch).mockRejectedValueOnce(new Error('Network error'));
1030
-
1031
- render(<App />);
1032
-
1033
- await waitFor(() => {
1034
- expect(screen.getByText(/Error/)).toBeInTheDocument();
1035
- });
1036
- });
1037
-
1038
- // 😰 问题:
1039
- // 1. 需要 mock 大量全局变量(localStorage, fetch, i18next)
1040
- // 2. 测试运行慢(需要渲染组件)
1041
- // 3. 难以测试错误场景
1042
- // 4. 测试之间可能互相干扰
1043
- // 5. 难以测试初始化的各个步骤
1044
- });
1045
- ```
1046
-
1047
- #### ✅ Bootstrap 方式:独立测试插件
1048
-
1049
- ```typescript
1050
- // ✅ Bootstrap 方式:逻辑和 UI 分离
1051
- // 1. 插件实现
1052
- @injectable()
1053
- export class UserService implements ExecutorPlugin {
1054
- readonly pluginName = 'UserService';
1055
-
1056
- constructor(
1057
- @inject(UserApi) private api: UserApi,
1058
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage,
1059
- @inject(IOCIdentifier.RouteServiceInterface) private router: RouteService
1060
- ) {}
1061
-
1062
- async onBefore(): Promise<void> {
1063
- const token = this.storage.getItem('token');
1064
- if (!token) {
1065
- throw new AppError('NO_USER_TOKEN');
1066
- }
1067
-
1068
- const userInfo = await this.api.getUserInfo(token);
1069
- this.setUser(userInfo);
1070
- }
1071
- }
1072
-
1073
- // 2. UI 组件变得简单
1074
- function App() {
1075
- return (
1076
- <BootstrapsProvider>
1077
- <ComboProvider themeConfig={themeConfig}>
1078
- <AppRouterProvider pages={allPages} />
1079
- </ComboProvider>
1080
- </BootstrapsProvider>
1081
- );
1082
- }
1083
- ```
1084
-
1085
- **测试代码(Bootstrap 方式):😊😊😊 非常简单**
1086
-
1087
- ```typescript
1088
- // ✅ Bootstrap 方式的测试:清晰、简单、快速
1089
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1090
- import { UserService } from '@/base/services/UserService';
1091
- import { AppError } from '@/base/cases/AppError';
1092
-
1093
- describe('UserService Plugin', () => {
1094
- let userService: UserService;
1095
- let mockApi: any;
1096
- let mockStorage: any;
1097
- let mockRouter: any;
1098
-
1099
- beforeEach(() => {
1100
- // ✅ 只需要 mock 依赖接口,不需要 mock 全局变量
1101
- mockApi = {
1102
- getUserInfo: vi.fn()
1103
- };
1104
-
1105
- mockStorage = {
1106
- getItem: vi.fn(),
1107
- setItem: vi.fn()
1108
- };
1109
-
1110
- mockRouter = {
1111
- push: vi.fn()
1112
- };
1113
-
1114
- // ✅ 创建服务实例
1115
- userService = new UserService(mockApi, mockStorage, mockRouter);
1116
- });
1117
-
1118
- it('should load user when token exists', async () => {
1119
- // ✅ 设置测试数据
1120
- mockStorage.getItem.mockReturnValue('mock-token');
1121
- mockApi.getUserInfo.mockResolvedValue({
1122
- id: '1',
1123
- name: 'John Doe'
1124
- });
1125
-
1126
- // ✅ 执行插件生命周期
1127
- await userService.onBefore();
1128
-
1129
- // ✅ 清晰的断言
1130
- expect(mockStorage.getItem).toHaveBeenCalledWith('token');
1131
- expect(mockApi.getUserInfo).toHaveBeenCalledWith('mock-token');
1132
- expect(userService.getUser()).toEqual({
1133
- id: '1',
1134
- name: 'John Doe'
1135
- });
1136
- });
1137
-
1138
- it('should throw error when token is missing', async () => {
1139
- // ✅ 轻松测试错误场景
1140
- mockStorage.getItem.mockReturnValue(null);
1141
-
1142
- // ✅ 验证错误
1143
- await expect(userService.onBefore()).rejects.toThrow(AppError);
1144
- await expect(userService.onBefore()).rejects.toThrow('NO_USER_TOKEN');
1145
- });
1146
-
1147
- it('should handle API error', async () => {
1148
- // ✅ 轻松模拟 API 错误
1149
- mockStorage.getItem.mockReturnValue('mock-token');
1150
- mockApi.getUserInfo.mockRejectedValue(new Error('Network error'));
1151
-
1152
- // ✅ 验证错误处理
1153
- await expect(userService.onBefore()).rejects.toThrow('Network error');
1154
- });
1155
-
1156
- // ✅ 优势:
1157
- // 1. 不需要 mock 全局变量
1158
- // 2. 测试运行快(不需要渲染 UI)
1159
- // 3. 易于测试错误场景
1160
- // 4. 测试之间完全独立
1161
- // 5. 可以单独测试每个初始化步骤
1162
- });
1163
- ```
1164
-
1165
- ### 测试复杂度对比
1166
-
1167
- | 测试场景 | 传统方式 | Bootstrap 方式 | 提升 |
1168
- | ---------------- | -------------------------------------------- | ------------------------------- | --------- |
1169
- | **Mock 复杂度** | 😰😰😰 需要 mock 全局变量、fetch、i18next 等 | 😊 只需 mock 依赖接口 | **80%** |
1170
- | **测试运行速度** | 😰😰 慢(需要渲染组件,等待异步) | 😊😊😊 快(纯逻辑测试) | **5-10x** |
1171
- | **测试错误场景** | 😰😰😰 困难(需要复杂的 mock 设置) | 😊😊😊 简单(直接 mock reject) | **90%** |
1172
- | **测试隔离性** | 😰😰 差(全局变量可能互相影响) | 😊😊😊 好(每个测试独立) | **100%** |
1173
- | **测试可读性** | 😰😰 差(充满 mock 和 hack) | 😊😊😊 好(清晰的输入输出) | **80%** |
1174
- | **覆盖率** | 😰😰 低(难以覆盖所有分支) | 😊😊😊 高(易于覆盖所有场景) | **50%** |
1175
-
1176
- ### 实际项目中的测试示例
1177
-
1178
- #### 示例 1:测试 I18n 插件
1179
-
1180
- ```typescript
1181
- // src/base/services/I18nService.test.ts
1182
- import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
1183
- import { I18nService } from '@/base/services/I18nService';
1184
- import i18n from 'i18next';
1185
-
1186
- // Mock i18next
1187
- vi.mock('i18next', () => ({
1188
- default: {
1189
- use: vi.fn().mockReturnThis(),
1190
- init: vi.fn(),
1191
- t: vi.fn(),
1192
- changeLanguage: vi.fn(),
1193
- language: 'en',
1194
- services: {
1195
- languageDetector: {
1196
- addDetector: vi.fn()
1197
- }
1198
- }
1199
- }
1200
- }));
1201
-
1202
- describe('I18nService', () => {
1203
- let service: I18nService;
1204
-
1205
- beforeEach(() => {
1206
- service = new I18nService('/en/test/path');
1207
- vi.clearAllMocks();
1208
- });
1209
-
1210
- describe('onBefore', () => {
1211
- it('should initialize i18n with correct configuration', () => {
1212
- // ✅ 执行插件生命周期
1213
- service.onBefore();
1214
-
1215
- // ✅ 验证初始化配置
1216
- expect(i18n.use).toHaveBeenCalledTimes(3);
1217
- expect(i18n.init).toHaveBeenCalledWith(
1218
- expect.objectContaining({
1219
- debug: false,
1220
- detection: {
1221
- order: ['pathLanguageDetector', 'navigator', 'localStorage'],
1222
- caches: []
1223
- }
1224
- })
1225
- );
1226
- });
1227
-
1228
- it('should detect language from path correctly', () => {
1229
- service.onBefore();
1230
-
1231
- const detector = vi.mocked(i18n.services.languageDetector.addDetector)
1232
- .mock.calls[0][0];
1233
-
1234
- // ✅ 测试语言检测逻辑
1235
- const language = detector.lookup();
1236
- expect(language).toBe('en');
1237
- });
1238
-
1239
- it('should return fallback language for invalid path', () => {
1240
- const invalidService = new I18nService('/invalid/path');
1241
- invalidService.onBefore();
1242
-
1243
- const detector = vi.mocked(i18n.services.languageDetector.addDetector)
1244
- .mock.calls[0][0];
1245
-
1246
- // ✅ 测试边界情况
1247
- const language = detector.lookup();
1248
- expect(language).toBe('zh'); // fallback language
1249
- });
1250
- });
1251
-
1252
- describe('changeLanguage', () => {
1253
- it('should change language using i18n', async () => {
1254
- await service.changeLanguage('en');
1255
- expect(i18n.changeLanguage).toHaveBeenCalledWith('en');
1256
- });
1257
-
1258
- it('should handle language change error', async () => {
1259
- // ✅ 测试错误场景
1260
- vi.mocked(i18n.changeLanguage).mockRejectedValueOnce(
1261
- new Error('Change failed')
1262
- );
1263
-
1264
- await expect(service.changeLanguage('en')).rejects.toThrow(
1265
- 'Change failed'
1266
- );
1267
- });
1268
- });
1269
- });
1270
- ```
1271
-
1272
- #### 示例 2:测试 Bootstrap 启动流程
1273
-
1274
- ```typescript
1275
- // __tests__/src/core/bootstraps/BootstrapsApp.test.ts
1276
- import { describe, it, expect, beforeEach, vi } from 'vitest';
1277
- import { BootstrapClient } from '@/core/bootstraps/BootstrapClient';
1278
- import type { BootstrapClientArgs } from '@/core/bootstraps/BootstrapClient';
1279
- import { InversifyContainer } from '@/base/cases/InversifyContainer';
1280
- import { createIOCFunction } from '@qlover/corekit-bridge';
1281
- import { browserGlobalsName } from '@config/common';
1282
-
1283
- // Mock 依赖
1284
- vi.mock('@/core/registers/IocRegisterImpl', () => ({
1285
- IocRegisterImpl: vi.fn().mockImplementation(() => ({
1286
- getRegisterList: vi.fn().mockReturnValue([]),
1287
- register: vi.fn()
1288
- }))
1289
- }));
1290
-
1291
- vi.mock('@/core/bootstraps/BootstrapsRegistry', () => ({
1292
- BootstrapsRegistry: vi.fn().mockImplementation(() => ({
1293
- register: vi.fn().mockReturnValue([])
1294
- }))
1295
- }));
1296
-
1297
- describe('BootstrapClient', () => {
1298
- let mockArgs: BootstrapClientArgs;
1299
- let mockIOC: ReturnType<typeof createIOCFunction>;
1300
-
1301
- beforeEach(() => {
1302
- vi.clearAllMocks();
1303
-
1304
- const container = new InversifyContainer();
1305
- mockIOC = createIOCFunction(container);
1306
-
1307
- mockArgs = {
1308
- root: {},
1309
- bootHref: 'http://localhost:3000',
1310
- ioc: {
1311
- create: vi.fn().mockReturnValue(mockIOC)
1312
- }
1313
- };
1314
- });
1315
-
1316
- describe('main', () => {
1317
- it('should initialize bootstrap successfully', async () => {
1318
- // ✅ 执行启动流程
1319
- const result = await BootstrapClient.main(mockArgs);
1320
-
1321
- // ✅ 验证启动结果
1322
- expect(result.bootHref).toBe('http://localhost:3000');
1323
-
1324
- // ✅ 验证全局变量注入
1325
- expect(
1326
- (mockArgs.root as Record<string, unknown>)[browserGlobalsName]
1327
- ).toBeDefined();
1328
-
1329
- const injectedGlobals = (mockArgs.root as Record<string, unknown>)[
1330
- browserGlobalsName
1331
- ] as Record<string, unknown>;
1332
-
1333
- expect(injectedGlobals).toHaveProperty('logger');
1334
- expect(injectedGlobals).toHaveProperty('appConfig');
1335
- });
1336
-
1337
- it('should handle initialization error', async () => {
1338
- // ✅ 测试错误场景
1339
- mockArgs.ioc.create = vi.fn().mockImplementation(() => {
1340
- throw new Error('IOC creation failed');
1341
- });
1342
-
1343
- // ✅ 验证错误不会导致应用崩溃
1344
- await expect(BootstrapClient.main(mockArgs)).rejects.toThrow(
1345
- 'IOC creation failed'
1346
- );
1347
- });
1348
- });
1349
- });
1350
- ```
1351
-
1352
- #### 示例 3:测试 API 配置插件
1353
-
1354
- ```typescript
1355
- // __tests__/src/base/apis/UserApiBootstrap.test.ts
1356
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1357
- import { UserApiBootstarp } from '@/base/apis/userApi/UserApiBootstarp';
1358
-
1359
- describe('UserApiBootstrap', () => {
1360
- let plugin: UserApiBootstarp;
1361
- let mockContext: any;
1362
- let mockUserApi: any;
1363
-
1364
- beforeEach(() => {
1365
- plugin = new UserApiBootstarp();
1366
-
1367
- // ✅ 创建 mock 上下文
1368
- mockUserApi = {
1369
- usePlugin: vi.fn()
1370
- };
1371
-
1372
- mockContext = {
1373
- parameters: {
1374
- ioc: {
1375
- get: vi.fn().mockReturnValue(mockUserApi)
1376
- }
1377
- }
1378
- };
1379
- });
1380
-
1381
- it('should have correct plugin name', () => {
1382
- expect(plugin.pluginName).toBe('UserApiBootstarp');
1383
- });
1384
-
1385
- it('should configure API plugins in onBefore', () => {
1386
- // ✅ 执行插件生命周期
1387
- plugin.onBefore(mockContext);
1388
-
1389
- // ✅ 验证 API 配置
1390
- expect(mockContext.parameters.ioc.get).toHaveBeenCalled();
1391
- expect(mockUserApi.usePlugin).toHaveBeenCalled();
1392
- });
1393
-
1394
- it('should add multiple plugins to API', () => {
1395
- plugin.onBefore(mockContext);
1396
-
1397
- // ✅ 验证添加了多个插件
1398
- expect(mockUserApi.usePlugin).toHaveBeenCalledTimes(3);
1399
- });
1400
- });
1401
- ```
1402
-
1403
- ### 测试最佳实践
1404
-
1405
- #### 1. ✅ 使用 Vitest 的测试工具
1406
-
1407
- ```typescript
1408
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
1409
-
1410
- describe('MyPlugin', () => {
1411
- beforeEach(() => {
1412
- // ✅ 每个测试前重置 mock
1413
- vi.clearAllMocks();
1414
- });
1415
-
1416
- afterEach(() => {
1417
- // ✅ 清理资源
1418
- vi.restoreAllMocks();
1419
- });
1420
-
1421
- it('should do something', () => {
1422
- // 测试逻辑
1423
- });
1424
- });
1425
- ```
1426
-
1427
- #### 2. ✅ 测试插件的生命周期
1428
-
1429
- ```typescript
1430
- describe('UserService Plugin', () => {
1431
- it('should execute onBefore lifecycle', async () => {
1432
- const service = new UserService(mockApi, mockStorage, mockRouter);
1433
-
1434
- // ✅ 测试 onBefore
1435
- await service.onBefore();
1436
-
1437
- expect(mockApi.getUserInfo).toHaveBeenCalled();
1438
- });
1439
-
1440
- it('should execute onAfter lifecycle', async () => {
1441
- const service = new UserService(mockApi, mockStorage, mockRouter);
1442
-
1443
- // ✅ 测试 onAfter
1444
- await service.onAfter?.();
1445
-
1446
- // 验证清理逻辑
1447
- });
1448
-
1449
- it('should handle onError lifecycle', async () => {
1450
- const service = new UserService(mockApi, mockStorage, mockRouter);
1451
- const error = new Error('Test error');
1452
-
1453
- // ✅ 测试 onError
1454
- await service.onError?.(error, mockContext);
1455
-
1456
- // 验证错误处理
1457
- });
1458
- });
1459
- ```
1460
-
1461
- #### 3. ✅ 测试边界情况和错误场景
1462
-
1463
- ```typescript
1464
- describe('UserService Error Handling', () => {
1465
- it('should handle missing token', async () => {
1466
- mockStorage.getItem.mockReturnValue(null);
1467
-
1468
- // ✅ 验证错误
1469
- await expect(service.onBefore()).rejects.toThrow('NO_USER_TOKEN');
1470
- });
1471
-
1472
- it('should handle network error', async () => {
1473
- mockStorage.getItem.mockReturnValue('token');
1474
- mockApi.getUserInfo.mockRejectedValue(new Error('Network error'));
1475
-
1476
- // ✅ 验证错误处理
1477
- await expect(service.onBefore()).rejects.toThrow('Network error');
1478
- });
1479
-
1480
- it('should handle invalid token', async () => {
1481
- mockStorage.getItem.mockReturnValue('invalid-token');
1482
- mockApi.getUserInfo.mockRejectedValue(new Error('401 Unauthorized'));
1483
-
1484
- // ✅ 验证 401 错误处理
1485
- await expect(service.onBefore()).rejects.toThrow('401 Unauthorized');
1486
- });
1487
- });
1488
- ```
1489
-
1490
- #### 4. ✅ 测试插件之间的依赖
1491
-
1492
- ```typescript
1493
- describe('Plugin Dependencies', () => {
1494
- it('should ensure I18n is initialized before UserService', async () => {
1495
- const i18nService = new I18nService('/en/path');
1496
- const userService = new UserService(mockApi, mockStorage, mockRouter);
1497
-
1498
- // ✅ I18n 先初始化
1499
- await i18nService.onBefore();
1500
-
1501
- // ✅ 然后初始化 UserService
1502
- await userService.onBefore();
1503
-
1504
- // ✅ 验证 UserService 可以使用翻译
1505
- expect(i18n.t('some.key')).toBeDefined();
1506
- });
1507
- });
1508
- ```
1509
-
1510
- ### 运行测试
1511
-
1512
- ```bash
1513
- # 运行所有测试
1514
- npm run test
1515
-
1516
- # 运行测试并监听文件变化
1517
- npm run test -- --watch
1518
-
1519
- # 运行特定文件的测试
1520
- npm run test -- UserService.test.ts
1521
-
1522
- # 生成测试覆盖率报告
1523
- npm run test -- --coverage
1524
- ```
1525
-
1526
- ### 测试覆盖率目标
1527
-
1528
- 在 Bootstrap 架构下,我们可以轻松达到高覆盖率:
1529
-
1530
- - **插件逻辑**:> 90% 覆盖率
1531
- - **服务层**:> 85% 覆盖率
1532
- - **API 适配器**:> 80% 覆盖率
1533
- - **整体应用**:> 75% 覆盖率
1534
-
1535
- ### 总结:测试的价值
1536
-
1537
- Bootstrap 架构通过分离关注点,让测试变得:
1538
-
1539
- 1. **更简单** - 不需要 mock 全局变量和复杂的环境
1540
- 2. **更快速** - 纯逻辑测试,不需要渲染 UI
1541
- 3. **更可靠** - 测试之间完全独立,不会互相干扰
1542
- 4. **更全面** - 易于测试所有边界情况和错误场景
1543
- 5. **更有信心** - 高覆盖率保证代码质量
1544
-
1545
- > 💡 **重要提示**:可测试性是 Bootstrap 架构最大的优势之一。如果你发现某个插件难以测试,很可能是设计有问题,需要重新考虑职责划分。
1546
-
1547
- ---
1548
-
1549
- ## 💎 最佳实践
1550
-
1551
- ### 1. 插件设计原则
1552
-
1553
- #### ✅ 单一职责
1554
-
1555
- ```typescript
1556
- // ✅ 好的插件设计:只做一件事
1557
- export class ApiConfigPlugin implements BootstrapExecutorPlugin {
1558
- readonly pluginName = 'ApiConfigPlugin';
1559
-
1560
- onBefore({ parameters: { ioc } }: BootstrapContext): void {
1561
- // 只负责配置 API
1562
- const api = ioc.get<FeApi>(FeApi);
1563
- api.setBaseURL(config.apiBaseUrl);
1564
- api.usePlugin(new AuthPlugin());
1565
- }
1566
- }
1567
-
1568
- // ❌ 不好的插件设计:做了太多事
1569
- export class BadPlugin implements BootstrapExecutorPlugin {
1570
- readonly pluginName = 'BadPlugin';
1571
-
1572
- onBefore({ parameters: { ioc } }: BootstrapContext): void {
1573
- // 配置 API
1574
- const api = ioc.get<FeApi>(FeApi);
1575
- api.setBaseURL(config.apiBaseUrl);
1576
-
1577
- // 初始化国际化
1578
- i18next.init({
1579
- /* ... */
1580
- });
1581
-
1582
- // 检查用户认证
1583
- checkAuth();
1584
-
1585
- // 配置路由
1586
- configureRouter();
1587
-
1588
- // 太多职责!❌
1589
- }
1590
- }
1591
- ```
1592
-
1593
- #### ✅ 明确依赖
1594
-
1595
- ```typescript
1596
- // ✅ 通过构造函数注入依赖
1597
- @injectable()
1598
- export class UserService implements ExecutorPlugin {
1599
- constructor(
1600
- @inject(UserApi) private api: UserApi,
1601
- @inject(IOCIdentifier.AppConfig) private config: AppConfig
1602
- ) {}
1603
- }
1604
-
1605
- // ❌ 直接创建依赖
1606
- export class BadUserService implements ExecutorPlugin {
1607
- private api = new UserApi(); // ❌ 硬编码依赖
1608
- private config = new AppConfig(); // ❌ 难以测试
1609
- }
1610
- ```
1611
-
1612
- ### 2. 错误处理
1613
-
1614
- ```typescript
1615
- export class UserService implements ExecutorPlugin {
1616
- readonly pluginName = 'UserService';
1617
-
1618
- async onBefore(): Promise<void> {
1619
- try {
1620
- await this.initializeUser();
1621
- } catch (error) {
1622
- // ✅ 优雅的错误处理
1623
- if (error instanceof AppError) {
1624
- // 业务错误
1625
- this.handleBusinessError(error);
1626
- } else if (error instanceof NetworkError) {
1627
- // 网络错误
1628
- this.handleNetworkError(error);
1629
- } else {
1630
- // 未知错误
1631
- this.logger.error('Unknown error:', error);
1632
- }
1633
-
1634
- // 不要让错误传播,导致应用崩溃
1635
- // 而是进行适当的降级处理
1636
- }
1637
- }
1638
-
1639
- private handleBusinessError(error: AppError) {
1640
- if (error.code === 'NO_USER_TOKEN') {
1641
- // 跳转登录页
1642
- this.router.push('/login');
1643
- } else if (error.code === 'TOKEN_EXPIRED') {
1644
- // 刷新 token
1645
- this.refreshToken();
1646
- }
1647
- }
1648
- }
1649
- ```
1650
-
1651
- ### 3. 性能优化
1652
-
1653
- ```typescript
1654
- // ✅ 按需加载插件
1655
- export class BootstrapsRegistry {
1656
- register(): BootstrapExecutorPlugin[] {
1657
- const plugins: BootstrapExecutorPlugin[] = [
1658
- // 必需插件
1659
- IOC(IOCIdentifier.I18nServiceInterface),
1660
- new UserApiBootstarp()
1661
- ];
1662
-
1663
- // 开发环境插件
1664
- if (!this.appConfig.isProduction) {
1665
- plugins.push(new DevToolsPlugin(), new MockDataPlugin());
1666
- }
1667
-
1668
- // 功能开关插件
1669
- if (this.appConfig.features.analytics) {
1670
- plugins.push(new AnalyticsPlugin());
1671
- }
1672
-
1673
- return plugins;
1674
- }
1675
- }
1676
- ```
1677
-
1678
- ### 4. 日志记录
1679
-
1680
- ```typescript
1681
- export class ApiConfigPlugin implements BootstrapExecutorPlugin {
1682
- readonly pluginName = 'ApiConfigPlugin';
1683
-
1684
- async onBefore({ parameters: { logger } }: BootstrapContext): Promise<void> {
1685
- logger.info(`[${this.pluginName}] Configuring API...`);
1686
-
1687
- try {
1688
- await this.configureAPI();
1689
- logger.info(`[${this.pluginName}] ✅ API configured successfully`);
1690
- } catch (error) {
1691
- logger.error(`[${this.pluginName}] ❌ API configuration failed:`, error);
1692
- throw error;
1693
- }
1694
- }
1695
- }
1696
- ```
1697
-
1698
- ---
1699
-
1700
- ## ❓ 常见问题
1701
-
1702
- ### Q1: Bootstrap 和 React 的生命周期有什么关系?
1703
-
1704
- **A:** Bootstrap 在 React 渲染之前执行。
1705
-
1706
- ```
1707
- Bootstrap 初始化 → Bootstrap 启动 → React 渲染
1708
- ```
1709
-
1710
- ### Q2: 插件执行顺序重要吗?
1711
-
1712
- **A:** 非常重要!插件按照注册顺序依次执行。
1713
-
1714
- ```typescript
1715
- // ✅ 正确的顺序
1716
- bootstrap.use([
1717
- IOC(I18nService), // 1. 先初始化国际化(其他插件可能需要)
1718
- new ApiConfigPlugin(), // 2. 再配置 API
1719
- IOC(UserService) // 3. 最后检查用户认证(依赖 API)
1720
- ]);
1721
-
1722
- // ❌ 错误的顺序
1723
- bootstrap.use([
1724
- IOC(UserService), // ❌ 用户服务依赖 API,但 API 还没配置
1725
- new ApiConfigPlugin(), // 配置 API
1726
- IOC(I18nService) // 国际化在最后(太晚了)
1727
- ]);
1728
- ```
1729
-
1730
- ### Q3: 如何调试 Bootstrap?
1731
-
1732
- ```typescript
1733
- // 方法 1:使用日志
1734
- export class MyPlugin implements BootstrapExecutorPlugin {
1735
- readonly pluginName = 'MyPlugin';
1736
-
1737
- async onBefore({ parameters: { logger } }: BootstrapContext): Promise<void> {
1738
- logger.info(`[${this.pluginName}] Starting...`);
1739
- // ... 你的逻辑
1740
- logger.info(`[${this.pluginName}] Completed`);
1741
- }
1742
- }
1743
-
1744
- // 方法 2:使用调试插件
1745
- export const debugPlugin: BootstrapExecutorPlugin = {
1746
- pluginName: 'DebugPlugin',
1747
-
1748
- onBefore(context) {
1749
- console.log('onBefore context:', context);
1750
- },
1751
-
1752
- onAfter(context) {
1753
- console.log('onAfter context:', context);
1754
- }
1755
- };
1756
- ```
1757
-
1758
- ### Q4: 如何测试插件?
1759
-
1760
- ```typescript
1761
- describe('UserService Plugin', () => {
1762
- it('should initialize user on startup', async () => {
1763
- // 创建 mock 依赖
1764
- const mockApi = {
1765
- getUserInfo: jest.fn().mockResolvedValue({ name: 'John' })
1766
- };
1767
- const mockStorage = {
1768
- getItem: jest.fn().mockReturnValue('mock-token')
1769
- };
1770
-
1771
- // 创建服务
1772
- const userService = new UserService(
1773
- mockRouter,
1774
- mockApi,
1775
- mockConfig,
1776
- mockStorage
1777
- );
1778
-
1779
- // 执行插件生命周期
1780
- await userService.onBefore();
1781
-
1782
- // 验证
1783
- expect(mockApi.getUserInfo).toHaveBeenCalledWith('mock-token');
1784
- });
1785
- });
1786
- ```
1787
-
1788
- ### Q5: Bootstrap 适合所有项目吗?
1789
-
1790
- **A:** 不一定。Bootstrap 更适合:
1791
-
1792
- ✅ **适合使用的场景:**
1793
-
1794
- - 中大型应用
1795
- - 需要复杂初始化逻辑
1796
- - 多端应用(Web、移动端、小程序)
1797
- - 需要模块化和可测试性
1798
- - 团队协作开发
1799
-
1800
- ❌ **不适合的场景:**
1801
-
1802
- - 简单的展示页面
1803
- - 原型项目
1804
- - 没有复杂初始化逻辑的项目
1805
-
1806
- ### Q6: 如何保证测试覆盖率?
1807
-
1808
- **A:** Bootstrap 架构天然支持高覆盖率:
1809
-
1810
- ```typescript
1811
- // ✅ 每个插件都可以独立测试
1812
- describe('UserService', () => {
1813
- it('should initialize user', async () => {
1814
- const service = new UserService(mockApi, mockStorage, mockRouter);
1815
- await service.onBefore();
1816
- expect(mockApi.getUserInfo).toHaveBeenCalled();
1817
- });
1818
-
1819
- // 易于测试所有边界情况
1820
- it('should handle missing token', async () => {
1821
- mockStorage.getItem.mockReturnValue(null);
1822
- await expect(service.onBefore()).rejects.toThrow('NO_USER_TOKEN');
1823
- });
1824
-
1825
- it('should handle API error', async () => {
1826
- mockApi.getUserInfo.mockRejectedValue(new Error('Network error'));
1827
- await expect(service.onBefore()).rejects.toThrow('Network error');
1828
- });
1829
- });
1830
- ```
1831
-
1832
- **覆盖率目标:**
1833
-
1834
- - 插件逻辑:> 90%
1835
- - 服务层:> 85%
1836
- - API 适配器:> 80%
1837
-
1838
- ### Q7: Vitest 和 Jest 有什么区别?
1839
-
1840
- **A:** 本项目使用 Vitest,它是 Vite 生态的测试框架:
1841
-
1842
- | 特性 | Vitest | Jest |
1843
- | ------------ | ----------------------------- | ------------ |
1844
- | **速度** | ⚡ 非常快(基于 Vite) | 慢 |
1845
- | **配置** | 🎯 零配置(复用 vite.config) | 需要单独配置 |
1846
- | **ESM 支持** | ✅ 原生支持 | ⚠️ 实验性 |
1847
- | **API** | 与 Jest 兼容 | - |
1848
- | **HMR** | ✅ 支持 | ❌ 不支持 |
1849
-
1850
- ```typescript
1851
- // Vitest 使用方式(与 Jest 几乎相同)
1852
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1853
-
1854
- describe('MyTest', () => {
1855
- beforeEach(() => {
1856
- vi.clearAllMocks();
1857
- });
1858
-
1859
- it('should work', () => {
1860
- expect(true).toBe(true);
1861
- });
1862
- });
1863
- ```
1864
-
1865
- ---
1866
-
1867
- ## 📚 相关文档
1868
-
1869
- - [项目架构设计](./index.md) - 了解整体架构
1870
- - [IOC 容器](./ioc.md) - 依赖注入详解
1871
- - [环境变量](./env.md) - 环境配置管理
1872
- - [全局变量封装](./global.md) - 浏览器 API 封装
1873
-
1874
- ---
1875
-
1876
- ## 🎉 总结
1877
-
1878
- Bootstrap 启动器是现代前端架构的重要组成部分,它帮助我们:
1879
-
1880
- 1. **分离关注点** - UI 和初始化逻辑分离
1881
- 2. **提高可维护性** - 模块化设计,职责清晰
1882
- 3. **增强可测试性** - 每个插件可独立测试
1883
- 4. **支持团队协作** - 不同开发者可以独立开发插件
1884
- 5. **适应变化** - 易于扩展和修改
1885
-
1886
- 通过 Bootstrap,我们构建了一个更加健壮、可维护、可测试的前端应用架构。
1887
-
1888
- ---
1889
-
1890
- **问题反馈:**
1891
- 如果你对 Bootstrap 有任何疑问或建议,请在团队频道中讨论或提交 Issue。