@qlover/create-app 0.7.14 → 0.8.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.
- package/CHANGELOG.md +27 -0
- package/dist/configs/_common/.gitignore.template +6 -0
- package/dist/configs/_common/.prettierignore +17 -5
- package/dist/configs/_common/.vscode/settings.json +6 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/.env.template +1 -1
- package/dist/templates/next-app/README.en.md +130 -0
- package/dist/templates/next-app/README.md +114 -20
- package/dist/templates/next-app/config/Identifier/api.ts +5 -5
- package/dist/templates/next-app/config/Identifier/common/admint.table.ts +69 -0
- package/dist/templates/next-app/config/Identifier/common/common.ts +76 -0
- package/dist/templates/next-app/config/Identifier/common/index.ts +3 -0
- package/dist/templates/next-app/config/Identifier/{validator.ts → common/validators.ts} +5 -5
- package/dist/templates/next-app/config/Identifier/index.ts +2 -12
- package/dist/templates/next-app/config/Identifier/pages/index.ts +6 -0
- package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +27 -0
- package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +266 -0
- package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +293 -0
- package/dist/templates/{react-app/config/Identifier → next-app/config/Identifier/pages}/page.home.ts +15 -22
- package/dist/templates/next-app/config/Identifier/{page.login.ts → pages/page.login.ts} +28 -34
- package/dist/templates/next-app/config/Identifier/{page.register.ts → pages/page.register.ts} +30 -29
- package/dist/templates/next-app/config/adminNavs.ts +19 -0
- package/dist/templates/next-app/config/common.ts +22 -13
- package/dist/templates/next-app/config/i18n/HomeI18n.ts +5 -5
- package/dist/templates/next-app/config/i18n/admin18n.ts +61 -19
- package/dist/templates/next-app/config/i18n/i18nConfig.ts +2 -0
- package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +36 -0
- package/dist/templates/next-app/config/i18n/loginI18n.ts +22 -22
- package/dist/templates/next-app/config/i18n/register18n.ts +23 -24
- package/dist/templates/next-app/docs/en/api.md +387 -0
- package/dist/templates/next-app/docs/en/component.md +544 -0
- package/dist/templates/next-app/docs/en/database.md +496 -0
- package/dist/templates/next-app/docs/en/development-guide.md +727 -0
- package/dist/templates/next-app/docs/en/env.md +563 -0
- package/dist/templates/next-app/docs/en/i18n.md +287 -0
- package/dist/templates/next-app/docs/en/index.md +165 -0
- package/dist/templates/next-app/docs/en/page.md +457 -0
- package/dist/templates/next-app/docs/en/project-structure.md +176 -0
- package/dist/templates/next-app/docs/en/router.md +427 -0
- package/dist/templates/next-app/docs/en/theme.md +532 -0
- package/dist/templates/next-app/docs/en/validator.md +478 -0
- package/dist/templates/next-app/docs/zh/api.md +387 -0
- package/dist/templates/next-app/docs/zh/component.md +544 -0
- package/dist/templates/next-app/docs/zh/database.md +496 -0
- package/dist/templates/next-app/docs/zh/development-guide.md +727 -0
- package/dist/templates/next-app/docs/zh/env.md +563 -0
- package/dist/templates/next-app/docs/zh/i18n.md +287 -0
- package/dist/templates/next-app/docs/zh/index.md +165 -0
- package/dist/templates/next-app/docs/zh/page.md +457 -0
- package/dist/templates/next-app/docs/zh/project-structure.md +176 -0
- package/dist/templates/next-app/docs/zh/router.md +427 -0
- package/dist/templates/next-app/docs/zh/theme.md +532 -0
- package/dist/templates/next-app/docs/zh/validator.md +476 -0
- package/dist/templates/next-app/make/generateLocales.ts +19 -12
- package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +15 -0
- package/dist/templates/next-app/migrations/sql/1694244000000.sql +11 -0
- package/dist/templates/next-app/package.json +7 -3
- package/dist/templates/next-app/public/locales/en.json +172 -207
- package/dist/templates/next-app/public/locales/zh.json +172 -207
- package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +153 -0
- package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +48 -50
- package/dist/templates/next-app/src/app/[locale]/login/LoginForm.tsx +2 -2
- package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +34 -0
- package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +40 -0
- package/dist/templates/next-app/src/app/api/admin/locales/route.ts +42 -0
- package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +32 -0
- package/dist/templates/next-app/src/app/api/locales/json/route.ts +44 -0
- package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +1 -13
- package/dist/templates/next-app/src/base/cases/Datetime.ts +18 -0
- package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +12 -6
- package/dist/templates/next-app/src/base/cases/ResourceState.ts +17 -0
- package/dist/templates/next-app/src/base/cases/TranslateI18nInterface.ts +25 -0
- package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +200 -0
- package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +8 -0
- package/dist/templates/next-app/src/base/services/AdminLocalesService.ts +20 -0
- package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +26 -0
- package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +42 -0
- package/dist/templates/next-app/src/base/services/ResourceService.ts +122 -0
- package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +104 -0
- package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +38 -5
- package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +1 -1
- package/dist/templates/next-app/src/i18n/request.ts +30 -1
- package/dist/templates/next-app/src/server/PageParams.ts +2 -10
- package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +5 -0
- package/dist/templates/next-app/src/server/port/DBTableInterface.ts +2 -0
- package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +43 -0
- package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +197 -0
- package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +122 -0
- package/dist/templates/next-app/src/server/sqlBridges/SupabaseBridge.ts +60 -11
- package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +6 -0
- package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +131 -0
- package/dist/templates/next-app/src/server/validators/LoginValidator.ts +2 -5
- package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +32 -16
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +2 -1
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +28 -29
- package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +2 -1
- package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +17 -3
- package/dist/templates/next-app/src/uikit/components/BaseHeader.tsx +5 -4
- package/dist/templates/next-app/src/uikit/components/BaseLayout.tsx +5 -4
- package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +3 -2
- package/dist/templates/next-app/src/uikit/components/ComboProvider.tsx +1 -1
- package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +118 -0
- package/dist/templates/next-app/src/uikit/components/LogoutButton.tsx +5 -6
- package/dist/templates/next-app/src/uikit/components/ThemeSwitcher.tsx +1 -1
- package/dist/templates/next-app/src/uikit/components/With.tsx +2 -2
- package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +62 -0
- package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +28 -0
- package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +6 -0
- package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +8 -14
- package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +25 -0
- package/dist/templates/react-app/.prettierignore +17 -0
- package/dist/templates/react-app/README.en.md +71 -54
- package/dist/templates/react-app/README.md +35 -18
- package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +14 -0
- package/dist/templates/react-app/__tests__/__mocks__/MockAppConfit.ts +1 -1
- package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +2 -2
- package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +1 -1
- package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +45 -0
- package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +34 -0
- package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +46 -0
- package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +12 -0
- package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -2
- package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +51 -0
- package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +69 -0
- package/dist/templates/react-app/__tests__/setup/index.ts +1 -51
- package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +51 -0
- package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +115 -0
- package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +2 -2
- package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +1 -1
- package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +3 -5
- package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +13 -2
- package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +1 -1
- package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +1 -1
- package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +5 -5
- package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +1 -2
- package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +25 -15
- package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +29 -15
- package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +19 -9
- package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +153 -0
- package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +9 -7
- package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +4 -5
- package/dist/templates/react-app/__tests__/src/main.test.tsx +4 -4
- package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +68 -59
- package/dist/templates/react-app/config/IOCIdentifier.ts +8 -8
- package/dist/templates/react-app/config/Identifier/{common.error.ts → common/common.error.ts} +5 -5
- package/dist/templates/react-app/config/Identifier/{common.ts → common/common.ts} +9 -9
- package/dist/templates/react-app/config/Identifier/common/index.ts +2 -0
- package/dist/templates/react-app/config/Identifier/index.ts +1 -9
- package/dist/templates/react-app/config/Identifier/pages/index.ts +8 -0
- package/dist/templates/react-app/config/Identifier/{page.about.ts → pages/page.about.ts} +34 -26
- package/dist/templates/react-app/config/Identifier/{page.executor.ts → pages/page.executor.ts} +47 -39
- package/dist/templates/{next-app/config/Identifier → react-app/config/Identifier/pages}/page.home.ts +24 -23
- package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +102 -0
- package/dist/templates/react-app/config/Identifier/{page.jsonStorage.ts → pages/page.jsonStorage.ts} +18 -11
- package/dist/templates/react-app/config/Identifier/{page.login.ts → pages/page.login.ts} +37 -27
- package/dist/templates/react-app/config/Identifier/{page.register.ts → pages/page.register.ts} +37 -25
- package/dist/templates/react-app/config/Identifier/{page.request.ts → pages/page.request.ts} +34 -44
- package/dist/templates/react-app/config/app.router.ts +66 -69
- package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +51 -0
- package/dist/templates/react-app/config/i18n/aboutI18n.ts +42 -0
- package/dist/templates/react-app/config/i18n/executorI18n.ts +51 -0
- package/dist/templates/react-app/config/i18n/homeI18n.ts +24 -0
- package/dist/templates/react-app/config/i18n/i18nConfig.ts +30 -0
- package/dist/templates/react-app/config/i18n/identifiter18n.ts +30 -0
- package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +27 -0
- package/dist/templates/react-app/config/i18n/login18n.ts +42 -0
- package/dist/templates/react-app/config/i18n/notFoundI18n.ts +34 -0
- package/dist/templates/react-app/config/i18n/register18n.ts +40 -0
- package/dist/templates/react-app/config/i18n/request18n.ts +41 -0
- package/dist/templates/react-app/config/theme.ts +14 -4
- package/dist/templates/react-app/docs/en/bootstrap.md +1670 -341
- package/dist/templates/react-app/docs/en/development-guide.md +1021 -345
- package/dist/templates/react-app/docs/en/env.md +1132 -278
- package/dist/templates/react-app/docs/en/i18n.md +858 -147
- package/dist/templates/react-app/docs/en/index.md +733 -104
- package/dist/templates/react-app/docs/en/ioc.md +1228 -287
- package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +321 -0
- package/dist/templates/react-app/docs/en/playwright/index.md +19 -0
- package/dist/templates/react-app/docs/en/playwright/installation-summary.md +332 -0
- package/dist/templates/react-app/docs/en/playwright/overview.md +222 -0
- package/dist/templates/react-app/docs/en/playwright/quickstart.md +325 -0
- package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +340 -0
- package/dist/templates/react-app/docs/en/playwright/setup-complete.md +290 -0
- package/dist/templates/react-app/docs/en/playwright/testing-guide.md +565 -0
- package/dist/templates/react-app/docs/en/store.md +1194 -184
- package/dist/templates/react-app/docs/en/why-no-globals.md +797 -0
- package/dist/templates/react-app/docs/zh/bootstrap.md +1670 -341
- package/dist/templates/react-app/docs/zh/development-guide.md +1021 -345
- package/dist/templates/react-app/docs/zh/env.md +1132 -275
- package/dist/templates/react-app/docs/zh/i18n.md +858 -147
- package/dist/templates/react-app/docs/zh/index.md +717 -104
- package/dist/templates/react-app/docs/zh/ioc.md +1229 -287
- package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +321 -0
- package/dist/templates/react-app/docs/zh/playwright/index.md +19 -0
- package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +332 -0
- package/dist/templates/react-app/docs/zh/playwright/overview.md +222 -0
- package/dist/templates/react-app/docs/zh/playwright/quickstart.md +325 -0
- package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +340 -0
- package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +290 -0
- package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +565 -0
- package/dist/templates/react-app/docs/zh/store.md +1192 -184
- package/dist/templates/react-app/docs/zh/why-no-globals.md +797 -0
- package/dist/templates/react-app/e2e/App.spec.ts +319 -0
- package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +40 -0
- package/dist/templates/react-app/e2e/main.spec.ts +20 -0
- package/dist/templates/react-app/e2e/utils/test-helpers.ts +19 -0
- package/dist/templates/react-app/eslint.config.mjs +247 -0
- package/dist/templates/react-app/makes/eslint-utils.mjs +195 -0
- package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +26 -0
- package/dist/templates/react-app/package.json +31 -3
- package/dist/templates/react-app/playwright.config.ts +79 -0
- package/dist/templates/react-app/public/locales/en/common.json +190 -179
- package/dist/templates/react-app/public/locales/zh/common.json +190 -179
- package/dist/templates/react-app/src/App.tsx +15 -42
- package/dist/templates/react-app/src/base/apis/AiApi.ts +5 -5
- package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +1 -1
- package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +1 -1
- package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +8 -8
- package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +1 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +6 -6
- package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +1 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +12 -14
- package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +1 -1
- package/dist/templates/react-app/src/base/cases/DialogHandler.ts +5 -2
- package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +3 -3
- package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +3 -3
- package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +2 -2
- package/dist/templates/react-app/src/base/cases/RequestLogger.ts +4 -4
- package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +1 -1
- package/dist/templates/react-app/src/base/cases/ResourceState.ts +23 -0
- package/dist/templates/react-app/src/base/cases/RouterLoader.ts +4 -4
- package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +26 -0
- package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +2 -3
- package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +1 -1
- package/dist/templates/react-app/src/base/port/IOCInterface.ts +36 -0
- package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +2 -1
- package/dist/templates/react-app/src/base/port/ProcesserExecutorInterface.ts +1 -1
- package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +2 -2
- package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +9 -5
- package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +1 -1
- package/dist/templates/react-app/src/base/services/I18nService.ts +29 -29
- package/dist/templates/react-app/src/base/services/IdentifierService.ts +143 -0
- package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +3 -3
- package/dist/templates/react-app/src/base/services/RouteService.ts +27 -8
- package/dist/templates/react-app/src/base/services/UserService.ts +8 -8
- package/dist/templates/react-app/src/base/types/Page.ts +14 -2
- package/dist/templates/react-app/src/base/types/global.d.ts +1 -1
- package/dist/templates/react-app/src/core/IOC.ts +5 -46
- package/dist/templates/react-app/src/core/bootstraps/{BootstrapApp.ts → BootstrapClient.ts} +44 -17
- package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +14 -7
- package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +1 -1
- package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +1 -1
- package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +40 -0
- package/dist/templates/react-app/src/core/{IocRegisterImpl.ts → clientIoc/ClientIOCRegister.ts} +35 -24
- package/dist/templates/react-app/src/core/globals.ts +9 -9
- package/dist/templates/react-app/src/main.tsx +4 -4
- package/dist/templates/react-app/src/pages/404.tsx +6 -3
- package/dist/templates/react-app/src/pages/500.tsx +5 -2
- package/dist/templates/react-app/src/pages/NoRouteFound.tsx +5 -0
- package/dist/templates/react-app/src/pages/auth/Layout.tsx +9 -6
- package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +46 -56
- package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +46 -58
- package/dist/templates/react-app/src/pages/base/AboutPage.tsx +35 -40
- package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +51 -51
- package/dist/templates/react-app/src/pages/base/HomePage.tsx +14 -15
- package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +70 -11
- package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +24 -25
- package/dist/templates/react-app/src/pages/base/Layout.tsx +2 -2
- package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +3 -2
- package/dist/templates/react-app/src/pages/base/RequestPage.tsx +41 -59
- package/dist/templates/react-app/src/styles/css/antd-themes/{_default.css → _common/_default.css} +85 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/{dark.css → _common/dark.css} +99 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +3 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/{pink.css → _common/pink.css} +86 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/index.css +4 -3
- package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +108 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +67 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +3 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +67 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +34 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +31 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +3 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +36 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +44 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +43 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +3 -0
- package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +43 -0
- package/dist/templates/react-app/src/styles/css/page.css +4 -3
- package/dist/templates/react-app/src/styles/css/themes/_default.css +1 -0
- package/dist/templates/react-app/src/styles/css/themes/dark.css +1 -0
- package/dist/templates/react-app/src/styles/css/themes/pink.css +1 -0
- package/dist/templates/react-app/src/styles/css/zIndex.css +1 -1
- package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +3 -3
- package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +2 -2
- package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +1 -1
- package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +3 -3
- package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +35 -0
- package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +15 -11
- package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +14 -11
- package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +18 -0
- package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +13 -0
- package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +62 -0
- package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +38 -0
- package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +48 -27
- package/dist/templates/react-app/src/uikit/components/Loading.tsx +4 -2
- package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +4 -5
- package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +34 -11
- package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +9 -5
- package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +6 -3
- package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +97 -40
- package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +5 -5
- package/dist/templates/react-app/src/uikit/components/With.tsx +17 -0
- package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +17 -11
- package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +13 -0
- package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +26 -0
- package/dist/templates/react-app/src/uikit/hooks/useI18nGuard.ts +8 -11
- package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +25 -0
- package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +35 -0
- package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +3 -3
- package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -1
- package/dist/templates/react-app/tsconfig.e2e.json +21 -0
- package/dist/templates/react-app/tsconfig.json +8 -1
- package/dist/templates/react-app/tsconfig.node.json +1 -1
- package/dist/templates/react-app/tsconfig.test.json +3 -1
- package/dist/templates/react-app/vite.config.ts +50 -34
- package/package.json +2 -1
- package/dist/configs/react-app/eslint.config.js +0 -94
- package/dist/templates/next-app/config/Identifier/common.error.ts +0 -41
- package/dist/templates/next-app/config/Identifier/common.ts +0 -69
- package/dist/templates/next-app/config/Identifier/page.about.ts +0 -181
- package/dist/templates/next-app/config/Identifier/page.admin.ts +0 -48
- package/dist/templates/next-app/config/Identifier/page.executor.ts +0 -272
- package/dist/templates/next-app/config/Identifier/page.identifiter.ts +0 -39
- package/dist/templates/next-app/config/Identifier/page.jsonStorage.ts +0 -72
- package/dist/templates/next-app/config/Identifier/page.request.ts +0 -182
- package/dist/templates/next-app/docs/env.md +0 -94
- package/dist/templates/next-app/src/base/cases/ChatAction.ts +0 -21
- package/dist/templates/next-app/src/base/cases/FocusBarAction.ts +0 -36
- package/dist/templates/next-app/src/base/cases/RequestState.ts +0 -20
- package/dist/templates/next-app/src/base/port/AdminPageInterface.ts +0 -85
- package/dist/templates/next-app/src/base/port/AsyncStateInterface.ts +0 -7
- package/dist/templates/next-app/src/base/services/AdminUserService.ts +0 -45
- package/dist/templates/next-app/src/uikit/components/ChatRoot.tsx +0 -17
- package/dist/templates/next-app/src/uikit/components/chat/ChatActionInterface.ts +0 -30
- package/dist/templates/next-app/src/uikit/components/chat/ChatFocusBar.tsx +0 -65
- package/dist/templates/next-app/src/uikit/components/chat/ChatMessages.tsx +0 -59
- package/dist/templates/next-app/src/uikit/components/chat/ChatWrap.tsx +0 -28
- package/dist/templates/next-app/src/uikit/components/chat/FocusBarActionInterface.ts +0 -19
- package/dist/templates/next-app/src/uikit/hook/useMountedClient.ts +0 -17
- package/dist/templates/next-app/src/uikit/hook/useStore.ts +0 -15
- package/dist/templates/react-app/__tests__/__mocks__/I18nService.ts +0 -13
- package/dist/templates/react-app/__tests__/src/App.test.tsx +0 -139
- package/dist/templates/react-app/config/Identifier/page.identifiter.ts +0 -39
- package/dist/templates/react-app/config/i18n.ts +0 -15
- package/dist/templates/react-app/docs/en/project-structure.md +0 -434
- package/dist/templates/react-app/docs/zh/project-structure.md +0 -434
- package/dist/templates/react-app/src/base/cases/RequestState.ts +0 -20
- package/dist/templates/react-app/src/base/port/AsyncStateInterface.ts +0 -7
- package/dist/templates/react-app/src/uikit/hooks/useDocumentTitle.ts +0 -15
- package/dist/templates/react-app/src/uikit/hooks/useStore.ts +0 -15
|
@@ -1,120 +1,328 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 环境变量管理
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 📋 目录
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- [什么是环境变量管理](#-什么是环境变量管理)
|
|
6
|
+
- [为什么需要环境变量](#-为什么需要环境变量)
|
|
7
|
+
- [工作原理](#-工作原理)
|
|
8
|
+
- [项目中的实现](#-项目中的实现)
|
|
9
|
+
- [多环境配置](#-多环境配置)
|
|
10
|
+
- [环境变量注入](#-环境变量注入)
|
|
11
|
+
- [AppConfig 使用](#-appconfig-使用)
|
|
12
|
+
- [高级用法](#-高级用法)
|
|
13
|
+
- [测试配置](#-测试配置)
|
|
14
|
+
- [最佳实践](#-最佳实践)
|
|
15
|
+
- [常见问题](#-常见问题)
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
---
|
|
8
18
|
|
|
9
|
-
##
|
|
19
|
+
## 🎯 什么是环境变量管理
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
环境变量管理是 Bootstrap 架构的核心组成部分,负责在不同环境(开发、测试、生产)中使用不同的配置,并通过 **自动注入** 的方式将配置传递给应用。
|
|
22
|
+
|
|
23
|
+
### 核心流程
|
|
12
24
|
|
|
13
25
|
```
|
|
14
|
-
|
|
26
|
+
启动应用 → Vite 加载 .env 文件 → Bootstrap 初始化 → 注入环境变量到 AppConfig → IOC 容器 → 应用使用
|
|
15
27
|
```
|
|
16
28
|
|
|
17
|
-
###
|
|
29
|
+
### 关键概念
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
┌─────────────────────────────────────────────────┐
|
|
33
|
+
│ 环境变量管理系统 │
|
|
34
|
+
│ ┌───────────────────────────────────────────┐ │
|
|
35
|
+
│ │ 1. Vite --mode 选择环境 │ │
|
|
36
|
+
│ │ 2. 加载对应的 .env 文件 │ │
|
|
37
|
+
│ │ 3. envConfig 插件预处理 │ │
|
|
38
|
+
│ │ 4. Bootstrap 注入到 AppConfig │ │
|
|
39
|
+
│ │ 5. 注册到 IOC 容器 │ │
|
|
40
|
+
│ │ 6. 应用通过 IOC 获取配置 │ │
|
|
41
|
+
│ └───────────────────────────────────────────┘ │
|
|
42
|
+
└─────────────────────────────────────────────────┘
|
|
43
|
+
```
|
|
18
44
|
|
|
19
|
-
|
|
20
|
-
- **@qlover/corekit-bridge/vite-env-config**:Vite 环境变量配置插件
|
|
21
|
-
- **dotenv**:.env 文件解析
|
|
22
|
-
- **Vite**:前端构建工具
|
|
45
|
+
---
|
|
23
46
|
|
|
24
|
-
|
|
47
|
+
## 🤔 为什么需要环境变量
|
|
25
48
|
|
|
49
|
+
### 问题:硬编码配置的痛点
|
|
50
|
+
|
|
51
|
+
#### ❌ 传统方式:配置散落各处
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// ❌ 问题 1:API 地址硬编码在代码中
|
|
55
|
+
function fetchUserInfo() {
|
|
56
|
+
// 😰 开发环境和生产环境的 API 地址不同,每次切换都要改代码
|
|
57
|
+
return fetch('http://localhost:3000/api/user');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ❌ 问题 2:配置散落在各个文件
|
|
61
|
+
function saveToken(token: string) {
|
|
62
|
+
// 😰 存储键名硬编码,难以统一管理
|
|
63
|
+
localStorage.setItem('user_token', token);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ❌ 问题 3:敏感信息直接写在代码中
|
|
67
|
+
function callAI(prompt: string) {
|
|
68
|
+
// 😰 API Key 直接暴露在代码中,存在安全风险
|
|
69
|
+
return fetch('https://api.openai.com/v1/chat', {
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: 'Bearer sk-xxxxxxxxxxxx' // 😰 危险!
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ❌ 问题 4:环境切换困难
|
|
77
|
+
if (window.location.host === 'localhost:3000') {
|
|
78
|
+
// 😰 需要手动判断环境
|
|
79
|
+
apiUrl = 'http://localhost:3000/api';
|
|
80
|
+
} else if (window.location.host === 'staging.example.com') {
|
|
81
|
+
apiUrl = 'https://api.staging.example.com';
|
|
82
|
+
} else {
|
|
83
|
+
apiUrl = 'https://api.production.com';
|
|
84
|
+
}
|
|
26
85
|
```
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
86
|
+
|
|
87
|
+
**问题总结:**
|
|
88
|
+
|
|
89
|
+
- 😰 **配置散落** - 配置分散在多个文件中,难以管理
|
|
90
|
+
- 😰 **环境切换困难** - 需要手动修改代码或使用复杂的条件判断
|
|
91
|
+
- 😰 **安全风险** - 敏感信息可能被提交到代码仓库
|
|
92
|
+
- 😰 **难以测试** - 测试时需要 mock 大量硬编码的值
|
|
93
|
+
- 😰 **团队协作困难** - 每个开发者的本地配置可能不同
|
|
94
|
+
|
|
95
|
+
#### ✅ 解决方案:环境变量 + AppConfig
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// ✅ 1. 环境变量文件(不同环境不同配置)
|
|
99
|
+
// .env.localhost
|
|
100
|
+
VITE_API_BASE_URL=http://localhost:3000/api
|
|
101
|
+
VITE_AI_API_TOKEN=sk-dev-xxxxx
|
|
102
|
+
|
|
103
|
+
// .env.staging
|
|
104
|
+
VITE_API_BASE_URL=https://api.staging.example.com
|
|
105
|
+
VITE_AI_API_TOKEN=sk-staging-xxxxx
|
|
106
|
+
|
|
107
|
+
// .env.production
|
|
108
|
+
VITE_API_BASE_URL=https://api.production.com
|
|
109
|
+
VITE_AI_API_TOKEN=sk-prod-xxxxx
|
|
110
|
+
|
|
111
|
+
// ✅ 2. AppConfig 统一管理配置
|
|
112
|
+
export class AppConfig {
|
|
113
|
+
readonly feApiBaseUrl = ''; // ← 自动注入
|
|
114
|
+
readonly aiApiToken = ''; // ← 自动注入
|
|
115
|
+
readonly userTokenStorageKey = '__fe_user_token__';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ✅ 3. 通过 IOC 容器获取配置
|
|
119
|
+
@injectable()
|
|
120
|
+
export class UserService {
|
|
121
|
+
constructor(
|
|
122
|
+
@inject(IOCIdentifier.AppConfig) private config: AppConfig
|
|
123
|
+
) {}
|
|
124
|
+
|
|
125
|
+
async fetchUserInfo() {
|
|
126
|
+
// ✅ 从配置中获取 API 地址,自动适配环境
|
|
127
|
+
return fetch(`${this.config.feApiBaseUrl}/user`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ✅ 4. 运行时切换环境(无需修改代码)
|
|
132
|
+
npm run dev # localhost 环境
|
|
133
|
+
npm run dev:staging # staging 环境
|
|
134
|
+
npm run build:production # production 环境
|
|
38
135
|
```
|
|
39
136
|
|
|
40
|
-
|
|
137
|
+
**优势:**
|
|
138
|
+
|
|
139
|
+
- ✅ **集中管理** - 所有配置在 AppConfig 中统一管理
|
|
140
|
+
- ✅ **环境切换简单** - 只需切换运行命令
|
|
141
|
+
- ✅ **安全** - 敏感信息通过 `.env.local` 管理,不提交到仓库
|
|
142
|
+
- ✅ **易于测试** - 测试时可以轻松 mock AppConfig
|
|
143
|
+
- ✅ **团队协作友好** - 每个开发者可以有自己的 `.env.local`
|
|
41
144
|
|
|
42
|
-
|
|
145
|
+
---
|
|
43
146
|
|
|
44
|
-
|
|
147
|
+
## ⚙️ 工作原理
|
|
148
|
+
|
|
149
|
+
### 环境变量加载流程
|
|
45
150
|
|
|
46
151
|
```
|
|
47
|
-
|
|
152
|
+
┌────────────────────────────────────────────────────────────┐
|
|
153
|
+
│ 1. package.json: 定义启动命令 │
|
|
154
|
+
│ npm run dev → vite --mode localhost │
|
|
155
|
+
│ npm run dev:staging → vite --mode staging │
|
|
156
|
+
│ npm run build:production → vite build --mode production │
|
|
157
|
+
└──────────────────┬─────────────────────────────────────────┘
|
|
158
|
+
↓
|
|
159
|
+
┌────────────────────────────────────────────────────────────┐
|
|
160
|
+
│ 2. Vite: 根据 --mode 加载对应的 .env 文件 │
|
|
161
|
+
│ --mode localhost → .env.localhost │
|
|
162
|
+
│ --mode staging → .env.staging │
|
|
163
|
+
│ --mode production → .env.production │
|
|
164
|
+
│ │
|
|
165
|
+
│ 加载优先级:.env.local > .env.[mode] > .env │
|
|
166
|
+
└──────────────────┬─────────────────────────────────────────┘
|
|
167
|
+
↓
|
|
168
|
+
┌────────────────────────────────────────────────────────────┐
|
|
169
|
+
│ 3. vite.config.ts: envConfig 插件预处理 │
|
|
170
|
+
│ - 注入 APP_NAME、APP_VERSION 等 │
|
|
171
|
+
│ - 设置环境变量前缀 (VITE_) │
|
|
172
|
+
└──────────────────┬─────────────────────────────────────────┘
|
|
173
|
+
↓
|
|
174
|
+
┌────────────────────────────────────────────────────────────┐
|
|
175
|
+
│ 4. BootstrapClient: 初始化 Bootstrap │
|
|
176
|
+
│ envOptions: { │
|
|
177
|
+
│ target: appConfig, // 注入目标 │
|
|
178
|
+
│ source: import.meta.env, // 环境变量源 │
|
|
179
|
+
│ prefix: 'VITE_', // 前缀 │
|
|
180
|
+
│ blackList: ['env', 'userNodeEnv'] // 黑名单 │
|
|
181
|
+
│ } │
|
|
182
|
+
└──────────────────┬─────────────────────────────────────────┘
|
|
183
|
+
↓
|
|
184
|
+
┌────────────────────────────────────────────────────────────┐
|
|
185
|
+
│ 5. InjectEnv 插件: 自动注入环境变量到 AppConfig │
|
|
186
|
+
│ - VITE_APP_NAME → appConfig.appName │
|
|
187
|
+
│ - VITE_FE_API_BASE_URL → appConfig.feApiBaseUrl │
|
|
188
|
+
│ - VITE_AI_API_TOKEN → appConfig.aiApiToken │
|
|
189
|
+
└──────────────────┬─────────────────────────────────────────┘
|
|
190
|
+
↓
|
|
191
|
+
┌────────────────────────────────────────────────────────────┐
|
|
192
|
+
│ 6. IOC 容器: 注册 AppConfig │
|
|
193
|
+
│ container.bind(IOCIdentifier.AppConfig).toConstantValue │
|
|
194
|
+
└──────────────────┬─────────────────────────────────────────┘
|
|
195
|
+
↓
|
|
196
|
+
┌────────────────────────────────────────────────────────────┐
|
|
197
|
+
│ 7. 应用使用: 通过 IOC 获取配置 │
|
|
198
|
+
│ const config = useIOC('AppConfig'); │
|
|
199
|
+
│ console.log(config.feApiBaseUrl); │
|
|
200
|
+
└────────────────────────────────────────────────────────────┘
|
|
48
201
|
```
|
|
49
202
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
# 开发模式
|
|
54
|
-
vite dev --mode development
|
|
55
|
-
# 加载顺序:.env.local > .env.development > .env
|
|
203
|
+
### 命名转换规则
|
|
56
204
|
|
|
57
|
-
|
|
58
|
-
vite build --mode production
|
|
59
|
-
# 加载顺序:.env.local > .env.production > .env
|
|
205
|
+
环境变量名会自动转换为 AppConfig 属性名:
|
|
60
206
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
207
|
+
```
|
|
208
|
+
VITE_APP_NAME → appName
|
|
209
|
+
VITE_FE_API_BASE_URL → feApiBaseUrl
|
|
210
|
+
VITE_AI_API_TOKEN → aiApiToken
|
|
211
|
+
VITE_USER_TOKEN_STORAGE_KEY → userTokenStorageKey
|
|
64
212
|
```
|
|
65
213
|
|
|
66
|
-
|
|
214
|
+
**转换规则:**
|
|
67
215
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
VITE_API_BASE_URL=http://api.example.com
|
|
72
|
-
VITE_USER_TOKEN_KEY=user_token
|
|
216
|
+
1. 移除前缀 (`VITE_`)
|
|
217
|
+
2. 将下划线分隔转换为驼峰命名
|
|
218
|
+
3. 匹配 AppConfig 中的属性名
|
|
73
219
|
|
|
74
|
-
|
|
75
|
-
VITE_API_BASE_URL=http://localhost:3000/api
|
|
76
|
-
VITE_DEBUG=true
|
|
220
|
+
---
|
|
77
221
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
222
|
+
## 🛠️ 项目中的实现
|
|
223
|
+
|
|
224
|
+
### 1. 定义启动命令
|
|
81
225
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
226
|
+
```json
|
|
227
|
+
// package.json
|
|
228
|
+
{
|
|
229
|
+
"scripts": {
|
|
230
|
+
"dev": "vite --mode localhost",
|
|
231
|
+
"dev:staging": "vite --mode staging",
|
|
232
|
+
"dev:prod": "vite --mode production",
|
|
233
|
+
"build": "npm run lint && vite build",
|
|
234
|
+
"build:staging": "npm run lint && vite build --mode staging",
|
|
235
|
+
"build:production": "npm run lint && vite build --mode production"
|
|
236
|
+
}
|
|
237
|
+
}
|
|
85
238
|
```
|
|
86
239
|
|
|
87
|
-
|
|
240
|
+
**说明:**
|
|
241
|
+
|
|
242
|
+
- `--mode` 参数决定加载哪个 `.env` 文件
|
|
243
|
+
- 开发环境:使用 `localhost` 模式
|
|
244
|
+
- 测试环境:使用 `staging` 模式
|
|
245
|
+
- 生产环境:使用 `production` 模式
|
|
88
246
|
|
|
89
|
-
###
|
|
247
|
+
### 2. 配置 Vite
|
|
90
248
|
|
|
91
|
-
```
|
|
249
|
+
```typescript
|
|
92
250
|
// vite.config.ts
|
|
93
|
-
import
|
|
251
|
+
import { defineConfig } from 'vitest/config';
|
|
252
|
+
import { envPrefix } from './config/common';
|
|
253
|
+
import { name, version } from './package.json';
|
|
254
|
+
import envConfig from '@qlover/corekit-bridge/build/vite-env-config';
|
|
94
255
|
|
|
95
256
|
export default defineConfig({
|
|
96
257
|
plugins: [
|
|
258
|
+
// ✅ envConfig 插件:预处理环境变量
|
|
97
259
|
envConfig({
|
|
98
|
-
envPops: true, //
|
|
99
|
-
envPrefix
|
|
260
|
+
envPops: true, // 启用环境变量处理
|
|
261
|
+
envPrefix, // 环境变量前缀: 'VITE_'
|
|
100
262
|
records: [
|
|
101
263
|
['APP_NAME', name], // 注入应用名称
|
|
102
264
|
['APP_VERSION', version] // 注入应用版本
|
|
103
265
|
]
|
|
104
266
|
})
|
|
267
|
+
// ... 其他插件
|
|
105
268
|
],
|
|
106
|
-
envPrefix:
|
|
269
|
+
envPrefix: envPrefix, // Vite 环境变量前缀
|
|
107
270
|
server: {
|
|
108
271
|
port: Number(process.env.VITE_SERVER_PORT || 3200)
|
|
109
272
|
}
|
|
110
273
|
});
|
|
111
274
|
```
|
|
112
275
|
|
|
113
|
-
|
|
276
|
+
**关键配置:**
|
|
277
|
+
|
|
278
|
+
- `envConfig` 插件负责预处理环境变量
|
|
279
|
+
- `records` 可以注入额外的变量(如 package.json 中的信息)
|
|
280
|
+
- `envPrefix` 设置为 `'VITE_'`,只有此前缀的变量会被暴露给客户端
|
|
281
|
+
|
|
282
|
+
### 3. 定义公共配置
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
// config/common.ts
|
|
286
|
+
export const envPrefix = 'VITE_';
|
|
114
287
|
|
|
115
|
-
|
|
288
|
+
/**
|
|
289
|
+
* 启动器环境变量注入黑名单
|
|
290
|
+
* 这些属性不会从环境变量注入
|
|
291
|
+
*/
|
|
292
|
+
export const envBlackList = ['env', 'userNodeEnv'];
|
|
293
|
+
|
|
294
|
+
export const browserGlobalsName = 'feGlobals';
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 4. 定义 AppConfig
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
116
300
|
// src/base/cases/AppConfig.ts
|
|
301
|
+
import type { EnvConfigInterface } from '@qlover/corekit-bridge';
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 应用配置类
|
|
305
|
+
*
|
|
306
|
+
* 所有属性都会在 Bootstrap 初始化时自动注入对应的环境变量值
|
|
307
|
+
*
|
|
308
|
+
* 环境变量命名规则:
|
|
309
|
+
* - 属性名会转换为大写下划线格式
|
|
310
|
+
* - 添加 VITE_ 前缀
|
|
311
|
+
*
|
|
312
|
+
* 示例:
|
|
313
|
+
* - appName → VITE_APP_NAME
|
|
314
|
+
* - feApiBaseUrl → VITE_FE_API_BASE_URL
|
|
315
|
+
* - aiApiToken → VITE_AI_API_TOKEN
|
|
316
|
+
*/
|
|
117
317
|
export class AppConfig implements EnvConfigInterface {
|
|
318
|
+
constructor(
|
|
319
|
+
/**
|
|
320
|
+
* 当前环境模式
|
|
321
|
+
* @description 从 Vite 的 MODE 自动设置
|
|
322
|
+
*/
|
|
323
|
+
readonly env: string = import.meta.env.VITE_USER_NODE_ENV
|
|
324
|
+
) {}
|
|
325
|
+
|
|
118
326
|
/**
|
|
119
327
|
* 应用名称
|
|
120
328
|
* @description 从 VITE_APP_NAME 环境变量注入
|
|
@@ -127,18 +335,30 @@ export class AppConfig implements EnvConfigInterface {
|
|
|
127
335
|
*/
|
|
128
336
|
readonly appVersion = '';
|
|
129
337
|
|
|
130
|
-
/**
|
|
131
|
-
* 当前环境模式
|
|
132
|
-
* @description 从 Vite 的 mode 获取
|
|
133
|
-
*/
|
|
134
|
-
readonly env: string = import.meta.env.MODE;
|
|
135
|
-
|
|
136
338
|
/**
|
|
137
339
|
* 用户令牌存储键
|
|
138
340
|
* @description 从 VITE_USER_TOKEN_STORAGE_KEY 环境变量注入
|
|
139
341
|
*/
|
|
140
342
|
readonly userTokenStorageKey = '__fe_user_token__';
|
|
141
343
|
|
|
344
|
+
/**
|
|
345
|
+
* 用户信息存储键
|
|
346
|
+
* @description 从 VITE_USER_INFO_STORAGE_KEY 环境变量注入
|
|
347
|
+
*/
|
|
348
|
+
readonly userInfoStorageKey = '__fe_user_info__';
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* 前端 API 基础 URL
|
|
352
|
+
* @description 从 VITE_FE_API_BASE_URL 环境变量注入
|
|
353
|
+
*/
|
|
354
|
+
readonly feApiBaseUrl = '';
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* 用户 API 基础 URL
|
|
358
|
+
* @description 从 VITE_USER_API_BASE_URL 环境变量注入
|
|
359
|
+
*/
|
|
360
|
+
readonly userApiBaseUrl = '';
|
|
361
|
+
|
|
142
362
|
/**
|
|
143
363
|
* AI API 基础 URL
|
|
144
364
|
* @description 从 VITE_AI_API_BASE_URL 环境变量注入
|
|
@@ -151,329 +371,966 @@ export class AppConfig implements EnvConfigInterface {
|
|
|
151
371
|
*/
|
|
152
372
|
readonly aiApiToken = '';
|
|
153
373
|
|
|
154
|
-
|
|
374
|
+
/**
|
|
375
|
+
* AI API 令牌前缀
|
|
376
|
+
* @description 从 VITE_AI_API_TOKEN_PREFIX 环境变量注入
|
|
377
|
+
*/
|
|
378
|
+
readonly aiApiTokenPrefix = 'Bearer';
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* 是否需要 AI API 令牌
|
|
382
|
+
* @description 从 VITE_AI_API_REQUIRE_TOKEN 环境变量注入
|
|
383
|
+
*/
|
|
384
|
+
readonly aiApiRequireToken = true;
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* 默认登录用户名
|
|
388
|
+
* @description 从 VITE_LOGIN_USER 环境变量注入
|
|
389
|
+
*/
|
|
390
|
+
readonly loginUser = '';
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* 默认登录密码
|
|
394
|
+
* @description 从 VITE_LOGIN_PASSWORD 环境变量注入
|
|
395
|
+
*/
|
|
396
|
+
readonly loginPassword = '';
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* OpenAI 可用模型列表
|
|
400
|
+
*/
|
|
401
|
+
readonly openAiModels = [
|
|
402
|
+
'gpt-4o-mini',
|
|
403
|
+
'gpt-3.5-turbo',
|
|
404
|
+
'gpt-3.5-turbo-2',
|
|
405
|
+
'gpt-4',
|
|
406
|
+
'gpt-4-32k'
|
|
407
|
+
];
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* OpenAI API 基础 URL
|
|
411
|
+
* @description 从 VITE_OPEN_AI_BASE_URL 环境变量注入
|
|
412
|
+
*/
|
|
413
|
+
readonly openAiBaseUrl = '';
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* OpenAI API 令牌
|
|
417
|
+
* @description 从 VITE_OPEN_AI_TOKEN 环境变量注入
|
|
418
|
+
*/
|
|
419
|
+
readonly openAiToken = '';
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* OpenAI API 令牌前缀
|
|
423
|
+
* @description 从 VITE_OPEN_AI_TOKEN_PREFIX 环境变量注入
|
|
424
|
+
*/
|
|
425
|
+
readonly openAiTokenPrefix = '';
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* 是否需要 OpenAI API 令牌
|
|
429
|
+
* @description 从 VITE_OPEN_AI_REQUIRE_TOKEN 环境变量注入
|
|
430
|
+
*/
|
|
431
|
+
readonly openAiRequireToken = true;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* 项目启动 URL
|
|
435
|
+
* @description 从 Bootstrap 注入的 BOOT_HREF
|
|
436
|
+
*/
|
|
437
|
+
readonly bootHref = '';
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* 是否为生产环境
|
|
441
|
+
*/
|
|
442
|
+
get isProduction(): boolean {
|
|
443
|
+
return this.env === 'production';
|
|
444
|
+
}
|
|
155
445
|
}
|
|
156
446
|
```
|
|
157
447
|
|
|
158
|
-
###
|
|
448
|
+
### 5. Bootstrap 配置
|
|
159
449
|
|
|
160
|
-
```
|
|
161
|
-
// src/core/bootstraps/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
450
|
+
```typescript
|
|
451
|
+
// src/core/bootstraps/BootstrapClient.ts
|
|
452
|
+
import { envBlackList, envPrefix, browserGlobalsName } from '@config/common';
|
|
453
|
+
import { Bootstrap } from '@qlover/corekit-bridge';
|
|
454
|
+
import * as globals from '../globals';
|
|
455
|
+
|
|
456
|
+
export class BootstrapClient {
|
|
457
|
+
static async main(args: BootstrapClientArgs) {
|
|
458
|
+
const { root, bootHref, ioc, iocRegister } = args;
|
|
459
|
+
const { logger, appConfig } = globals;
|
|
460
|
+
|
|
461
|
+
// 创建 IOC 容器
|
|
462
|
+
const IOC = ioc.create({
|
|
463
|
+
pathname: bootHref,
|
|
464
|
+
appConfig: appConfig
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// 创建 Bootstrap 实例
|
|
468
|
+
const bootstrap = new Bootstrap({
|
|
469
|
+
root,
|
|
470
|
+
logger,
|
|
471
|
+
ioc: {
|
|
472
|
+
manager: IOC,
|
|
473
|
+
register: iocRegister
|
|
474
|
+
},
|
|
475
|
+
// ✅ 环境变量注入配置
|
|
476
|
+
envOptions: {
|
|
477
|
+
target: appConfig, // 注入目标:AppConfig 实例
|
|
478
|
+
source: Object.assign({}, import.meta.env, {
|
|
479
|
+
[envPrefix + 'BOOT_HREF']: bootHref // 额外注入启动 URL
|
|
480
|
+
}),
|
|
481
|
+
prefix: envPrefix, // 环境变量前缀:'VITE_'
|
|
482
|
+
blackList: envBlackList // 黑名单:['env', 'userNodeEnv']
|
|
483
|
+
},
|
|
484
|
+
// 全局变量封装配置
|
|
485
|
+
globalOptions: {
|
|
486
|
+
sources: globals,
|
|
487
|
+
target: browserGlobalsName
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
logger.info('bootstrap start...');
|
|
493
|
+
|
|
494
|
+
// ✅ 初始化 Bootstrap(此时会执行环境变量注入)
|
|
495
|
+
await bootstrap.initialize();
|
|
496
|
+
|
|
497
|
+
// 注册业务插件
|
|
498
|
+
const bootstrapsRegistry = new BootstrapsRegistry(IOC);
|
|
499
|
+
await bootstrap.use(bootstrapsRegistry.register()).start();
|
|
500
|
+
|
|
501
|
+
logger.info('bootstrap completed successfully');
|
|
502
|
+
} catch (error) {
|
|
503
|
+
logger.error(`${appConfig.appName} startup error:`, error);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return args;
|
|
177
507
|
}
|
|
178
|
-
}
|
|
508
|
+
}
|
|
179
509
|
```
|
|
180
510
|
|
|
181
|
-
|
|
511
|
+
**关键流程:**
|
|
182
512
|
|
|
183
|
-
|
|
513
|
+
1. `appConfig` 是一个 AppConfig 实例,所有属性都有默认值
|
|
514
|
+
2. `bootstrap.initialize()` 时会执行 `InjectEnv` 插件
|
|
515
|
+
3. `InjectEnv` 插件遍历 `appConfig` 的属性,从 `import.meta.env` 中查找对应的环境变量
|
|
516
|
+
4. 如果找到环境变量且值不为空,则覆盖默认值
|
|
517
|
+
5. 最后 `appConfig` 被注册到 IOC 容器中
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## 🌍 多环境配置
|
|
522
|
+
|
|
523
|
+
### 环境文件结构
|
|
524
|
+
|
|
525
|
+
```
|
|
526
|
+
项目根目录/
|
|
527
|
+
├── .env # 默认配置(所有环境共享)
|
|
528
|
+
├── .env.localhost # 本地开发环境
|
|
529
|
+
├── .env.staging # 测试环境
|
|
530
|
+
├── .env.production # 生产环境
|
|
531
|
+
├── .env.local # 本地覆盖配置(不提交到 git)
|
|
532
|
+
└── .env.template # 环境变量模板(提交到 git)
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### 加载优先级
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
.env.local > .env.[mode] > .env
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**示例:**
|
|
184
542
|
|
|
185
543
|
```bash
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
544
|
+
# 运行:npm run dev (vite --mode localhost)
|
|
545
|
+
# 加载顺序:
|
|
546
|
+
# 1. .env.local # 优先级最高
|
|
547
|
+
# 2. .env.localhost # 其次
|
|
548
|
+
# 3. .env # 最后
|
|
549
|
+
|
|
550
|
+
# 运行:npm run build:production (vite build --mode production)
|
|
551
|
+
# 加载顺序:
|
|
552
|
+
# 1. .env.local
|
|
553
|
+
# 2. .env.production
|
|
554
|
+
# 3. .env
|
|
194
555
|
```
|
|
195
556
|
|
|
557
|
+
### 示例 1:默认配置
|
|
558
|
+
|
|
196
559
|
```bash
|
|
197
|
-
# .env
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
560
|
+
# .env
|
|
561
|
+
# 所有环境共享的配置
|
|
562
|
+
|
|
563
|
+
VITE_APP_NAME=MyApp
|
|
564
|
+
VITE_USER_TOKEN_STORAGE_KEY=__fe_user_token__
|
|
565
|
+
VITE_USER_INFO_STORAGE_KEY=__fe_user_info__
|
|
566
|
+
VITE_AI_API_TOKEN_PREFIX=Bearer
|
|
567
|
+
VITE_AI_API_REQUIRE_TOKEN=true
|
|
202
568
|
```
|
|
203
569
|
|
|
204
|
-
### 2
|
|
570
|
+
### 示例 2:本地开发环境
|
|
205
571
|
|
|
206
572
|
```bash
|
|
207
|
-
# .env.
|
|
208
|
-
|
|
209
|
-
|
|
573
|
+
# .env.localhost
|
|
574
|
+
# 本地开发环境配置
|
|
575
|
+
|
|
576
|
+
# API 配置
|
|
577
|
+
VITE_FE_API_BASE_URL=http://localhost:3000/api
|
|
578
|
+
VITE_USER_API_BASE_URL=http://localhost:3000/api/user
|
|
579
|
+
VITE_AI_API_BASE_URL=http://localhost:3001/v1
|
|
580
|
+
|
|
581
|
+
# AI 配置(开发环境可能使用本地 Mock)
|
|
582
|
+
VITE_AI_API_TOKEN=sk-dev-xxxxx
|
|
583
|
+
VITE_AI_API_REQUIRE_TOKEN=false
|
|
584
|
+
|
|
585
|
+
# 调试配置
|
|
586
|
+
VITE_LOG_LEVEL=debug
|
|
210
587
|
VITE_DEBUG=true
|
|
211
|
-
|
|
588
|
+
|
|
589
|
+
# 默认登录信息(方便开发)
|
|
590
|
+
VITE_LOGIN_USER=admin
|
|
591
|
+
VITE_LOGIN_PASSWORD=admin123
|
|
212
592
|
```
|
|
213
593
|
|
|
214
|
-
### 3
|
|
594
|
+
### 示例 3:测试环境
|
|
215
595
|
|
|
216
596
|
```bash
|
|
217
|
-
# .env.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
597
|
+
# .env.staging
|
|
598
|
+
# 测试环境配置
|
|
599
|
+
|
|
600
|
+
# API 配置
|
|
601
|
+
VITE_FE_API_BASE_URL=https://api.staging.example.com
|
|
602
|
+
VITE_USER_API_BASE_URL=https://api.staging.example.com/user
|
|
603
|
+
VITE_AI_API_BASE_URL=https://api.staging.example.com/ai
|
|
604
|
+
|
|
605
|
+
# AI 配置
|
|
606
|
+
VITE_AI_API_TOKEN=sk-staging-xxxxx
|
|
607
|
+
VITE_AI_API_REQUIRE_TOKEN=true
|
|
608
|
+
|
|
609
|
+
# 调试配置
|
|
610
|
+
VITE_LOG_LEVEL=info
|
|
611
|
+
VITE_DEBUG=true
|
|
222
612
|
```
|
|
223
613
|
|
|
224
|
-
### 4
|
|
614
|
+
### 示例 4:生产环境
|
|
225
615
|
|
|
226
616
|
```bash
|
|
227
|
-
# .env.
|
|
228
|
-
|
|
229
|
-
VITE_LOCAL_DEBUG=true
|
|
230
|
-
VITE_CUSTOM_FEATURE=true
|
|
231
|
-
```
|
|
617
|
+
# .env.production
|
|
618
|
+
# 生产环境配置
|
|
232
619
|
|
|
233
|
-
|
|
620
|
+
# API 配置
|
|
621
|
+
VITE_FE_API_BASE_URL=https://api.example.com
|
|
622
|
+
VITE_USER_API_BASE_URL=https://api.example.com/user
|
|
623
|
+
VITE_AI_API_BASE_URL=https://api.openai.com/v1
|
|
234
624
|
|
|
235
|
-
|
|
625
|
+
# AI 配置
|
|
626
|
+
VITE_AI_API_TOKEN=sk-prod-xxxxx
|
|
627
|
+
VITE_AI_API_REQUIRE_TOKEN=true
|
|
236
628
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const apiUrl = import.meta.env.VITE_API_BASE_URL;
|
|
241
|
-
const isDebug = import.meta.env.VITE_DEBUG === 'true';
|
|
242
|
-
return (
|
|
243
|
-
<div>
|
|
244
|
-
<p>API URL: {apiUrl}</p>
|
|
245
|
-
{isDebug && <p>Debug mode enabled</p>}
|
|
246
|
-
</div>
|
|
247
|
-
);
|
|
248
|
-
}
|
|
629
|
+
# 调试配置
|
|
630
|
+
VITE_LOG_LEVEL=error
|
|
631
|
+
VITE_DEBUG=false
|
|
249
632
|
```
|
|
250
633
|
|
|
251
|
-
###
|
|
634
|
+
### 示例 5:本地覆盖配置
|
|
252
635
|
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const appConfig = IOC(IOCIdentifier.AppConfig);
|
|
636
|
+
```bash
|
|
637
|
+
# .env.local
|
|
638
|
+
# 本地个人配置,不提交到 git
|
|
257
639
|
|
|
258
|
-
|
|
259
|
-
|
|
640
|
+
# 覆盖 AI API Token(使用自己的 Key)
|
|
641
|
+
VITE_AI_API_TOKEN=sk-my-personal-key
|
|
260
642
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
});
|
|
267
|
-
}
|
|
643
|
+
# 覆盖 API 地址(连接到自己的本地服务)
|
|
644
|
+
VITE_FE_API_BASE_URL=http://192.168.1.100:3000/api
|
|
645
|
+
|
|
646
|
+
# 启用特定功能
|
|
647
|
+
VITE_ENABLE_EXPERIMENTAL_FEATURES=true
|
|
268
648
|
```
|
|
269
649
|
|
|
270
|
-
###
|
|
650
|
+
### 示例 6:环境变量模板
|
|
271
651
|
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
constructor(@inject(IOCIdentifier.AppConfig) private appConfig: AppConfig) {}
|
|
652
|
+
```bash
|
|
653
|
+
# .env.template
|
|
654
|
+
# 环境变量模板,提交到 git 供团队参考
|
|
276
655
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
656
|
+
# ===== 必填配置 =====
|
|
657
|
+
VITE_FE_API_BASE_URL=https://your-api-url.com
|
|
658
|
+
VITE_AI_API_TOKEN=your-ai-api-token-here
|
|
280
659
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
660
|
+
# ===== 可选配置 =====
|
|
661
|
+
VITE_LOGIN_USER=your-default-username
|
|
662
|
+
VITE_LOGIN_PASSWORD=your-default-password
|
|
663
|
+
|
|
664
|
+
# ===== 说明 =====
|
|
665
|
+
# 1. 复制此文件为 .env.local
|
|
666
|
+
# 2. 填写实际的配置值
|
|
667
|
+
# 3. .env.local 不会被提交到 git
|
|
288
668
|
```
|
|
289
669
|
|
|
290
|
-
|
|
670
|
+
---
|
|
291
671
|
|
|
292
|
-
|
|
672
|
+
## 🔄 环境变量注入
|
|
293
673
|
|
|
294
|
-
|
|
295
|
-
|
|
674
|
+
### InjectEnv 插件工作原理
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
// @qlover/corekit-bridge/src/core/bootstrap/plugins/InjectEnv.ts (简化版)
|
|
296
678
|
export class InjectEnv implements BootstrapExecutorPlugin {
|
|
297
679
|
readonly pluginName = 'InjectEnv';
|
|
298
680
|
|
|
299
681
|
constructor(protected options: InjectEnvConfig) {}
|
|
300
682
|
|
|
683
|
+
/**
|
|
684
|
+
* 在 Bootstrap 初始化前执行
|
|
685
|
+
*/
|
|
301
686
|
onBefore(): void {
|
|
302
687
|
const { target, source, prefix, blackList } = this.options;
|
|
303
|
-
|
|
688
|
+
|
|
689
|
+
// 遍历目标对象的所有属性
|
|
304
690
|
for (const key in target) {
|
|
691
|
+
// 跳过黑名单中的属性
|
|
305
692
|
if (blackList.includes(key)) {
|
|
306
|
-
continue;
|
|
693
|
+
continue;
|
|
307
694
|
}
|
|
308
695
|
|
|
309
|
-
const
|
|
310
|
-
|
|
696
|
+
const currentValue = target[key as keyof typeof target];
|
|
697
|
+
|
|
698
|
+
// 获取对应的环境变量值
|
|
699
|
+
const envValue = this.getEnvValue(key, currentValue);
|
|
311
700
|
|
|
312
701
|
// 如果环境变量存在且与默认值不同,则注入
|
|
313
|
-
if (!this.isEmpty(envValue) && envValue !==
|
|
702
|
+
if (!this.isEmpty(envValue) && envValue !== currentValue) {
|
|
314
703
|
target[key as keyof typeof target] = envValue;
|
|
315
704
|
}
|
|
316
705
|
}
|
|
317
706
|
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* 获取环境变量值
|
|
710
|
+
*/
|
|
711
|
+
private getEnvValue<D>(key: string, defaultValue?: D): D {
|
|
712
|
+
const { prefix = '', source = {} } = this.options;
|
|
713
|
+
|
|
714
|
+
// 将驼峰命名转换为大写下划线命名
|
|
715
|
+
// appName → APP_NAME
|
|
716
|
+
// feApiBaseUrl → FE_API_BASE_URL
|
|
717
|
+
const formattedKey = key.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
718
|
+
|
|
719
|
+
// 添加前缀
|
|
720
|
+
const envKey = `${prefix}${formattedKey}`;
|
|
721
|
+
|
|
722
|
+
// 获取环境变量值
|
|
723
|
+
const value = source[envKey];
|
|
724
|
+
|
|
725
|
+
// 如果是 JSON 字符串,则解析
|
|
726
|
+
if (typeof value === 'string' && this.isJSONString(value)) {
|
|
727
|
+
return JSON.parse(value);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return (value ?? defaultValue) as D;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* 判断值是否为空
|
|
735
|
+
*/
|
|
736
|
+
private isEmpty(value: any): boolean {
|
|
737
|
+
return value === undefined || value === null || value === '';
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* 判断是否为 JSON 字符串
|
|
742
|
+
*/
|
|
743
|
+
private isJSONString(str: string): boolean {
|
|
744
|
+
try {
|
|
745
|
+
JSON.parse(str);
|
|
746
|
+
return true;
|
|
747
|
+
} catch {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
318
751
|
}
|
|
319
752
|
```
|
|
320
753
|
|
|
321
|
-
###
|
|
754
|
+
### 注入示例
|
|
755
|
+
|
|
756
|
+
```typescript
|
|
757
|
+
// 假设有以下环境变量:
|
|
758
|
+
VITE_APP_NAME=MyApp
|
|
759
|
+
VITE_FE_API_BASE_URL=https://api.example.com
|
|
760
|
+
VITE_AI_API_TOKEN=sk-xxxxx
|
|
322
761
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
762
|
+
// AppConfig 初始状态:
|
|
763
|
+
const appConfig = new AppConfig();
|
|
764
|
+
console.log(appConfig.appName); // ''
|
|
765
|
+
console.log(appConfig.feApiBaseUrl); // ''
|
|
766
|
+
console.log(appConfig.aiApiToken); // ''
|
|
326
767
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const envKey = `${prefix}${formattedKey}`;
|
|
768
|
+
// Bootstrap 初始化后(InjectEnv 插件执行后):
|
|
769
|
+
await bootstrap.initialize();
|
|
330
770
|
|
|
331
|
-
|
|
771
|
+
console.log(appConfig.appName); // 'MyApp'
|
|
772
|
+
console.log(appConfig.feApiBaseUrl); // 'https://api.example.com'
|
|
773
|
+
console.log(appConfig.aiApiToken); // 'sk-xxxxx'
|
|
774
|
+
```
|
|
332
775
|
|
|
333
|
-
|
|
334
|
-
if (typeof value === 'string' && InjectEnv.isJSONString(value)) {
|
|
335
|
-
return JSON.parse(value);
|
|
336
|
-
}
|
|
776
|
+
---
|
|
337
777
|
|
|
338
|
-
|
|
778
|
+
## 📦 AppConfig 使用
|
|
779
|
+
|
|
780
|
+
### 1. 在服务中使用(推荐)⭐
|
|
781
|
+
|
|
782
|
+
```typescript
|
|
783
|
+
// src/base/services/UserService.ts
|
|
784
|
+
import { injectable, inject } from 'inversify';
|
|
785
|
+
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
786
|
+
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
787
|
+
|
|
788
|
+
@injectable()
|
|
789
|
+
export class UserService {
|
|
790
|
+
constructor(
|
|
791
|
+
@inject(IOCIdentifier.AppConfig) private config: AppConfig,
|
|
792
|
+
@inject(UserApi) private api: UserApi
|
|
793
|
+
) {}
|
|
794
|
+
|
|
795
|
+
async login(username: string, password: string) {
|
|
796
|
+
// ✅ 使用配置中的 API 地址
|
|
797
|
+
const response = await this.api.post(
|
|
798
|
+
`${this.config.userApiBaseUrl}/login`,
|
|
799
|
+
{ username, password }
|
|
800
|
+
);
|
|
801
|
+
|
|
802
|
+
// ✅ 使用配置中的存储键
|
|
803
|
+
this.storage.setItem(this.config.userTokenStorageKey, response.token);
|
|
804
|
+
|
|
805
|
+
return response.user;
|
|
806
|
+
}
|
|
339
807
|
}
|
|
340
808
|
```
|
|
341
809
|
|
|
342
|
-
|
|
810
|
+
### 2. 在 UI 组件中使用
|
|
343
811
|
|
|
344
|
-
|
|
812
|
+
```typescript
|
|
813
|
+
// src/pages/base/HomePage.tsx
|
|
814
|
+
import { useIOC } from '@/uikit/hooks/useIOC';
|
|
345
815
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
VITE_API_BASE_URL=https://api.example.com
|
|
350
|
-
VITE_USER_TOKEN_STORAGE_KEY=user_token
|
|
351
|
-
VITE_DEBUG=true
|
|
816
|
+
function HomePage() {
|
|
817
|
+
// ✅ 通过 Hook 获取配置
|
|
818
|
+
const config = useIOC('AppConfig');
|
|
352
819
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
820
|
+
return (
|
|
821
|
+
<div>
|
|
822
|
+
<h1>{config.appName}</h1>
|
|
823
|
+
<p>Version: {config.appVersion}</p>
|
|
824
|
+
<p>Environment: {config.env}</p>
|
|
825
|
+
{!config.isProduction && <p>🚧 Development Mode</p>}
|
|
826
|
+
</div>
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### 3. 在插件中使用
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
// src/base/apis/feApi/FeApiBootstrap.ts
|
|
835
|
+
export class FeApiBootstarp implements BootstrapExecutorPlugin {
|
|
836
|
+
readonly pluginName = 'FeApiBootstarp';
|
|
837
|
+
|
|
838
|
+
onBefore({ parameters: { ioc } }: BootstrapContext): void {
|
|
839
|
+
const feApi = ioc.get<FeApi>(FeApi);
|
|
840
|
+
// ✅ 从 IOC 获取配置
|
|
841
|
+
const config = ioc.get<AppConfig>(IOCIdentifier.AppConfig);
|
|
842
|
+
|
|
843
|
+
// ✅ 使用配置设置 API 基础 URL
|
|
844
|
+
feApi.setBaseURL(config.feApiBaseUrl);
|
|
845
|
+
|
|
846
|
+
// 添加其他插件
|
|
847
|
+
feApi.usePlugin(
|
|
848
|
+
new AuthTokenPlugin({
|
|
849
|
+
getToken: () => {
|
|
850
|
+
const storage = ioc.get(IOCIdentifier.LocalStorageEncrypt);
|
|
851
|
+
return storage.getItem(config.userTokenStorageKey);
|
|
852
|
+
}
|
|
853
|
+
})
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
358
857
|
```
|
|
359
858
|
|
|
360
|
-
###
|
|
859
|
+
### 4. 直接在代码中使用环境变量
|
|
361
860
|
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
VITE_API_KEY=your_secret_key
|
|
365
|
-
VITE_DATABASE_PASSWORD=your_password
|
|
861
|
+
```typescript
|
|
862
|
+
// 注意:直接使用 import.meta.env 不推荐,因为无法被 IOC 管理
|
|
366
863
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
864
|
+
// ❌ 不推荐:直接使用(绕过了 AppConfig)
|
|
865
|
+
function MyComponent() {
|
|
866
|
+
const apiUrl = import.meta.env.VITE_FE_API_BASE_URL;
|
|
867
|
+
// ...
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// ✅ 推荐:通过 AppConfig 使用
|
|
871
|
+
function MyComponent() {
|
|
872
|
+
const config = useIOC('AppConfig');
|
|
873
|
+
const apiUrl = config.feApiBaseUrl;
|
|
874
|
+
// ...
|
|
875
|
+
}
|
|
370
876
|
```
|
|
371
877
|
|
|
372
|
-
|
|
878
|
+
---
|
|
373
879
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
880
|
+
## 🚀 高级用法
|
|
881
|
+
|
|
882
|
+
### 1. 动态修改配置
|
|
883
|
+
|
|
884
|
+
有时候你可能需要在运行时动态修改配置(而不是通过环境变量):
|
|
885
|
+
|
|
886
|
+
```typescript
|
|
887
|
+
// ✅ 方法 1:在 Bootstrap 初始化前修改
|
|
888
|
+
const appConfig = new AppConfig();
|
|
889
|
+
|
|
890
|
+
// 动态修改配置
|
|
891
|
+
if (window.location.hostname.includes('localhost')) {
|
|
892
|
+
// 本地开发时使用不同的 API 地址
|
|
893
|
+
(appConfig as any).feApiBaseUrl = 'http://localhost:3000/api';
|
|
381
894
|
}
|
|
382
895
|
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
896
|
+
// 然后传递给 Bootstrap
|
|
897
|
+
const bootstrap = new Bootstrap({
|
|
898
|
+
envOptions: {
|
|
899
|
+
target: appConfig, // 使用修改后的配置
|
|
900
|
+
source: import.meta.env,
|
|
901
|
+
prefix: 'VITE_',
|
|
902
|
+
blackList: envBlackList
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
await bootstrap.initialize();
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
```typescript
|
|
910
|
+
// ✅ 方法 2:创建配置工厂函数
|
|
911
|
+
export function createAppConfig(): AppConfig {
|
|
912
|
+
const config = new AppConfig();
|
|
913
|
+
|
|
914
|
+
// 根据特定条件动态设置配置
|
|
915
|
+
if (someCondition) {
|
|
916
|
+
(config as any).aiApiBaseUrl = 'https://custom-api.com';
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return config;
|
|
389
920
|
}
|
|
921
|
+
|
|
922
|
+
// 在 Bootstrap 中使用
|
|
923
|
+
const appConfig = createAppConfig();
|
|
390
924
|
```
|
|
391
925
|
|
|
392
|
-
###
|
|
926
|
+
### 2. 配置验证
|
|
393
927
|
|
|
394
|
-
```
|
|
395
|
-
//
|
|
928
|
+
```typescript
|
|
929
|
+
// src/base/cases/AppConfig.ts
|
|
396
930
|
export class AppConfig implements EnvConfigInterface {
|
|
397
|
-
|
|
398
|
-
this.validateRequiredEnvVars();
|
|
399
|
-
}
|
|
931
|
+
// ... 属性定义
|
|
400
932
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
933
|
+
/**
|
|
934
|
+
* 验证必需的配置项
|
|
935
|
+
*/
|
|
936
|
+
validate(): void {
|
|
937
|
+
const required: (keyof AppConfig)[] = [
|
|
938
|
+
'appName',
|
|
939
|
+
'feApiBaseUrl',
|
|
940
|
+
'userTokenStorageKey'
|
|
941
|
+
];
|
|
942
|
+
|
|
943
|
+
for (const key of required) {
|
|
944
|
+
if (!this[key]) {
|
|
945
|
+
throw new Error(`Missing required configuration: ${key}`);
|
|
406
946
|
}
|
|
407
947
|
}
|
|
408
948
|
}
|
|
409
949
|
}
|
|
950
|
+
|
|
951
|
+
// 在 Bootstrap 中使用
|
|
952
|
+
const appConfig = new AppConfig();
|
|
953
|
+
|
|
954
|
+
await bootstrap.initialize();
|
|
955
|
+
|
|
956
|
+
// 初始化后验证
|
|
957
|
+
appConfig.validate();
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
### 3. 配置组合
|
|
961
|
+
|
|
962
|
+
```typescript
|
|
963
|
+
// ✅ 方法 3:从多个源组合配置
|
|
964
|
+
const appConfig = new AppConfig();
|
|
965
|
+
|
|
966
|
+
const bootstrap = new Bootstrap({
|
|
967
|
+
envOptions: {
|
|
968
|
+
target: appConfig,
|
|
969
|
+
// 合并多个配置源
|
|
970
|
+
source: Object.assign(
|
|
971
|
+
{},
|
|
972
|
+
import.meta.env, // Vite 环境变量
|
|
973
|
+
{ VITE_BOOT_HREF: window.location.href }, // 运行时信息
|
|
974
|
+
window.__APP_CONFIG__ // 服务端注入的配置
|
|
975
|
+
),
|
|
976
|
+
prefix: 'VITE_',
|
|
977
|
+
blackList: envBlackList
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
### 4. 条件配置
|
|
983
|
+
|
|
984
|
+
```typescript
|
|
985
|
+
// src/core/bootstraps/BootstrapClient.ts
|
|
986
|
+
const appConfig = new AppConfig();
|
|
987
|
+
|
|
988
|
+
// ✅ 根据环境设置不同的配置源
|
|
989
|
+
const configSource =
|
|
990
|
+
import.meta.env.VITE_USER_NODE_ENV === 'production'
|
|
991
|
+
? import.meta.env // 生产环境:只使用环境变量
|
|
992
|
+
: {
|
|
993
|
+
...import.meta.env,
|
|
994
|
+
...window.__DEV_CONFIG__ // 开发环境:允许 window 注入
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const bootstrap = new Bootstrap({
|
|
998
|
+
envOptions: {
|
|
999
|
+
target: appConfig,
|
|
1000
|
+
source: configSource,
|
|
1001
|
+
prefix: 'VITE_',
|
|
1002
|
+
blackList: envBlackList
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## 🧪 测试配置
|
|
1010
|
+
|
|
1011
|
+
### 1. 测试时 Mock AppConfig
|
|
1012
|
+
|
|
1013
|
+
```typescript
|
|
1014
|
+
// __tests__/src/base/services/UserService.test.ts
|
|
1015
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
1016
|
+
import { UserService } from '@/base/services/UserService';
|
|
1017
|
+
import type { AppConfig } from '@/base/cases/AppConfig';
|
|
1018
|
+
|
|
1019
|
+
describe('UserService', () => {
|
|
1020
|
+
let userService: UserService;
|
|
1021
|
+
let mockConfig: AppConfig;
|
|
1022
|
+
|
|
1023
|
+
beforeEach(() => {
|
|
1024
|
+
// ✅ 创建 mock 配置
|
|
1025
|
+
mockConfig = {
|
|
1026
|
+
userApiBaseUrl: 'http://localhost:3000/api',
|
|
1027
|
+
userTokenStorageKey: '__test_token__',
|
|
1028
|
+
userInfoStorageKey: '__test_user__',
|
|
1029
|
+
isProduction: false
|
|
1030
|
+
} as AppConfig;
|
|
1031
|
+
|
|
1032
|
+
// 创建服务
|
|
1033
|
+
userService = new UserService(mockConfig, mockApi, mockStorage);
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
it('should use config values', async () => {
|
|
1037
|
+
await userService.login('user', 'pass');
|
|
1038
|
+
|
|
1039
|
+
// ✅ 验证使用了配置中的值
|
|
1040
|
+
expect(mockApi.post).toHaveBeenCalledWith(
|
|
1041
|
+
`${mockConfig.userApiBaseUrl}/login`,
|
|
1042
|
+
expect.any(Object)
|
|
1043
|
+
);
|
|
1044
|
+
});
|
|
1045
|
+
});
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
### 2. 测试不同环境配置
|
|
1049
|
+
|
|
1050
|
+
```typescript
|
|
1051
|
+
// __tests__/src/base/cases/AppConfig.test.ts
|
|
1052
|
+
import { describe, it, expect } from 'vitest';
|
|
1053
|
+
import { AppConfig } from '@/base/cases/AppConfig';
|
|
1054
|
+
|
|
1055
|
+
describe('AppConfig', () => {
|
|
1056
|
+
it('should detect production environment', () => {
|
|
1057
|
+
const config = new AppConfig('production');
|
|
1058
|
+
expect(config.isProduction).toBe(true);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it('should detect non-production environment', () => {
|
|
1062
|
+
const config = new AppConfig('localhost');
|
|
1063
|
+
expect(config.isProduction).toBe(false);
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
it('should have default values', () => {
|
|
1067
|
+
const config = new AppConfig();
|
|
1068
|
+
expect(config.appName).toBe('');
|
|
1069
|
+
expect(config.userTokenStorageKey).toBe('__fe_user_token__');
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
410
1072
|
```
|
|
411
1073
|
|
|
412
|
-
|
|
1074
|
+
### 3. 测试环境变量注入
|
|
1075
|
+
|
|
1076
|
+
```typescript
|
|
1077
|
+
// __tests__/src/core/bootstraps/BootstrapClient.test.ts
|
|
1078
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
1079
|
+
import { BootstrapClient } from '@/core/bootstraps/BootstrapClient';
|
|
413
1080
|
|
|
414
|
-
|
|
1081
|
+
describe('BootstrapClient', () => {
|
|
1082
|
+
it('should inject environment variables to AppConfig', async () => {
|
|
1083
|
+
const mockArgs = {
|
|
1084
|
+
root: {},
|
|
1085
|
+
bootHref: 'http://localhost:3000',
|
|
1086
|
+
ioc: {
|
|
1087
|
+
create: vi.fn().mockReturnValue(mockIOC)
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
// 执行启动
|
|
1092
|
+
await BootstrapClient.main(mockArgs);
|
|
415
1093
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
1094
|
+
// ✅ 验证配置已注入
|
|
1095
|
+
const globals = (mockArgs.root as any).feGlobals;
|
|
1096
|
+
expect(globals.appConfig).toBeDefined();
|
|
1097
|
+
expect(globals.appConfig.appName).toBeTruthy();
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
420
1100
|
```
|
|
421
1101
|
|
|
422
|
-
|
|
1102
|
+
---
|
|
1103
|
+
|
|
1104
|
+
## 💎 最佳实践
|
|
1105
|
+
|
|
1106
|
+
### 1. ✅ 使用环境变量前缀
|
|
1107
|
+
|
|
1108
|
+
```bash
|
|
1109
|
+
# ✅ 好的命名:使用 VITE_ 前缀
|
|
1110
|
+
VITE_APP_NAME=MyApp
|
|
1111
|
+
VITE_API_BASE_URL=https://api.example.com
|
|
1112
|
+
|
|
1113
|
+
# ❌ 错误命名:没有前缀
|
|
1114
|
+
APP_NAME=MyApp
|
|
1115
|
+
API_BASE_URL=https://api.example.com
|
|
1116
|
+
```
|
|
423
1117
|
|
|
424
|
-
|
|
1118
|
+
### 2. ✅ 敏感信息使用 .env.local
|
|
425
1119
|
|
|
426
1120
|
```bash
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
1121
|
+
# .env.local(不提交到 git)
|
|
1122
|
+
VITE_AI_API_TOKEN=sk-your-secret-key
|
|
1123
|
+
VITE_DATABASE_PASSWORD=your-password
|
|
1124
|
+
|
|
1125
|
+
# .gitignore
|
|
1126
|
+
.env.local
|
|
431
1127
|
```
|
|
432
1128
|
|
|
433
|
-
|
|
1129
|
+
### 3. ✅ 提供 .env.template
|
|
434
1130
|
|
|
435
1131
|
```bash
|
|
436
|
-
#
|
|
437
|
-
.env.
|
|
438
|
-
|
|
1132
|
+
# .env.template(提交到 git)
|
|
1133
|
+
# 团队成员可以复制此文件为 .env.local 并填写实际值
|
|
1134
|
+
|
|
1135
|
+
VITE_AI_API_TOKEN=your-api-token-here
|
|
1136
|
+
VITE_DATABASE_PASSWORD=your-password-here
|
|
439
1137
|
```
|
|
440
1138
|
|
|
441
|
-
|
|
1139
|
+
### 4. ✅ 使用类型安全的配置
|
|
1140
|
+
|
|
1141
|
+
```typescript
|
|
1142
|
+
// ✅ 好的做法:通过 AppConfig 访问
|
|
1143
|
+
const config = useIOC('AppConfig');
|
|
1144
|
+
const apiUrl = config.feApiBaseUrl; // ✅ 类型安全
|
|
1145
|
+
|
|
1146
|
+
// ❌ 不好的做法:直接访问环境变量
|
|
1147
|
+
const apiUrl = import.meta.env.VITE_FE_API_BASE_URL; // ❌ 可能为 undefined
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
### 5. ✅ 为 AppConfig 添加注释
|
|
1151
|
+
|
|
1152
|
+
```typescript
|
|
1153
|
+
export class AppConfig {
|
|
1154
|
+
/**
|
|
1155
|
+
* AI API 基础 URL
|
|
1156
|
+
* @description 从 VITE_AI_API_BASE_URL 环境变量注入
|
|
1157
|
+
* @default 'https://api.openai.com/v1'
|
|
1158
|
+
* @example 'https://api.openai.com/v1'
|
|
1159
|
+
*/
|
|
1160
|
+
readonly aiApiBaseUrl = 'https://api.openai.com/v1';
|
|
1161
|
+
}
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
### 6. ✅ 避免在代码中判断环境
|
|
1165
|
+
|
|
1166
|
+
```typescript
|
|
1167
|
+
// ❌ 不好:在代码中判断环境
|
|
1168
|
+
if (process.env.NODE_ENV === 'production') {
|
|
1169
|
+
apiUrl = 'https://api.production.com';
|
|
1170
|
+
} else {
|
|
1171
|
+
apiUrl = 'http://localhost:3000';
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// ✅ 好:通过配置管理
|
|
1175
|
+
const config = useIOC('AppConfig');
|
|
1176
|
+
const apiUrl = config.feApiBaseUrl; // 自动根据环境使用正确的值
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
### 7. ✅ 配置命名规范
|
|
1180
|
+
|
|
1181
|
+
```bash
|
|
1182
|
+
# ✅ 好的命名:清晰、具体
|
|
1183
|
+
VITE_FE_API_BASE_URL=https://api.example.com
|
|
1184
|
+
VITE_USER_TOKEN_STORAGE_KEY=__fe_user_token__
|
|
1185
|
+
VITE_AI_API_REQUIRE_TOKEN=true
|
|
1186
|
+
|
|
1187
|
+
# ❌ 不好的命名:模糊、简写
|
|
1188
|
+
VITE_API=https://api.example.com
|
|
1189
|
+
VITE_KEY=__token__
|
|
1190
|
+
VITE_REQ=true
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
---
|
|
1194
|
+
|
|
1195
|
+
## ❓ 常见问题
|
|
1196
|
+
|
|
1197
|
+
### Q1: 为什么我的环境变量没有注入?
|
|
1198
|
+
|
|
1199
|
+
**A:** 检查以下几点:
|
|
442
1200
|
|
|
443
|
-
|
|
444
|
-
|
|
1201
|
+
1. **环境变量前缀**
|
|
1202
|
+
|
|
1203
|
+
```bash
|
|
1204
|
+
# ✅ 正确:使用 VITE_ 前缀
|
|
1205
|
+
VITE_APP_NAME=MyApp
|
|
1206
|
+
|
|
1207
|
+
# ❌ 错误:没有前缀
|
|
1208
|
+
APP_NAME=MyApp
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
2. **AppConfig 属性名**
|
|
1212
|
+
|
|
1213
|
+
```typescript
|
|
1214
|
+
// ✅ 正确:属性名存在
|
|
1215
|
+
export class AppConfig {
|
|
1216
|
+
readonly appName = ''; // ← VITE_APP_NAME 会注入这里
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// ❌ 错误:没有对应的属性
|
|
1220
|
+
export class AppConfig {
|
|
1221
|
+
// 没有 appName 属性,VITE_APP_NAME 不会被注入
|
|
1222
|
+
}
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
3. **黑名单配置**
|
|
1226
|
+
|
|
1227
|
+
```typescript
|
|
1228
|
+
// config/common.ts
|
|
445
1229
|
export const envBlackList = ['env', 'userNodeEnv'];
|
|
446
|
-
//
|
|
1230
|
+
// 确保你的属性不在黑名单中
|
|
447
1231
|
```
|
|
448
1232
|
|
|
449
|
-
###
|
|
1233
|
+
### Q2: 如何在不同环境使用不同配置?
|
|
1234
|
+
|
|
1235
|
+
**A:** 使用 `--mode` 参数:
|
|
450
1236
|
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
console.log('AppConfig:', config);
|
|
458
|
-
console.log('All Env Vars:', import.meta.env);
|
|
459
|
-
console.groupEnd();
|
|
1237
|
+
```json
|
|
1238
|
+
{
|
|
1239
|
+
"scripts": {
|
|
1240
|
+
"dev": "vite --mode localhost", // 加载 .env.localhost
|
|
1241
|
+
"dev:staging": "vite --mode staging", // 加载 .env.staging
|
|
1242
|
+
"build:prod": "vite build --mode production" // 加载 .env.production
|
|
460
1243
|
}
|
|
461
1244
|
}
|
|
1245
|
+
```
|
|
462
1246
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
1247
|
+
### Q3: 如何处理敏感信息?
|
|
1248
|
+
|
|
1249
|
+
**A:** 使用 `.env.local`:
|
|
1250
|
+
|
|
1251
|
+
```bash
|
|
1252
|
+
# .env.local(不提交到 git)
|
|
1253
|
+
VITE_AI_API_TOKEN=sk-your-secret-key
|
|
1254
|
+
|
|
1255
|
+
# .gitignore
|
|
1256
|
+
.env.local
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
### Q4: 能否在运行时动态修改配置?
|
|
1260
|
+
|
|
1261
|
+
**A:** 可以,但需要在 Bootstrap 初始化前:
|
|
1262
|
+
|
|
1263
|
+
```typescript
|
|
1264
|
+
const appConfig = new AppConfig();
|
|
1265
|
+
|
|
1266
|
+
// ✅ 在 Bootstrap 初始化前修改
|
|
1267
|
+
(appConfig as any).feApiBaseUrl = 'https://custom-api.com';
|
|
1268
|
+
|
|
1269
|
+
const bootstrap = new Bootstrap({
|
|
1270
|
+
envOptions: {
|
|
1271
|
+
target: appConfig, // 使用修改后的配置
|
|
1272
|
+
source: import.meta.env,
|
|
1273
|
+
prefix: 'VITE_',
|
|
1274
|
+
blackList: envBlackList
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
await bootstrap.initialize();
|
|
467
1279
|
```
|
|
468
1280
|
|
|
469
|
-
|
|
1281
|
+
### Q5: 为什么推荐通过 AppConfig 而不是直接使用 import.meta.env?
|
|
1282
|
+
|
|
1283
|
+
**A:**
|
|
1284
|
+
|
|
1285
|
+
| 特性 | import.meta.env | AppConfig |
|
|
1286
|
+
| -------------- | ------------------- | --------------- |
|
|
1287
|
+
| **类型安全** | ❌ 可能为 undefined | ✅ 完整类型定义 |
|
|
1288
|
+
| **默认值** | ❌ 没有 | ✅ 有默认值 |
|
|
1289
|
+
| **可测试性** | ❌ 难以 mock | ✅ 易于 mock |
|
|
1290
|
+
| **集中管理** | ❌ 分散各处 | ✅ 统一管理 |
|
|
1291
|
+
| **运行时修改** | ❌ 不可能 | ✅ 可以 |
|
|
1292
|
+
|
|
1293
|
+
### Q6: 环境变量和配置文件有什么区别?
|
|
1294
|
+
|
|
1295
|
+
**A:**
|
|
1296
|
+
|
|
1297
|
+
**环境变量:** 适用于:
|
|
1298
|
+
|
|
1299
|
+
- 不同环境的配置(API 地址、Token 等)
|
|
1300
|
+
- 敏感信息
|
|
1301
|
+
- 部署时需要修改的配置
|
|
1302
|
+
|
|
1303
|
+
**配置文件(config/):** 适用于:
|
|
1304
|
+
|
|
1305
|
+
- 应用逻辑配置(路由、主题、i18n 等)
|
|
1306
|
+
- 不随环境变化的配置
|
|
1307
|
+
- 代码级的配置
|
|
1308
|
+
|
|
1309
|
+
---
|
|
1310
|
+
|
|
1311
|
+
## 📚 相关文档
|
|
1312
|
+
|
|
1313
|
+
- [项目架构设计](./index.md) - 了解整体架构
|
|
1314
|
+
- [Bootstrap 启动器](./bootstrap.md) - Bootstrap 详解
|
|
1315
|
+
- [IOC 容器](./ioc.md) - 依赖注入详解
|
|
1316
|
+
- [全局变量封装](./global.md) - 浏览器 API 封装
|
|
1317
|
+
|
|
1318
|
+
---
|
|
1319
|
+
|
|
1320
|
+
## 🎉 总结
|
|
1321
|
+
|
|
1322
|
+
环境变量管理系统通过 **Bootstrap + AppConfig + IOC** 的组合,提供了:
|
|
1323
|
+
|
|
1324
|
+
1. **环境隔离** 🌍 - 不同环境使用不同配置,无需修改代码
|
|
1325
|
+
2. **类型安全** 🔒 - 通过 TypeScript 提供完整的类型检查
|
|
1326
|
+
3. **集中管理** 📦 - 所有配置在 AppConfig 中统一管理
|
|
1327
|
+
4. **自动注入** ⚡ - Bootstrap 自动将环境变量注入到 AppConfig
|
|
1328
|
+
5. **易于测试** 🧪 - 可以轻松 mock AppConfig 进行测试
|
|
1329
|
+
6. **灵活扩展** 🚀 - 支持多种配置源和动态修改
|
|
470
1330
|
|
|
471
|
-
|
|
1331
|
+
通过合理使用环境变量管理,你可以构建一个更加健壮、灵活、易于维护的应用架构。
|
|
472
1332
|
|
|
473
|
-
|
|
474
|
-
2. **类型安全**:通过 TypeScript 提供类型检查
|
|
475
|
-
3. **集中管理**:所有配置在 AppConfig 中统一管理
|
|
476
|
-
4. **灵活配置**:支持多种环境变量文件
|
|
477
|
-
5. **安全处理**:敏感信息可以通过 .env.local 本地管理
|
|
1333
|
+
---
|
|
478
1334
|
|
|
479
|
-
|
|
1335
|
+
**问题反馈:**
|
|
1336
|
+
如果你对环境变量管理有任何疑问或建议,请在团队频道中讨论或提交 Issue。
|