@qlover/create-app 1.0.2 → 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 +43 -0
  2. package/dist/index.cjs +9 -9
  3. package/dist/index.js +9 -9
  4. package/package.json +4 -3
  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 -51
  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,1364 +0,0 @@
1
- # IOC 容器 (依赖注入)
2
-
3
- ## 📋 目录
4
-
5
- - [核心理念](#-核心理念) - UI 分离,逻辑独立
6
- - [什么是 IOC](#-什么是-ioc) - 控制反转
7
- - [为什么需要 IOC](#-为什么需要-ioc) - 解决的核心问题
8
- - [两个关键问题](#-两个关键问题) - 为什么需要接口?为什么简单组件也要分离?
9
- - [项目中的实现](#-项目中的实现) - Bootstrap 集成
10
- - [使用方式](#-使用方式) - 实战指南
11
- - [测试](#-测试) - 独立测试和组合测试
12
- - [最佳实践](#-最佳实践) - 8 条核心实践
13
- - [常见问题](#-常见问题) - FAQ
14
-
15
- ---
16
-
17
- ## 🎯 核心理念
18
-
19
- > **🚨 重要原则:UI 就是 UI,逻辑就是逻辑,两者必须分离!**
20
-
21
- > **⭐ 核心优势:UI 和逻辑可以独立测试,也可以组合测试!**
22
-
23
- ### 核心概念
24
-
25
- ```
26
- ┌─────────────────────────────────────────┐
27
- │ 传统方式:UI 和逻辑混在一起 │
28
- │ │
29
- │ Component (组件) │
30
- │ ├── UI 渲染 │
31
- │ ├── 业务逻辑 │
32
- │ ├── API 调用 │
33
- │ ├── 状态管理 │
34
- │ └── 数据处理 │
35
- │ │
36
- │ ❌ 问题: │
37
- │ - 难以测试(需要渲染组件) │
38
- │ - 逻辑无法复用 │
39
- │ - 职责不清晰 │
40
- └─────────────────────────────────────────┘
41
-
42
- ┌─────────────────────────────────────────┐
43
- │ IOC 方式:UI 和逻辑完全分离 │
44
- │ │
45
- │ Component (UI 层) │
46
- │ └── 只负责渲染 │
47
- │ ↓ 通过 IOC 获取 │
48
- │ Service (逻辑层) │
49
- │ ├── 业务逻辑 │
50
- │ ├── API 调用 │
51
- │ ├── 状态管理 │
52
- │ └── 数据处理 │
53
- │ │
54
- │ ✅ 优势: │
55
- │ - UI 和逻辑可以独立测试 │
56
- │ - 逻辑可以复用 │
57
- │ - 职责清晰 │
58
- └─────────────────────────────────────────┘
59
- ```
60
-
61
- ---
62
-
63
- ## 🔄 什么是 IOC
64
-
65
- IOC(Inversion of Control,控制反转)= **不要自己 new,让容器帮你创建和管理对象**
66
-
67
- ### 传统方式 vs IOC
68
-
69
- ```typescript
70
- // ❌ 传统方式:自己创建依赖(强耦合)
71
- class UserComponent {
72
- private userService = new UserService(); // 自己 new
73
- private storage = new LocalStorage(); // 自己 new
74
- private api = new UserApi(); // 自己 new
75
-
76
- async loadUser() {
77
- return await this.userService.getUser();
78
- }
79
- }
80
-
81
- // 问题:
82
- // 1. UserComponent 依赖具体的实现类
83
- // 2. 无法替换 UserService 的实现
84
- // 3. 测试时无法 mock UserService
85
- // 4. UserService 的依赖需要手动创建
86
-
87
-
88
- // ✅ IOC 方式:容器注入依赖(松耦合)
89
- function UserComponent() {
90
- // 从 IOC 容器获取服务
91
- const userService = useIOC('UserServiceInterface'); // 容器提供
92
-
93
- async function loadUser() {
94
- return await userService.getUser();
95
- }
96
-
97
- // UI 只负责渲染
98
- return <div>...</div>;
99
- }
100
-
101
- // 优势:
102
- // 1. UserComponent 依赖接口,不依赖实现
103
- // 2. 可以轻松替换 UserService 的实现
104
- // 3. 测试时可以 mock UserService
105
- // 4. UserService 的依赖由容器管理
106
- ```
107
-
108
- ### 类比理解
109
-
110
- ```
111
- 传统方式 = 自己做饭
112
- - 需要买菜(创建依赖)
113
- - 需要做饭(管理生命周期)
114
- - 需要洗碗(清理资源)
115
-
116
- IOC 方式 = 去餐厅
117
- - 点菜(告诉容器需要什么)
118
- - 等待上菜(容器提供服务)
119
- - 不需要关心厨房的事(依赖管理由容器负责)
120
- ```
121
-
122
- ---
123
-
124
- ## 🤔 为什么需要 IOC
125
-
126
- ### 核心问题:UI 和逻辑混在一起
127
-
128
- #### ❌ 问题示例:没有 UI 分离
129
-
130
- ```typescript
131
- // ❌ 传统组件:UI 和逻辑混在一起
132
- function UserProfile() {
133
- const [user, setUser] = useState(null);
134
- const [loading, setLoading] = useState(false);
135
- const [error, setError] = useState(null);
136
-
137
- // 😰 业务逻辑混在组件中
138
- useEffect(() => {
139
- setLoading(true);
140
-
141
- // 😰 API 调用在组件中
142
- fetch('/api/user')
143
- .then(res => res.json())
144
- .then(data => {
145
- // 😰 数据处理在组件中
146
- const processedData = {
147
- ...data,
148
- fullName: `${data.firstName} ${data.lastName}`
149
- };
150
- setUser(processedData);
151
- })
152
- .catch(err => setError(err))
153
- .finally(() => setLoading(false));
154
- }, []);
155
-
156
- // 😰 更多业务逻辑
157
- const handleLogout = () => {
158
- localStorage.removeItem('token');
159
- localStorage.removeItem('user');
160
- window.location.href = '/login';
161
- };
162
-
163
- // UI 渲染
164
- if (loading) return <div>Loading...</div>;
165
- if (error) return <div>Error: {error.message}</div>;
166
-
167
- return (
168
- <div>
169
- <h1>{user?.fullName}</h1>
170
- <button onClick={handleLogout}>Logout</button>
171
- </div>
172
- );
173
- }
174
-
175
- // 😰😰😰 问题总结:
176
- // 1. UI 和逻辑混在一起,难以维护
177
- // 2. 逻辑无法复用(如果另一个组件也需要用户信息怎么办?)
178
- // 3. 难以测试(需要渲染组件才能测试业务逻辑)
179
- // 4. 职责不清晰(组件做了太多事)
180
- // 5. 无法单独测试逻辑(必须通过 UI 测试)
181
- ```
182
-
183
- #### ✅ 解决方案:IOC + UI 分离
184
-
185
- ```typescript
186
- // ✅ 步骤 1:定义接口(Port)
187
- export interface UserServiceInterface {
188
- getUser(): Promise<UserInfo>;
189
- logout(): Promise<void>;
190
- isAuthenticated(): boolean;
191
- }
192
-
193
- // ✅ 步骤 2:实现服务(逻辑层)
194
- @injectable()
195
- export class UserService implements UserServiceInterface {
196
- constructor(
197
- @inject(UserApi) private api: UserApi,
198
- @inject(IOCIdentifier.AppConfig) private config: AppConfig,
199
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage,
200
- @inject(IOCIdentifier.RouteServiceInterface) private router: RouteService
201
- ) {}
202
-
203
- // 纯逻辑:获取用户信息
204
- async getUser(): Promise<UserInfo> {
205
- const data = await this.api.getUserInfo();
206
-
207
- // 数据处理
208
- return {
209
- ...data,
210
- fullName: `${data.firstName} ${data.lastName}`
211
- };
212
- }
213
-
214
- // 纯逻辑:退出登录
215
- async logout(): Promise<void> {
216
- this.storage.removeItem(this.config.userTokenStorageKey);
217
- this.storage.removeItem(this.config.userInfoStorageKey);
218
- await this.router.push('/login');
219
- }
220
-
221
- isAuthenticated(): boolean {
222
- return !!this.storage.getItem(this.config.userTokenStorageKey);
223
- }
224
- }
225
-
226
- // ✅ 步骤 3:UI 组件(UI 层)
227
- function UserProfile() {
228
- // 从 IOC 容器获取服务
229
- const userService = useIOC('UserServiceInterface');
230
- const [user, setUser] = useState(null);
231
- const [loading, setLoading] = useState(false);
232
-
233
- useEffect(() => {
234
- setLoading(true);
235
- // ✅ UI 只调用服务,不包含业务逻辑
236
- userService.getUser()
237
- .then(setUser)
238
- .finally(() => setLoading(false));
239
- }, []);
240
-
241
- // ✅ UI 只负责渲染和事件绑定
242
- if (loading) return <div>Loading...</div>;
243
-
244
- return (
245
- <div>
246
- <h1>{user?.fullName}</h1>
247
- <button onClick={() => userService.logout()}>Logout</button>
248
- </div>
249
- );
250
- }
251
-
252
- // ✅✅✅ 优势总结:
253
- // 1. UI 和逻辑完全分离,职责清晰
254
- // 2. 逻辑可以复用(其他组件也可以使用 UserService)
255
- // 3. 易于测试(可以独立测试 UserService,不需要渲染 UI)
256
- // 4. 易于维护(修改逻辑不影响 UI,修改 UI 不影响逻辑)
257
- // 5. 可以单独测试逻辑(不依赖 UI)
258
- ```
259
-
260
- ### 对比总结
261
-
262
- | 特性 | 没有 UI 分离 | IOC + UI 分离 |
263
- | -------------- | --------------------------- | --------------------------- |
264
- | **职责清晰度** | ❌ UI 和逻辑混在一起 | ✅ UI 只负责渲染,逻辑独立 |
265
- | **可测试性** | ❌ 必须渲染组件才能测试 | ✅ 逻辑可以独立测试 |
266
- | **可复用性** | ❌ 逻辑无法复用 | ✅ 逻辑可以在多个组件中复用 |
267
- | **可维护性** | ❌ 修改逻辑影响 UI | ✅ UI 和逻辑独立修改 |
268
- | **测试速度** | ❌ 慢(需要渲染 UI) | ✅ 快(纯逻辑测试) |
269
- | **测试复杂度** | ❌ 高(需要 mock 很多东西) | ✅ 低(只需 mock 接口) |
270
-
271
- ---
272
-
273
- ## ❓ 两个关键问题
274
-
275
- ### 问题 1:为什么一个实现类也需要一个接口?
276
-
277
- 很多开发者会问:"如果 `UserService` 只有一个实现类,为什么还要定义 `UserServiceInterface` 接口?"
278
-
279
- #### 答案:为了可测试性和灵活性
280
-
281
- ```typescript
282
- // ❌ 没有接口:难以测试
283
- class UserComponent {
284
- constructor(
285
- @inject(UserService) private userService: UserService // 依赖具体实现
286
- ) {}
287
- }
288
-
289
- // 测试时:
290
- describe('UserComponent', () => {
291
- it('should load user', () => {
292
- // ❌ 问题:无法 mock UserService
293
- // UserService 有很多依赖(API、Storage、Router 等)
294
- // 需要创建所有这些依赖才能创建 UserService
295
-
296
- const userApi = new UserApi(); // 需要创建
297
- const storage = new Storage(); // 需要创建
298
- const router = new Router(); // 需要创建
299
- const config = new AppConfig(); // 需要创建
300
-
301
- const userService = new UserService(userApi, config, storage, router);
302
- const component = new UserComponent(userService);
303
-
304
- // 😰 太复杂了!
305
- });
306
- });
307
-
308
- // ✅ 有接口:易于测试
309
- class UserComponent {
310
- constructor(
311
- @inject('UserServiceInterface') // 依赖接口
312
- private userService: UserServiceInterface
313
- ) {}
314
- }
315
-
316
- // 测试时:
317
- describe('UserComponent', () => {
318
- it('should load user', () => {
319
- // ✅ 只需要 mock 接口
320
- const mockUserService: UserServiceInterface = {
321
- getUser: jest.fn().mockResolvedValue({ name: 'John' }),
322
- logout: jest.fn(),
323
- isAuthenticated: jest.fn().mockReturnValue(true)
324
- };
325
-
326
- const component = new UserComponent(mockUserService);
327
-
328
- // ✅ 简单清晰!
329
- });
330
- });
331
- ```
332
-
333
- **关键优势:**
334
-
335
- 1. **测试简单** - 只需 mock 接口方法,不需要创建真实依赖
336
- 2. **隔离性** - 测试 UserComponent 时不需要关心 UserService 的实现细节
337
- 3. **灵活性** - 将来可以轻松替换实现(如添加 MockUserService、CacheUserService 等)
338
- 4. **解耦** - 组件只依赖接口,不依赖具体实现
339
-
340
- **即使只有一个实现类,接口也是必需的,因为:**
341
-
342
- - ✅ 测试时需要 mock
343
- - ✅ 将来可能有新的实现
344
- - ✅ 组件不应该依赖具体实现
345
- - ✅ 接口是契约,实现是细节
346
-
347
- ### 问题 2:为什么一个简单的 UI 组件也需要 UI 分离?
348
-
349
- 很多开发者会问:"我的组件很简单,只是显示一个用户名,为什么还要分离?"
350
-
351
- #### 答案:为了可测试性和未来的扩展性
352
-
353
- ```typescript
354
- // ❌ 简单组件,没有分离
355
- function UserName() {
356
- const [name, setName] = useState('');
357
-
358
- useEffect(() => {
359
- // 😰 即使很简单,逻辑也混在 UI 中
360
- fetch('/api/user')
361
- .then(res => res.json())
362
- .then(data => setName(data.name));
363
- }, []);
364
-
365
- return <span>{name}</span>;
366
- }
367
-
368
- // 问题:
369
- // 1. 无法测试逻辑(必须渲染组件)
370
- // 2. 如果逻辑变复杂了怎么办?(加缓存、加错误处理等)
371
- // 3. 如果其他组件也需要用户名怎么办?(复制粘贴?)
372
-
373
-
374
- // ✅ 简单组件,但有分离
375
- // 1. 服务(逻辑层)
376
- @injectable()
377
- export class UserService implements UserServiceInterface {
378
- constructor(@inject(UserApi) private api: UserApi) {}
379
-
380
- async getUserName(): Promise<string> {
381
- const user = await this.api.getUserInfo();
382
- return user.name;
383
- }
384
- }
385
-
386
- // 2. UI 组件(UI 层)
387
- function UserName() {
388
- const userService = useIOC('UserServiceInterface');
389
- const [name, setName] = useState('');
390
-
391
- useEffect(() => {
392
- userService.getUserName().then(setName);
393
- }, []);
394
-
395
- return <span>{name}</span>;
396
- }
397
-
398
- // 优势:
399
- // 1. ✅ 可以独立测试 getUserName 逻辑
400
- // 2. ✅ 将来逻辑变复杂时,只需修改 UserService
401
- // 3. ✅ 其他组件可以复用 UserService
402
- // 4. ✅ UI 组件保持简单,只负责渲染
403
- ```
404
-
405
- **关键场景:逻辑逐步变复杂**
406
-
407
- ```typescript
408
- // ❌ 没有分离:逻辑变复杂后,组件变得臃肿
409
- function UserName() {
410
- const [name, setName] = useState('');
411
- const [loading, setLoading] = useState(false);
412
- const [error, setError] = useState(null);
413
-
414
- useEffect(() => {
415
- setLoading(true);
416
-
417
- // 😰 加缓存
418
- const cached = localStorage.getItem('userName');
419
- if (cached) {
420
- setName(cached);
421
- setLoading(false);
422
- return;
423
- }
424
-
425
- // 😰 加错误处理
426
- fetch('/api/user')
427
- .then(res => {
428
- if (!res.ok) throw new Error('Failed');
429
- return res.json();
430
- })
431
- .then(data => {
432
- setName(data.name);
433
- localStorage.setItem('userName', data.name);
434
- })
435
- .catch(err => setError(err))
436
- .finally(() => setLoading(false));
437
- }, []);
438
-
439
- // 😰 组件变复杂了
440
- if (loading) return <span>Loading...</span>;
441
- if (error) return <span>Error</span>;
442
- return <span>{name}</span>;
443
- }
444
-
445
-
446
- // ✅ 有分离:逻辑变复杂后,只需修改服务
447
- @injectable()
448
- export class UserService implements UserServiceInterface {
449
- constructor(
450
- @inject(UserApi) private api: UserApi,
451
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage
452
- ) {}
453
-
454
- // ✅ 逻辑在服务中,清晰明了
455
- async getUserName(): Promise<string> {
456
- // 缓存逻辑
457
- const cached = this.storage.getItem('userName');
458
- if (cached) return cached;
459
-
460
- // API 调用
461
- const user = await this.api.getUserInfo();
462
-
463
- // 缓存
464
- this.storage.setItem('userName', user.name);
465
-
466
- return user.name;
467
- }
468
- }
469
-
470
- // ✅ UI 组件保持简单
471
- function UserName() {
472
- const userService = useIOC('UserServiceInterface');
473
- const [name, setName] = useState('');
474
- const [loading, setLoading] = useState(false);
475
-
476
- useEffect(() => {
477
- setLoading(true);
478
- userService.getUserName()
479
- .then(setName)
480
- .finally(() => setLoading(false));
481
- }, []);
482
-
483
- if (loading) return <span>Loading...</span>;
484
- return <span>{name}</span>;
485
- }
486
- ```
487
-
488
- **总结:即使组件很简单,也要分离,因为:**
489
-
490
- - ✅ **现在简单,不代表将来简单** - 需求会变化
491
- - ✅ **逻辑可以复用** - 其他组件可能也需要
492
- - ✅ **易于测试** - 逻辑可以独立测试
493
- - ✅ **职责清晰** - UI 只负责渲染,逻辑独立
494
- - ✅ **易于维护** - 修改逻辑不影响 UI
495
-
496
- ---
497
-
498
- ## 🛠️ 项目中的实现
499
-
500
- ### 1. 文件结构
501
-
502
- ```
503
- src/
504
- ├── base/
505
- │ ├── port/ # 接口定义层
506
- │ │ ├── UserServiceInterface.ts
507
- │ │ ├── I18nServiceInterface.ts
508
- │ │ └── RouteServiceInterface.ts
509
- │ └── services/ # 服务实现层
510
- │ ├── UserService.ts
511
- │ ├── I18nService.ts
512
- │ └── RouteService.ts
513
- ├── core/
514
- │ ├── clientIoc/
515
- │ │ ├── ClientIOC.ts # IOC 容器
516
- │ │ └── ClientIOCRegister.ts # 注册器
517
- │ └── globals.ts # 全局实例
518
- ├── uikit/
519
- │ ├── hooks/
520
- │ │ └── useIOC.ts # React Hook
521
- │ └── contexts/
522
- │ └── IOCContext.tsx # React Context
523
- └── config/
524
- └── IOCIdentifier.ts # 标识符定义
525
-
526
- ```
527
-
528
- ### 2. IOC 标识符定义
529
-
530
- ```typescript
531
- // config/IOCIdentifier.ts
532
- export interface IOCIdentifierMap {
533
- AppConfig: AppConfig;
534
- Logger: LoggerInterface;
535
- LocalStorageEncrypt: SyncStorageInterface<string, string>;
536
- UserServiceInterface: UserServiceInterface;
537
- I18nServiceInterface: I18nServiceInterface;
538
- RouteServiceInterface: RouteServiceInterface;
539
- }
540
-
541
- export const IOCIdentifier = {
542
- AppConfig: 'AppConfig',
543
- Logger: 'Logger',
544
- LocalStorageEncrypt: 'LocalStorageEncrypt',
545
- UserServiceInterface: 'UserServiceInterface',
546
- I18nServiceInterface: 'I18nServiceInterface',
547
- RouteServiceInterface: 'RouteServiceInterface'
548
- } as const;
549
- ```
550
-
551
- ### 3. 服务注册
552
-
553
- ```typescript
554
- // src/core/clientIoc/ClientIOCRegister.ts
555
- export class ClientIOCRegister implements IOCRegisterInterface {
556
- constructor(protected options: IocRegisterOptions) {}
557
-
558
- /**
559
- * 注册全局服务
560
- */
561
- protected registerGlobals(ioc: IOCContainerInterface): void {
562
- const { appConfig } = this.options;
563
- const { dialogHandler, localStorageEncrypt, JSON, logger } = globals;
564
-
565
- // ✅ 注册全局实例
566
- ioc.bind(IOCIdentifier.JSONSerializer, JSON);
567
- ioc.bind(IOCIdentifier.Logger, logger);
568
- ioc.bind(IOCIdentifier.AppConfig, appConfig);
569
- ioc.bind(IOCIdentifier.LocalStorageEncrypt, localStorageEncrypt);
570
- }
571
-
572
- /**
573
- * 注册业务服务
574
- */
575
- protected registerImplement(ioc: IOCContainerInterface): void {
576
- // ✅ 注册服务实现
577
- ioc.bind(
578
- IOCIdentifier.I18nServiceInterface,
579
- new I18nService(this.options.pathname)
580
- );
581
-
582
- ioc.bind(IOCIdentifier.RouteServiceInterface, new RouteService(/* ... */));
583
-
584
- // ✅ 服务可以依赖其他服务
585
- ioc.bind(IOCIdentifier.UserServiceInterface, ioc.get(UserService));
586
- }
587
-
588
- /**
589
- * 注册入口
590
- */
591
- register(ioc: IOCContainerInterface): void {
592
- this.registerGlobals(ioc);
593
- this.registerImplement(ioc);
594
- }
595
- }
596
- ```
597
-
598
- ### 4. 创建 IOC 容器
599
-
600
- ```typescript
601
- // src/core/clientIoc/ClientIOC.ts
602
- import { createIOCFunction } from '@qlover/corekit-bridge';
603
- import { InversifyContainer } from '@/base/cases/InversifyContainer';
604
- import { ClientIOCRegister } from './ClientIOCRegister';
605
-
606
- export const clientIOC = {
607
- create(options: IocRegisterOptions) {
608
- // 创建容器
609
- const container = new InversifyContainer();
610
-
611
- // 创建 IOC 函数
612
- const IOC = createIOCFunction(container);
613
-
614
- // 注册服务
615
- const register = new ClientIOCRegister(options);
616
- register.register(container, IOC);
617
-
618
- return IOC;
619
- }
620
- };
621
- ```
622
-
623
- ### 5. Bootstrap 中初始化
624
-
625
- ```typescript
626
- // src/core/bootstraps/BootstrapClient.ts
627
- export class BootstrapClient {
628
- static async main(args: BootstrapClientArgs) {
629
- const { root, bootHref, ioc } = args;
630
-
631
- // ✅ 创建 IOC 容器
632
- const IOC = ioc.create({
633
- pathname: bootHref,
634
- appConfig: appConfig
635
- });
636
-
637
- // Bootstrap 中使用 IOC
638
- const bootstrap = new Bootstrap({
639
- root,
640
- logger,
641
- ioc: {
642
- manager: IOC,
643
- register: iocRegister
644
- }
645
- });
646
-
647
- await bootstrap.initialize();
648
- await bootstrap.start();
649
- }
650
- }
651
- ```
652
-
653
- ---
654
-
655
- ## 📝 使用方式
656
-
657
- ### 1. 定义接口(Port)
658
-
659
- ```typescript
660
- // src/base/port/UserServiceInterface.ts
661
- export interface UserServiceInterface {
662
- getUser(): Promise<UserInfo>;
663
- login(username: string, password: string): Promise<void>;
664
- logout(): Promise<void>;
665
- isAuthenticated(): boolean;
666
- }
667
- ```
668
-
669
- ### 2. 实现服务
670
-
671
- ```typescript
672
- // src/base/services/UserService.ts
673
- import { injectable, inject } from 'inversify';
674
-
675
- @injectable()
676
- export class UserService implements UserServiceInterface {
677
- constructor(
678
- @inject(UserApi) private api: UserApi,
679
- @inject(IOCIdentifier.AppConfig) private config: AppConfig,
680
- @inject(IOCIdentifier.LocalStorageEncrypt) private storage: Storage,
681
- @inject(IOCIdentifier.RouteServiceInterface) private router: RouteService
682
- ) {}
683
-
684
- async getUser(): Promise<UserInfo> {
685
- const token = this.storage.getItem(this.config.userTokenStorageKey);
686
- if (!token) throw new Error('No token');
687
-
688
- return await this.api.getUserInfo(token);
689
- }
690
-
691
- async login(username: string, password: string): Promise<void> {
692
- const response = await this.api.login({ username, password });
693
- this.storage.setItem(this.config.userTokenStorageKey, response.token);
694
- }
695
-
696
- async logout(): Promise<void> {
697
- this.storage.removeItem(this.config.userTokenStorageKey);
698
- await this.router.push('/login');
699
- }
700
-
701
- isAuthenticated(): boolean {
702
- return !!this.storage.getItem(this.config.userTokenStorageKey);
703
- }
704
- }
705
- ```
706
-
707
- ### 3. 在 UI 组件中使用
708
-
709
- ```typescript
710
- // src/pages/UserProfile.tsx
711
- import { useIOC } from '@/uikit/hooks/useIOC';
712
-
713
- function UserProfile() {
714
- // ✅ 从 IOC 容器获取服务
715
- const userService = useIOC('UserServiceInterface');
716
- const [user, setUser] = useState<UserInfo | null>(null);
717
-
718
- useEffect(() => {
719
- userService.getUser().then(setUser);
720
- }, []);
721
-
722
- const handleLogout = () => {
723
- userService.logout();
724
- };
725
-
726
- // ✅ UI 只负责渲染
727
- return (
728
- <div>
729
- <h1>{user?.name}</h1>
730
- <button onClick={handleLogout}>Logout</button>
731
- </div>
732
- );
733
- }
734
- ```
735
-
736
- ### 4. 在服务中使用其他服务
737
-
738
- ```typescript
739
- // src/base/services/ProfileService.ts
740
- @injectable()
741
- export class ProfileService {
742
- constructor(
743
- // ✅ 服务可以依赖其他服务
744
- @inject(IOCIdentifier.UserServiceInterface)
745
- private userService: UserServiceInterface,
746
- @inject(IOCIdentifier.I18nServiceInterface)
747
- private i18n: I18nServiceInterface
748
- ) {}
749
-
750
- async getUserProfile(): Promise<string> {
751
- const user = await this.userService.getUser();
752
- return this.i18n.t('profile.welcome', { name: user.name });
753
- }
754
- }
755
- ```
756
-
757
- ---
758
-
759
- ## 🧪 测试
760
-
761
- ### 核心优势:UI 和逻辑可以独立测试,也可以组合测试
762
-
763
- #### 1. 独立测试逻辑(不需要 UI)
764
-
765
- ```typescript
766
- // __tests__/src/base/services/UserService.test.ts
767
- import { describe, it, expect, vi, beforeEach } from 'vitest';
768
- import { UserService } from '@/base/services/UserService';
769
-
770
- describe('UserService (逻辑测试)', () => {
771
- let userService: UserService;
772
- let mockApi: any;
773
- let mockStorage: any;
774
- let mockRouter: any;
775
- let mockConfig: any;
776
-
777
- beforeEach(() => {
778
- // ✅ 只需 mock 接口
779
- mockApi = {
780
- getUserInfo: vi.fn(),
781
- login: vi.fn()
782
- };
783
-
784
- mockStorage = {
785
- getItem: vi.fn(),
786
- setItem: vi.fn(),
787
- removeItem: vi.fn()
788
- };
789
-
790
- mockRouter = {
791
- push: vi.fn()
792
- };
793
-
794
- mockConfig = {
795
- userTokenStorageKey: '__test_token__'
796
- };
797
-
798
- // ✅ 创建服务
799
- userService = new UserService(mockApi, mockConfig, mockStorage, mockRouter);
800
- });
801
-
802
- it('should get user when token exists', async () => {
803
- // ✅ 设置 mock 返回值
804
- mockStorage.getItem.mockReturnValue('test-token');
805
- mockApi.getUserInfo.mockResolvedValue({ name: 'John' });
806
-
807
- // ✅ 测试逻辑
808
- const user = await userService.getUser();
809
-
810
- // ✅ 验证结果
811
- expect(user.name).toBe('John');
812
- expect(mockStorage.getItem).toHaveBeenCalledWith('__test_token__');
813
- expect(mockApi.getUserInfo).toHaveBeenCalledWith('test-token');
814
- });
815
-
816
- it('should throw error when no token', async () => {
817
- // ✅ 测试错误场景
818
- mockStorage.getItem.mockReturnValue(null);
819
-
820
- await expect(userService.getUser()).rejects.toThrow('No token');
821
- });
822
-
823
- it('should login and save token', async () => {
824
- // ✅ 测试登录逻辑
825
- mockApi.login.mockResolvedValue({ token: 'new-token' });
826
-
827
- await userService.login('user', 'pass');
828
-
829
- expect(mockApi.login).toHaveBeenCalledWith({
830
- username: 'user',
831
- password: 'pass'
832
- });
833
- expect(mockStorage.setItem).toHaveBeenCalledWith(
834
- '__test_token__',
835
- 'new-token'
836
- );
837
- });
838
-
839
- it('should logout and clear token', async () => {
840
- // ✅ 测试登出逻辑
841
- await userService.logout();
842
-
843
- expect(mockStorage.removeItem).toHaveBeenCalledWith('__test_token__');
844
- expect(mockRouter.push).toHaveBeenCalledWith('/login');
845
- });
846
- });
847
-
848
- // ✅✅✅ 优势:
849
- // 1. 不需要渲染 UI
850
- // 2. 测试运行快(纯逻辑)
851
- // 3. 易于 mock(只需 mock 接口)
852
- // 4. 可以测试所有边界情况
853
- ```
854
-
855
- #### 2. 独立测试 UI(不需要真实逻辑)
856
-
857
- ```typescript
858
- // __tests__/src/pages/UserProfile.test.tsx
859
- import { describe, it, expect, vi } from 'vitest';
860
- import { render, screen, waitFor, fireEvent } from '@testing-library/react';
861
- import { UserProfile } from '@/pages/UserProfile';
862
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
863
-
864
- describe('UserProfile (UI 测试)', () => {
865
- it('should display user name', async () => {
866
- // ✅ Mock 服务
867
- const mockUserService = {
868
- getUser: vi.fn().mockResolvedValue({ name: 'John Doe' }),
869
- logout: vi.fn(),
870
- isAuthenticated: vi.fn().mockReturnValue(true)
871
- };
872
-
873
- const mockIOC = (identifier: string) => {
874
- if (identifier === 'UserServiceInterface') return mockUserService;
875
- };
876
-
877
- // ✅ 渲染组件
878
- render(
879
- <IOCProvider value={mockIOC}>
880
- <UserProfile />
881
- </IOCProvider>
882
- );
883
-
884
- // ✅ 验证 UI
885
- await waitFor(() => {
886
- expect(screen.getByText('John Doe')).toBeInTheDocument();
887
- });
888
- });
889
-
890
- it('should call logout when button clicked', async () => {
891
- const mockUserService = {
892
- getUser: vi.fn().mockResolvedValue({ name: 'John' }),
893
- logout: vi.fn(),
894
- isAuthenticated: vi.fn().mockReturnValue(true)
895
- };
896
-
897
- const mockIOC = (identifier: string) => {
898
- if (identifier === 'UserServiceInterface') return mockUserService;
899
- };
900
-
901
- render(
902
- <IOCProvider value={mockIOC}>
903
- <UserProfile />
904
- </IOCProvider>
905
- );
906
-
907
- // ✅ 模拟用户操作
908
- const logoutButton = screen.getByText('Logout');
909
- fireEvent.click(logoutButton);
910
-
911
- // ✅ 验证服务调用
912
- expect(mockUserService.logout).toHaveBeenCalled();
913
- });
914
- });
915
-
916
- // ✅✅✅ 优势:
917
- // 1. 不需要真实的服务实现
918
- // 2. 可以轻松模拟各种场景
919
- // 3. UI 测试专注于 UI 逻辑
920
- ```
921
-
922
- #### 3. 组合测试(UI + 逻辑)
923
-
924
- ```typescript
925
- // __tests__/src/integration/UserFlow.test.tsx
926
- import { describe, it, expect } from 'vitest';
927
- import { render, screen, waitFor, fireEvent } from '@testing-library/react';
928
- import { UserProfile } from '@/pages/UserProfile';
929
- import { UserService } from '@/base/services/UserService';
930
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
931
-
932
- describe('User Flow (组合测试)', () => {
933
- it('should complete user login flow', async () => {
934
- // ✅ 使用真实的服务实现
935
- const mockApi = {
936
- getUserInfo: vi.fn().mockResolvedValue({ name: 'John' }),
937
- login: vi.fn().mockResolvedValue({ token: 'test-token' })
938
- };
939
-
940
- const mockStorage = {
941
- getItem: vi.fn(),
942
- setItem: vi.fn(),
943
- removeItem: vi.fn()
944
- };
945
-
946
- const mockRouter = { push: vi.fn() };
947
- const mockConfig = { userTokenStorageKey: '__token__' };
948
-
949
- // ✅ 创建真实服务
950
- const userService = new UserService(
951
- mockApi,
952
- mockConfig,
953
- mockStorage,
954
- mockRouter
955
- );
956
-
957
- const mockIOC = (identifier: string) => {
958
- if (identifier === 'UserServiceInterface') return userService;
959
- };
960
-
961
- // ✅ 渲染真实 UI
962
- render(
963
- <IOCProvider value={mockIOC}>
964
- <UserProfile />
965
- </IOCProvider>
966
- );
967
-
968
- // ✅ 测试完整流程
969
- await waitFor(() => {
970
- expect(screen.getByText('John')).toBeInTheDocument();
971
- });
972
-
973
- // ✅ 点击登出
974
- fireEvent.click(screen.getByText('Logout'));
975
-
976
- // ✅ 验证整个流程
977
- expect(mockStorage.removeItem).toHaveBeenCalledWith('__token__');
978
- expect(mockRouter.push).toHaveBeenCalledWith('/login');
979
- });
980
- });
981
-
982
- // ✅✅✅ 优势:
983
- // 1. 测试真实的用户流程
984
- // 2. 可以发现 UI 和逻辑的集成问题
985
- // 3. 更接近真实使用场景
986
- ```
987
-
988
- ### 测试策略总结
989
-
990
- ```
991
- ┌─────────────────────────────────────────┐
992
- │ 测试金字塔 │
993
- │ │
994
- │ △ UI 测试 (少量) │
995
- │ ╱ ╲ │
996
- │ ╱ ╲ │
997
- │ ╱ ╲ │
998
- │ ╱───────╲ 组合测试 (适量) │
999
- │ ╱ ╲ │
1000
- │╱═══════════╲ 逻辑测试 (大量) │
1001
- │ │
1002
- │ 逻辑测试:快速、稳定、覆盖全面 │
1003
- │ 组合测试:验证集成、发现问题 │
1004
- │ UI 测试:验证用户交互 │
1005
- └─────────────────────────────────────────┘
1006
- ```
1007
-
1008
- **推荐测试比例:**
1009
-
1010
- - 70% 逻辑测试(UserService.test.ts)
1011
- - 20% 组合测试(UserFlow.test.tsx)
1012
- - 10% UI 测试(UserProfile.test.tsx)
1013
-
1014
- ---
1015
-
1016
- ## 💎 最佳实践
1017
-
1018
- ### 1. ✅ 始终定义接口
1019
-
1020
- ```typescript
1021
- // ✅ 好的做法:先定义接口
1022
- export interface UserServiceInterface {
1023
- getUser(): Promise<UserInfo>;
1024
- logout(): Promise<void>;
1025
- }
1026
-
1027
- // 然后实现
1028
- @injectable()
1029
- export class UserService implements UserServiceInterface {
1030
- // ...
1031
- }
1032
-
1033
- // ❌ 不好的做法:直接写实现
1034
- @injectable()
1035
- export class UserService {
1036
- // 没有接口,难以测试
1037
- }
1038
- ```
1039
-
1040
- ### 2. ✅ UI 和逻辑完全分离
1041
-
1042
- ```typescript
1043
- // ✅ 好的做法:UI 只负责渲染
1044
- function UserProfile() {
1045
- const userService = useIOC('UserServiceInterface');
1046
- const [user, setUser] = useState(null);
1047
-
1048
- useEffect(() => {
1049
- userService.getUser().then(setUser);
1050
- }, []);
1051
-
1052
- return <div>{user?.name}</div>;
1053
- }
1054
-
1055
- // ❌ 不好的做法:逻辑混在 UI 中
1056
- function UserProfile() {
1057
- const [user, setUser] = useState(null);
1058
-
1059
- useEffect(() => {
1060
- fetch('/api/user')
1061
- .then(res => res.json())
1062
- .then(setUser);
1063
- }, []);
1064
-
1065
- return <div>{user?.name}</div>;
1066
- }
1067
- ```
1068
-
1069
- ### 3. ✅ 使用依赖注入
1070
-
1071
- ```typescript
1072
- // ✅ 好的做法:通过构造函数注入
1073
- @injectable()
1074
- export class UserService {
1075
- constructor(
1076
- @inject(UserApi) private api: UserApi,
1077
- @inject(IOCIdentifier.AppConfig) private config: AppConfig
1078
- ) {}
1079
- }
1080
-
1081
- // ❌ 不好的做法:直接创建依赖
1082
- export class UserService {
1083
- private api = new UserApi();
1084
- private config = new AppConfig();
1085
- }
1086
- ```
1087
-
1088
- ### 4. ✅ 服务单一职责
1089
-
1090
- ```typescript
1091
- // ✅ 好的做法:每个服务只负责一件事
1092
- @injectable()
1093
- export class UserService {
1094
- // 只负责用户相关逻辑
1095
- async getUser() {
1096
- /* ... */
1097
- }
1098
- async logout() {
1099
- /* ... */
1100
- }
1101
- }
1102
-
1103
- @injectable()
1104
- export class ThemeService {
1105
- // 只负责主题相关逻辑
1106
- setTheme() {
1107
- /* ... */
1108
- }
1109
- getTheme() {
1110
- /* ... */
1111
- }
1112
- }
1113
-
1114
- // ❌ 不好的做法:一个服务做多件事
1115
- @injectable()
1116
- export class ApplicationService {
1117
- async getUser() {
1118
- /* ... */
1119
- }
1120
- setTheme() {
1121
- /* ... */
1122
- }
1123
- changeLanguage() {
1124
- /* ... */
1125
- }
1126
- // 太多职责!
1127
- }
1128
- ```
1129
-
1130
- ### 5. ✅ 依赖接口,不依赖实现
1131
-
1132
- ```typescript
1133
- // ✅ 好的做法
1134
- @injectable()
1135
- export class UserService {
1136
- constructor(
1137
- @inject('UserApiInterface') private api: UserApiInterface // 接口
1138
- ) {}
1139
- }
1140
-
1141
- // ❌ 不好的做法
1142
- @injectable()
1143
- export class UserService {
1144
- constructor(
1145
- @inject(UserApi) private api: UserApi // 具体实现
1146
- ) {}
1147
- }
1148
- ```
1149
-
1150
- ### 6. ✅ 即使简单也要分离
1151
-
1152
- ```typescript
1153
- // ✅ 好的做法:即使很简单也分离
1154
- @injectable()
1155
- export class CounterService {
1156
- private count = 0;
1157
-
1158
- increment() {
1159
- this.count++;
1160
- return this.count;
1161
- }
1162
- }
1163
-
1164
- function Counter() {
1165
- const counterService = useIOC('CounterService');
1166
- const [count, setCount] = useState(0);
1167
-
1168
- const handleClick = () => {
1169
- setCount(counterService.increment());
1170
- };
1171
-
1172
- return <button onClick={handleClick}>{count}</button>;
1173
- }
1174
-
1175
- // ❌ 不好的做法:简单逻辑也混在 UI 中
1176
- function Counter() {
1177
- const [count, setCount] = useState(0);
1178
-
1179
- return (
1180
- <button onClick={() => setCount(count + 1)}>
1181
- {count}
1182
- </button>
1183
- );
1184
- }
1185
- ```
1186
-
1187
- ### 7. ✅ 编写全面的测试
1188
-
1189
- ```typescript
1190
- // ✅ 好的做法:逻辑测试 + UI 测试 + 组合测试
1191
- describe('UserService (逻辑)', () => {
1192
- it('should get user', async () => {
1193
- /* ... */
1194
- });
1195
- it('should handle error', async () => {
1196
- /* ... */
1197
- });
1198
- });
1199
-
1200
- describe('UserProfile (UI)', () => {
1201
- it('should display user', async () => {
1202
- /* ... */
1203
- });
1204
- });
1205
-
1206
- describe('User Flow (组合)', () => {
1207
- it('should complete flow', async () => {
1208
- /* ... */
1209
- });
1210
- });
1211
-
1212
- // ❌ 不好的做法:只有 UI 测试
1213
- describe('UserProfile', () => {
1214
- it('should work', async () => {
1215
- // 只测 UI,逻辑没有测试
1216
- });
1217
- });
1218
- ```
1219
-
1220
- ### 8. ✅ 使用类型安全的标识符
1221
-
1222
- ```typescript
1223
- // ✅ 好的做法:类型安全的标识符
1224
- const userService = useIOC('UserServiceInterface');
1225
- // TypeScript 知道 userService 的类型
1226
-
1227
- // ❌ 不好的做法:字符串字面量
1228
- const userService = useIOC('UserService');
1229
- // 容易拼写错误,没有类型检查
1230
- ```
1231
-
1232
- ---
1233
-
1234
- ## ❓ 常见问题
1235
-
1236
- ### Q1: IOC 会增加复杂度吗?
1237
-
1238
- **A:** 短期看可能增加复杂度,但长期看大大降低复杂度:
1239
-
1240
- **短期(小项目):**
1241
-
1242
- - 需要定义接口
1243
- - 需要注册服务
1244
- - 需要学习 IOC 概念
1245
-
1246
- **长期(项目变大):**
1247
-
1248
- - ✅ 易于测试(节省大量测试时间)
1249
- - ✅ 易于维护(清晰的依赖关系)
1250
- - ✅ 易于扩展(添加新功能很简单)
1251
- - ✅ 团队协作(职责清晰)
1252
-
1253
- ### Q2: 所有组件都要用 IOC 吗?
1254
-
1255
- **A:** 不一定,但建议:
1256
-
1257
- **需要使用 IOC 的场景:**
1258
-
1259
- - ✅ 包含业务逻辑的组件
1260
- - ✅ 需要调用 API 的组件
1261
- - ✅ 需要访问 Storage 的组件
1262
- - ✅ 需要测试的组件
1263
-
1264
- **可以不用 IOC 的场景:**
1265
-
1266
- - 纯展示组件(只接收 props)
1267
- - 非常简单的 UI 组件(如 Button、Icon)
1268
-
1269
- ### Q3: 为什么不直接 import 服务?
1270
-
1271
- **A:**
1272
-
1273
- ```typescript
1274
- // ❌ 直接 import
1275
- import { userService } from '@/services/UserService';
1276
-
1277
- function UserProfile() {
1278
- // 问题:
1279
- // 1. userService 是单例,无法测试时替换
1280
- // 2. userService 的依赖在模块加载时就创建了
1281
- // 3. 难以 mock
1282
- }
1283
-
1284
- // ✅ 使用 IOC
1285
- function UserProfile() {
1286
- const userService = useIOC('UserServiceInterface');
1287
-
1288
- // 优势:
1289
- // 1. 测试时可以提供 mock 实现
1290
- // 2. 依赖由容器管理,按需创建
1291
- // 3. 易于 mock
1292
- }
1293
- ```
1294
-
1295
- ### Q4: 如何测试使用 IOC 的组件?
1296
-
1297
- **A:** 提供 mock IOC:
1298
-
1299
- ```typescript
1300
- const mockIOC = (identifier: string) => {
1301
- if (identifier === 'UserServiceInterface') {
1302
- return mockUserService;
1303
- }
1304
- // ... 其他服务
1305
- };
1306
-
1307
- render(
1308
- <IOCProvider value={mockIOC}>
1309
- <UserProfile />
1310
- </IOCProvider>
1311
- );
1312
- ```
1313
-
1314
- ### Q5: IOC 和 Context 有什么区别?
1315
-
1316
- **A:**
1317
-
1318
- | 特性 | React Context | IOC 容器 |
1319
- | ------------ | ---------------- | ------------ |
1320
- | **作用域** | React 组件树 | 全局 |
1321
- | **依赖管理** | ❌ 无 | ✅ 有 |
1322
- | **生命周期** | 组件生命周期 | 应用生命周期 |
1323
- | **测试** | ⚠️ 需要 Provider | ✅ 易于 mock |
1324
- | **类型安全** | ⚠️ 需要手动定义 | ✅ 自动推导 |
1325
-
1326
- **建议:**
1327
-
1328
- - 使用 IOC 管理服务(逻辑)
1329
- - 使用 Context 管理 UI 状态
1330
-
1331
- ---
1332
-
1333
- ## 📚 相关文档
1334
-
1335
- - [项目架构设计](./index.md) - 了解整体架构
1336
- - [Bootstrap 启动器](./bootstrap.md) - IOC 在 Bootstrap 中的应用
1337
- - [环境变量管理](./env.md) - AppConfig 的注入
1338
- - [Store 状态管理](./store.md) - 应用层如何通知 UI 层(IOC + Store)
1339
- - [测试指南](./test-guide.md) - 详细的测试策略
1340
-
1341
- ---
1342
-
1343
- ## 🎉 总结
1344
-
1345
- IOC 容器的核心价值:
1346
-
1347
- 1. **UI 分离** 🎨 - UI 就是 UI,逻辑就是逻辑
1348
- 2. **可测试性** 🧪 - 逻辑可以独立测试,UI 可以独立测试,也可以组合测试
1349
- 3. **必须接口** 🔌 - 即使只有一个实现,也需要接口(为了测试)
1350
- 4. **全面分离** 🏗️ - 即使简单组件,也要分离(为了未来)
1351
- 5. **依赖管理** 📦 - 容器统一管理所有依赖
1352
- 6. **解耦合** 🔗 - 组件不依赖具体实现
1353
- 7. **易维护** 🛠️ - 清晰的依赖关系
1354
- 8. **易扩展** 🚀 - 轻松添加新功能
1355
-
1356
- **记住两个核心原则:**
1357
-
1358
- 1. **UI 就是 UI,逻辑就是逻辑,两者必须分离!**
1359
- 2. **即使只有一个实现,也需要接口;即使组件很简单,也要分离!**
1360
-
1361
- ---
1362
-
1363
- **问题反馈:**
1364
- 如果你对 IOC 容器有任何疑问或建议,请在团队频道中讨论或提交 Issue。