├── modules ├── app │ ├── src │ │ ├── layouts │ │ │ ├── .gitkeep │ │ │ └── Common │ │ │ │ ├── style.scss │ │ │ │ └── index.tsx │ │ ├── vite-env.d.ts │ │ ├── main.tsx │ │ ├── App.tsx │ │ ├── routes │ │ │ ├── index.tsx │ │ │ └── config.tsx │ │ ├── pages │ │ │ ├── login │ │ │ │ ├── style.module.scss │ │ │ │ └── index.tsx │ │ │ └── home.tsx │ │ ├── index.scss │ │ └── components │ │ │ └── MicroApp.tsx │ ├── .gitignore │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ └── package.json ├── README.md ├── demo-react-1 │ ├── src │ │ ├── vite-env.d.ts │ │ ├── index.scss │ │ ├── hmr.fix.ts │ │ ├── pages │ │ │ └── home.tsx │ │ ├── routes.tsx │ │ └── main.tsx │ ├── .gitignore │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ └── package.json ├── demo-react-2 │ ├── src │ │ ├── vite-env.d.ts │ │ ├── index.scss │ │ ├── hmr.fix.ts │ │ ├── pages │ │ │ └── home.tsx │ │ ├── routes.tsx │ │ └── main.tsx │ ├── .gitignore │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ └── package.json ├── demo-vue-1 │ ├── src │ │ ├── index.scss │ │ ├── assets │ │ │ └── logo.png │ │ ├── env.d.ts │ │ ├── App.vue │ │ ├── main.ts │ │ └── components │ │ │ └── HelloWorld.vue │ ├── postcss.config.js │ ├── public │ │ └── favicon.ico │ ├── tsconfig.node.json │ ├── tailwind.config.js │ ├── .gitignore │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ ├── package.json │ └── README.md └── demo-vue-2 │ ├── src │ ├── index.scss │ ├── assets │ │ └── logo.png │ ├── env.d.ts │ ├── App.vue │ ├── main.ts │ └── components │ │ └── HelloWorld.vue │ ├── postcss.config.js │ ├── public │ └── favicon.ico │ ├── tsconfig.node.json │ ├── tailwind.config.js │ ├── .gitignore │ ├── index.html │ ├── vite.config.ts │ ├── tsconfig.json │ ├── package.json │ └── README.md ├── .gitignore ├── packages ├── README.md ├── shared │ ├── vite-env.d.ts │ ├── layouts │ │ └── Blank.tsx │ ├── index.ts │ ├── components │ │ └── Lazy.tsx │ ├── hooks │ │ ├── useAccount.tsx │ │ └── useAppConfig.tsx │ └── package.json ├── assets │ └── package.json └── build │ ├── package.json │ └── vite.config.js ├── legacies └── README.md ├── templates ├── micro-react-vite │ ├── src │ │ ├── vite-env.d.ts │ │ ├── index.scss │ │ ├── hmr.fix.ts.tpl │ │ ├── pages │ │ │ └── home.tsx.tpl │ │ ├── routes.tsx │ │ └── main.tsx.tpl │ ├── .gitignore │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── index.html.tpl │ ├── vite.config.ts.tpl │ ├── tsconfig.json │ └── package.json.tpl ├── README.md └── micro-vue-vite │ ├── src │ ├── index.scss │ ├── assets │ │ └── logo.png │ ├── env.d.ts │ ├── App.vue.tpl │ ├── main.ts.tpl │ └── components │ │ └── HelloWorld.vue │ ├── postcss.config.js │ ├── public │ └── favicon.ico │ ├── tailwind.config.js │ ├── tsconfig.node.json │ ├── .gitignore │ ├── index.html.tpl │ ├── vite.config.ts.tpl │ ├── tsconfig.json │ ├── package.json.tpl │ └── README.md ├── pnpm-workspace.yaml ├── .npmrc ├── .prettierrc ├── scripts ├── create │ ├── Generators │ │ └── PkgGenerator.js │ └── index.js └── afterBuild.js ├── package.json └── README.md /modules/app/src/layouts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | # pnpm-lock.yaml 4 | -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | # 包目录 2 | 3 | 用来存放无状态的、非应用的共享模块 4 | -------------------------------------------------------------------------------- /modules/app/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/shared/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /legacies/README.md: -------------------------------------------------------------------------------- 1 | # 遗产目录 2 | 3 | 用来存放历史遗留的项目 4 | 5 | 此文件夹内的依赖独立,不与外部共享 6 | -------------------------------------------------------------------------------- /modules/README.md: -------------------------------------------------------------------------------- 1 | # 模块目录 2 | 3 | 用来存放各类应用,包括基座与子应用 4 | 5 | 此处 app 为基座应用 6 | -------------------------------------------------------------------------------- /modules/demo-react-1/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /modules/demo-react-2/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /modules/app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /templates/micro-react-vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /modules/demo-react-1/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /modules/demo-react-2/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'legacies/**' 3 | - 'modules/**' 4 | - 'packages/**' 5 | -------------------------------------------------------------------------------- /templates/README.md: -------------------------------------------------------------------------------- 1 | # 模板目录 2 | 3 | 模板语法参考 [Mustache](https://github.com/janl/mustache.js) 4 | -------------------------------------------------------------------------------- /packages/assets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@packages/assets", 3 | "version": "0.0.1" 4 | } 5 | -------------------------------------------------------------------------------- /templates/micro-react-vite/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /modules/demo-react-1/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /modules/demo-react-2/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /modules/demo-vue-1/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /modules/demo-vue-2/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # .npmrc 2 | shamefully-hoist=true # pnpm 依赖打平 3 | # shared-workspace-lockfile=false # 不共享 lock 文件 4 | -------------------------------------------------------------------------------- /templates/micro-react-vite/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /modules/app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /modules/demo-vue-1/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CJY0208/mono-micro-project/HEAD/modules/demo-vue-1/src/assets/logo.png -------------------------------------------------------------------------------- /modules/demo-vue-2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CJY0208/mono-micro-project/HEAD/modules/demo-vue-2/src/assets/logo.png -------------------------------------------------------------------------------- /modules/demo-vue-1/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /modules/demo-vue-2/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /modules/demo-react-1/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /modules/demo-react-2/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /templates/micro-react-vite/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CJY0208/mono-micro-project/HEAD/templates/micro-vue-vite/src/assets/logo.png -------------------------------------------------------------------------------- /modules/demo-vue-1/public/favicon.ico: -------------------------------------------------------------------------------- 1 | https://cdn.jsdelivr.net/gh/vitejs/vite@0858450b2a258b216ae9aa797cc02e9a0d4eb0af/packages/create-vite/template-vue-ts/public/favicon.ico -------------------------------------------------------------------------------- /modules/demo-vue-2/public/favicon.ico: -------------------------------------------------------------------------------- 1 | https://cdn.jsdelivr.net/gh/vitejs/vite@0858450b2a258b216ae9aa797cc02e9a0d4eb0af/packages/create-vite/template-vue-ts/public/favicon.ico -------------------------------------------------------------------------------- /packages/shared/layouts/Blank.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Outlet } from 'react-router-dom' 3 | 4 | export default function Blank() { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/public/favicon.ico: -------------------------------------------------------------------------------- 1 | https://cdn.jsdelivr.net/gh/vitejs/vite@0858450b2a258b216ae9aa797cc02e9a0d4eb0af/packages/create-vite/template-vue-ts/public/favicon.ico -------------------------------------------------------------------------------- /packages/shared/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export const test1 = () => { 3 | const name = 'testasdf' 4 | console.log(name) 5 | 6 | return name 7 | } 8 | 9 | // export {} from 10 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | } 8 | -------------------------------------------------------------------------------- /templates/micro-react-vite/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | } 8 | -------------------------------------------------------------------------------- /modules/demo-vue-1/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /modules/demo-vue-2/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /modules/app/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | 4 | import App from './App' 5 | import './index.scss' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "esnext", 5 | "moduleResolution": "node" 6 | }, 7 | "include": ["vite.config.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /modules/app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | corePlugins: { 8 | preflight: false, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/demo-vue-1/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | corePlugins: { 8 | preflight: false, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/demo-vue-2/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | corePlugins: { 8 | preflight: false, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/demo-react-1/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | corePlugins: { 8 | preflight: false, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/demo-react-2/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | corePlugins: { 8 | preflight: false, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/app/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter } from 'react-router-dom' 3 | import Routes from '@/routes' 4 | 5 | export default function App() { 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /modules/demo-react-1/src/hmr.fix.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import RefreshRuntime from '/demo-react-1/@react-refresh' 3 | RefreshRuntime.injectIntoGlobalHook(window) 4 | window.$RefreshReg$ = () => {} 5 | window.$RefreshSig$ = () => (type) => type 6 | window.__vite_plugin_react_preamble_installed__ = true 7 | -------------------------------------------------------------------------------- /modules/demo-react-2/src/hmr.fix.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import RefreshRuntime from '/demo-react-2/@react-refresh' 3 | RefreshRuntime.injectIntoGlobalHook(window) 4 | window.$RefreshReg$ = () => {} 5 | window.$RefreshSig$ = () => (type) => type 6 | window.__vite_plugin_react_preamble_installed__ = true 7 | -------------------------------------------------------------------------------- /templates/micro-react-vite/src/hmr.fix.ts.tpl: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | import RefreshRuntime from '/{{ name }}/@react-refresh' 3 | RefreshRuntime.injectIntoGlobalHook(window) 4 | window.$RefreshReg$ = () => {} 5 | window.$RefreshSig$ = () => (type) => type 6 | window.__vite_plugin_react_preamble_installed__ = true 7 | -------------------------------------------------------------------------------- /packages/shared/components/Lazy.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense, useMemo, lazy } from 'react' 2 | 3 | export default function Lazy({ entry }: any) { 4 | const Comp = useMemo(() => lazy(entry), []) 5 | 6 | return ( 7 | 8 | 9 | 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /modules/app/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useRoutes } from 'react-router-dom' 3 | 4 | import useRoutesConfig from './config' 5 | 6 | export default function Routes() { 7 | const config = useRoutesConfig() 8 | const elements = useRoutes(config) 9 | 10 | return <>{elements} 11 | } 12 | -------------------------------------------------------------------------------- /modules/demo-vue-1/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /modules/demo-vue-2/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /packages/build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@packages/build", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "@types/react": "^17.0.0", 6 | "@types/react-dom": "^17.0.0", 7 | "@vitejs/plugin-react": "^1.3.2", 8 | "@vitejs/plugin-vue": "^2.3.2", 9 | "vite": "^2.9.8", 10 | "vite-plugin-imp": "^2.1.8" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "semi": false, 4 | "singleQuote": true, 5 | "htmlWhitespaceSensitivity": "ignore", 6 | "endOfLine": "auto", 7 | "overrides": [ 8 | { 9 | "files": ["*.json", ".babelrc", ".prettierrc"], 10 | "options": { 11 | "parser": "json", 12 | "tabWidth": 2 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /modules/demo-vue-1/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /modules/demo-vue-2/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /modules/app/src/pages/login/style.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: #2d3a4b; 3 | 4 | :global { 5 | .ant-input { 6 | background: transparent; 7 | color: #fff; 8 | } 9 | 10 | .ant-input-affix-wrapper { 11 | height: 48px; 12 | background: rgba(0, 0, 0, 0.1); 13 | border: 1px solid rgba(255, 255, 255, 0.1); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/shared/hooks/useAccount.tsx: -------------------------------------------------------------------------------- 1 | import { createModel } from 'hox' 2 | import { useLocalStorageState } from 'ahooks' 3 | 4 | function useAccount() { 5 | const [account, setAccount] = useLocalStorageState( 6 | '@packages/shared:useAccount-account' 7 | ) 8 | 9 | return { 10 | account, 11 | setAccount, 12 | } 13 | } 14 | 15 | export default createModel(useAccount) 16 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /modules/demo-vue-1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /modules/demo-vue-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/index.html.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /modules/demo-react-1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /modules/demo-react-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /modules/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Mono Micro Project 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/shared/hooks/useAppConfig.tsx: -------------------------------------------------------------------------------- 1 | import { createModel } from 'hox' 2 | import { useLocalStorageState } from 'ahooks' 3 | 4 | function useAppConfig() { 5 | const [locale, setLocale] = useLocalStorageState( 6 | '@packages/shared:useAppConfig-locale', 7 | { 8 | defaultValue: 'zh-CN', 9 | } 10 | ) 11 | 12 | return { 13 | locale, 14 | setLocale, 15 | } 16 | } 17 | 18 | export default createModel(useAppConfig) 19 | -------------------------------------------------------------------------------- /templates/micro-react-vite/index.html.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /modules/demo-vue-1/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vite' 2 | import { getConfig } from '@packages/build/vite.config' 3 | 4 | const config = getConfig({ 5 | type: 'vue', 6 | micro: true, 7 | moduleName: 'demo-vue-1', 8 | dirname: __dirname, 9 | }) 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig( 13 | mergeConfig(config, { 14 | server: { 15 | host: true, 16 | port: 8083, 17 | }, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /modules/demo-vue-2/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vite' 2 | import { getConfig } from '@packages/build/vite.config' 3 | 4 | const config = getConfig({ 5 | type: 'vue', 6 | micro: true, 7 | moduleName: 'demo-vue-2', 8 | dirname: __dirname, 9 | }) 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig( 13 | mergeConfig(config, { 14 | server: { 15 | host: true, 16 | port: 8084, 17 | }, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /modules/demo-react-1/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vite' 2 | import { getConfig } from '@packages/build/vite.config' 3 | 4 | const config = getConfig({ 5 | type: 'react', 6 | micro: true, 7 | moduleName: 'demo-react-1', 8 | dirname: __dirname, 9 | }) 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig( 13 | mergeConfig(config, { 14 | server: { 15 | host: true, 16 | port: 8081, 17 | }, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /modules/demo-react-2/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vite' 2 | import { getConfig } from '@packages/build/vite.config' 3 | 4 | const config = getConfig({ 5 | type: 'react', 6 | micro: true, 7 | moduleName: 'demo-react-2', 8 | dirname: __dirname, 9 | }) 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig( 13 | mergeConfig(config, { 14 | server: { 15 | host: true, 16 | port: 8082, 17 | }, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/vite.config.ts.tpl: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vite' 2 | import { getConfig } from '@packages/build/vite.config' 3 | 4 | const config = getConfig({ 5 | type: 'vue', 6 | micro: true, 7 | moduleName: '{{ name }}', 8 | dirname: __dirname, 9 | }) 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig( 13 | mergeConfig(config, { 14 | server: { 15 | host: true, 16 | port: 8181, 17 | }, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /templates/micro-react-vite/vite.config.ts.tpl: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig } from 'vite' 2 | import { getConfig } from '@packages/build/vite.config' 3 | 4 | const config = getConfig({ 5 | type: 'react', 6 | micro: true, 7 | moduleName: '{{ name }}', 8 | dirname: __dirname, 9 | }) 10 | 11 | // https://vitejs.dev/config/ 12 | export default defineConfig( 13 | mergeConfig(config, { 14 | server: { 15 | host: true, 16 | port: 8181, 17 | }, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /modules/demo-vue-1/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /modules/demo-vue-2/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/src/App.vue.tpl: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /modules/app/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export default function Home() { 4 | const [count, setCount] = useState(0) 5 | 6 | return ( 7 |
8 |
Home
9 |
count: {count}
10 | 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /modules/demo-react-1/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export default function Home() { 4 | const [count, setCount] = useState(0) 5 | 6 | return ( 7 |
8 |
demo-react-1 Home
9 |
count: {count}
10 | 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /modules/demo-react-2/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export default function Home() { 4 | const [count, setCount] = useState(0) 5 | 6 | return ( 7 |
8 |
demo-react-2 Home
9 |
count: {count}
10 | 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /templates/micro-react-vite/src/pages/home.tsx.tpl: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | 3 | export default function Home() { 4 | const [count, setCount] = useState(0) 5 | 6 | return ( 7 |
8 |
{{ name }} Home
9 |
count: {count}
10 | 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /scripts/create/Generators/PkgGenerator.js: -------------------------------------------------------------------------------- 1 | const { Generator } = require('@umijs/utils') 2 | const path = require('path') 3 | 4 | class PkgGenerator extends Generator { 5 | constructor({ name, template, ...prop }) { 6 | super(prop) 7 | this.name = name 8 | this.template = template 9 | } 10 | 11 | async writing() { 12 | this.copyDirectory({ 13 | context: { 14 | name: this.name, 15 | template: this.template, 16 | }, 17 | path: path.join(this.cwd, `./templates/${this.template}`), 18 | target: path.join(this.cwd, `./modules/${this.name}`), 19 | }) 20 | } 21 | } 22 | 23 | module.exports = PkgGenerator 24 | -------------------------------------------------------------------------------- /modules/demo-vue-1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["esnext", "dom"], 14 | "skipLibCheck": true, 15 | "baseUrl": ".", 16 | "paths": { 17 | "@/*": ["src/*"] 18 | } 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 21 | "references": [{ "path": "./tsconfig.node.json" }] 22 | } 23 | -------------------------------------------------------------------------------- /modules/demo-vue-2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["esnext", "dom"], 14 | "skipLibCheck": true, 15 | "baseUrl": ".", 16 | "paths": { 17 | "@/*": ["src/*"] 18 | } 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 21 | "references": [{ "path": "./tsconfig.node.json" }] 22 | } 23 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["esnext", "dom"], 14 | "skipLibCheck": true, 15 | "baseUrl": ".", 16 | "paths": { 17 | "@/*": ["src/*"] 18 | } 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 21 | "references": [{ "path": "./tsconfig.node.json" }] 22 | } 23 | -------------------------------------------------------------------------------- /modules/app/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, mergeConfig, UserConfig } from 'vite' 2 | import { ip, getConfig } from '@packages/build/vite.config' 3 | 4 | const sharedConfig = getConfig({ 5 | type: 'react', 6 | dirname: __dirname, 7 | }) 8 | 9 | // https://vitejs.dev/config/ 10 | export default defineConfig( 11 | mergeConfig(sharedConfig, { 12 | server: { 13 | host: true, 14 | port: 8080, 15 | proxy: { 16 | '/demo-react-1': 'http://localhost:8081/', 17 | '/demo-react-2': 'http://localhost:8082/', 18 | '/demo-vue-1': 'http://localhost:8083/', 19 | '/demo-vue-2': 'http://localhost:8084/', 20 | }, 21 | }, 22 | } as UserConfig) 23 | ) 24 | -------------------------------------------------------------------------------- /modules/demo-vue-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/demo-vue-1", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.2.33", 12 | "vite-plugin-qiankun": "^1.0.14" 13 | }, 14 | "devDependencies": { 15 | "@packages/build": "^0.0.1", 16 | "@vitejs/plugin-vue": "^2.3.2", 17 | "autoprefixer": "^10.4.7", 18 | "less": "^4.1.2", 19 | "postcss": "^8.4.14", 20 | "sass": "^1.51.0", 21 | "tailwindcss": "^3.0.24", 22 | "typescript": "^4.6.4", 23 | "vite": "^2.9.8", 24 | "vue-tsc": "^0.34.11" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /modules/demo-vue-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/demo-vue-2", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.2.33", 12 | "vite-plugin-qiankun": "^1.0.14" 13 | }, 14 | "devDependencies": { 15 | "@packages/build": "^0.0.1", 16 | "@vitejs/plugin-vue": "^2.3.2", 17 | "autoprefixer": "^10.4.7", 18 | "less": "^4.1.2", 19 | "postcss": "^8.4.14", 20 | "sass": "^1.51.0", 21 | "tailwindcss": "^3.0.24", 22 | "typescript": "^4.6.4", 23 | "vite": "^2.9.8", 24 | "vue-tsc": "^0.34.11" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /modules/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "preserveSymlinks": true, 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "allowJs": false, 8 | "skipLibCheck": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "module": "ESNext", 14 | "moduleResolution": "Node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "react", 19 | "baseUrl": ".", 20 | "paths": { 21 | "@/*": ["src/*"] 22 | } 23 | }, 24 | "include": ["./src"] 25 | } 26 | -------------------------------------------------------------------------------- /modules/demo-react-1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "preserveSymlinks": true, 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "allowJs": false, 8 | "skipLibCheck": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "module": "ESNext", 14 | "moduleResolution": "Node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "react", 19 | "baseUrl": ".", 20 | "paths": { 21 | "@/*": ["src/*"] 22 | } 23 | }, 24 | "include": ["./src"] 25 | } 26 | -------------------------------------------------------------------------------- /modules/demo-react-2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "preserveSymlinks": true, 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "allowJs": false, 8 | "skipLibCheck": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "module": "ESNext", 14 | "moduleResolution": "Node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "react", 19 | "baseUrl": ".", 20 | "paths": { 21 | "@/*": ["src/*"] 22 | } 23 | }, 24 | "include": ["./src"] 25 | } 26 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/package.json.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/{{ name }}", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "vue": "^3.2.33", 12 | "vite-plugin-qiankun": "^1.0.14" 13 | }, 14 | "devDependencies": { 15 | "@packages/build": "^0.0.1", 16 | "@vitejs/plugin-vue": "^2.3.2", 17 | "autoprefixer": "^10.4.7", 18 | "less": "^4.1.2", 19 | "postcss": "^8.4.14", 20 | "sass": "^1.51.0", 21 | "tailwindcss": "^3.0.24", 22 | "typescript": "^4.6.4", 23 | "vite": "^2.9.8", 24 | "vue-tsc": "^0.34.11" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /templates/micro-react-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "preserveSymlinks": true, 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 7 | "allowJs": false, 8 | "skipLibCheck": true, 9 | "esModuleInterop": false, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "module": "ESNext", 14 | "moduleResolution": "Node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "noEmit": true, 18 | "jsx": "react", 19 | "baseUrl": ".", 20 | "paths": { 21 | "@/*": ["src/*"] 22 | } 23 | }, 24 | "include": ["./src"] 25 | } 26 | -------------------------------------------------------------------------------- /scripts/create/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const { argv } = require('yargs') 4 | 5 | const PkgGenerator = require('./generators/PkgGenerator') 6 | 7 | const br = ` 8 | ` 9 | if (!argv.name) { 10 | console.log('请使用 --name=XXX 参数提供模块名') 11 | process.exit(0) 12 | } 13 | 14 | const cwd = process.cwd() 15 | const names = argv.name.split(',') 16 | const template = argv.template || 'micro-react-vite' 17 | 18 | async function create(name) { 19 | if (argv.force || !fs.existsSync(path.join(cwd, `./modules/${name}`))) { 20 | await new PkgGenerator({ 21 | name, 22 | cwd, 23 | template, 24 | }).run() 25 | } else { 26 | console.log(`/modules/${name} 已存在,取消创建源码`) 27 | } 28 | } 29 | 30 | async function run() { 31 | await Promise.all(names.map(create)) 32 | } 33 | 34 | run() 35 | -------------------------------------------------------------------------------- /modules/app/src/index.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html, 6 | body, 7 | #root { 8 | width: 100%; 9 | height: 100%; 10 | overflow: hidden; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | } 16 | 17 | * { 18 | -webkit-overflow-scrolling: touch; 19 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 20 | -webkit-text-size-adjust: 100%; 21 | 22 | &:hover { 23 | &::-webkit-scrollbar-thumb { 24 | background-color: #999; 25 | } 26 | } 27 | 28 | &::-webkit-scrollbar { 29 | width: 6px; 30 | height: 6px; 31 | } 32 | 33 | &::-webkit-scrollbar-track { 34 | border-radius: 6px; 35 | background-color: transparent; 36 | } 37 | 38 | &::-webkit-scrollbar-thumb { 39 | border-radius: 6px; 40 | background-color: transparent; 41 | transition: background-color 0.2s; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /modules/demo-react-1/src/routes.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import { HashRouter, useRoutes } from 'react-router-dom' 3 | import { Spin } from 'antd' 4 | import Lazy from '@packages/shared/components/Lazy' 5 | import BlankLayout from '@packages/shared/layouts/Blank' 6 | 7 | function Routes() { 8 | const elements = useRoutes([ 9 | { 10 | element: , 11 | children: [ 12 | { 13 | path: '/', 14 | element: import('@/pages/home')} />, 15 | }, 16 | ], 17 | }, 18 | { 19 | path: '*', 20 | element:
404
, 21 | }, 22 | ]) 23 | 24 | return ( 25 | <> 26 | }>{elements} 27 | 28 | ) 29 | } 30 | 31 | export default () => ( 32 | 33 | 34 | 35 | ) 36 | -------------------------------------------------------------------------------- /modules/demo-react-2/src/routes.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import { HashRouter, useRoutes } from 'react-router-dom' 3 | import { Spin } from 'antd' 4 | import Lazy from '@packages/shared/components/Lazy' 5 | import BlankLayout from '@packages/shared/layouts/Blank' 6 | 7 | function Routes() { 8 | const elements = useRoutes([ 9 | { 10 | element: , 11 | children: [ 12 | { 13 | path: '/', 14 | element: import('@/pages/home')} />, 15 | }, 16 | ], 17 | }, 18 | { 19 | path: '*', 20 | element:
404
, 21 | }, 22 | ]) 23 | 24 | return ( 25 | <> 26 | }>{elements} 27 | 28 | ) 29 | } 30 | 31 | export default () => ( 32 | 33 | 34 | 35 | ) 36 | -------------------------------------------------------------------------------- /templates/micro-react-vite/src/routes.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import { HashRouter, useRoutes } from 'react-router-dom' 3 | import { Spin } from 'antd' 4 | import Lazy from '@packages/shared/components/Lazy' 5 | import BlankLayout from '@packages/shared/layouts/Blank' 6 | 7 | function Routes() { 8 | const elements = useRoutes([ 9 | { 10 | element: , 11 | children: [ 12 | { 13 | path: '/', 14 | element: import('@/pages/home')} />, 15 | }, 16 | ], 17 | }, 18 | { 19 | path: '*', 20 | element:
404
, 21 | }, 22 | ]) 23 | 24 | return ( 25 | <> 26 | }>{elements} 27 | 28 | ) 29 | } 30 | 31 | export default () => ( 32 | 33 | 34 | 35 | ) 36 | -------------------------------------------------------------------------------- /packages/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@packages/shared", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "@ant-design/icons": "^4.7.0", 6 | "@packages/shared": "^0.0.1", 7 | "@types/md5": "^2.3.2", 8 | "ahooks": "^3.3.10", 9 | "antd": "^4.20.1", 10 | "axios": "^0.27.2", 11 | "hox": "1.0.2", 12 | "lodash": "^4.17.21", 13 | "md5": "^2.3.0", 14 | "qiankun": "^2.7.0", 15 | "qs": "^6.10.3", 16 | "react": "^17.0.0", 17 | "react-dom": "^17.0.0", 18 | "react-router-dom": "^6.3.0", 19 | "vite-plugin-qiankun": "^1.0.14" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^17.0.0", 23 | "@types/react-dom": "^17.0.0", 24 | "@vitejs/plugin-react": "^1.3.2", 25 | "@vitejs/plugin-vue": "^2.3.2", 26 | "ip": "^1.1.5", 27 | "less-plugin-import-node-modules": "^1.0.0", 28 | "vite": "^2.9.8", 29 | "vite-plugin-imp": "^2.1.8" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/afterBuild.js: -------------------------------------------------------------------------------- 1 | const rimraf = require('rimraf') 2 | const fs = require('fs-extra') 3 | 4 | rimraf.sync('./dist') 5 | 6 | try { 7 | fs.copySync('./modules/app/dist', './dist', { 8 | overwrite: true, 9 | }) 10 | } catch (err) { 11 | console.error(err) 12 | } 13 | 14 | // const legacyModules = { 15 | // vue: 'dist', 16 | // react: 'build', 17 | // } 18 | 19 | // Object.entries(legacyModules).forEach(([name, distDir]) => { 20 | // try { 21 | // fs.copySync(`./legacies/${name}/${distDir}`, `./dist/${name}`, { 22 | // overwrite: true, 23 | // }) 24 | // } catch (err) { 25 | // console.error(err) 26 | // } 27 | // }) 28 | 29 | const microModules = { 30 | 'demo-react-1': 'dist', 31 | 'demo-react-2': 'dist', 32 | 'demo-vue-1': 'dist', 33 | 'demo-vue-2': 'dist', 34 | } 35 | 36 | Object.entries(microModules).forEach(([name, distDir]) => { 37 | try { 38 | fs.copySync(`./modules/${name}/${distDir}`, `./dist/${name}`, { 39 | overwrite: true, 40 | }) 41 | } catch (err) { 42 | console.error(err) 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /modules/app/src/layouts/Common/style.scss: -------------------------------------------------------------------------------- 1 | .common-layout-wrapper { 2 | height: 100vh; 3 | width: 100vw; 4 | 5 | .trigger { 6 | padding: 0 24px; 7 | font-size: 18px; 8 | line-height: 64px; 9 | cursor: pointer; 10 | transition: color 0.3s; 11 | } 12 | 13 | .trigger:hover { 14 | color: #1890ff; 15 | } 16 | 17 | .logo { 18 | height: 32px; 19 | margin: 16px; 20 | background: rgba(255, 255, 255, 0.3); 21 | } 22 | 23 | .ant-layout { 24 | height: 100vh; 25 | width: 100vw; 26 | } 27 | 28 | .ant-layout-content { 29 | overflow: auto; 30 | } 31 | 32 | .common-layout-header { 33 | border-bottom: solid 1px #f2f2f2; 34 | 35 | &-content { 36 | display: flex; 37 | align-items: center; 38 | justify-content: space-between; 39 | } 40 | } 41 | 42 | .common-layout-header, 43 | .common-layout-content { 44 | background: #fff; 45 | } 46 | 47 | .common-layout-content { 48 | padding: 24px; 49 | min-height: 100%; 50 | } 51 | 52 | .common-layout-header-content-right { 53 | padding: 0 24px; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /modules/demo-react-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/demo-react-1", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@ant-design/icons": "^4.7.0", 11 | "@packages/shared": "^0.0.1", 12 | "@types/md5": "^2.3.2", 13 | "ahooks": "^3.3.10", 14 | "antd": "^4.20.1", 15 | "axios": "^0.27.2", 16 | "hox": "1.0.2", 17 | "md5": "^2.3.0", 18 | "react": "^17.0.0", 19 | "react-dom": "^17.0.0", 20 | "react-router-dom": "^6.3.0", 21 | "react-i18next": "^11.16.9", 22 | "vite-plugin-qiankun": "^1.0.14" 23 | }, 24 | "devDependencies": { 25 | "@packages/build": "^0.0.1", 26 | "@types/react": "^17.0.0", 27 | "@types/react-dom": "^17.0.0", 28 | "@vitejs/plugin-react": "^1.3.2", 29 | "autoprefixer": "^10.4.7", 30 | "less": "^4.1.2", 31 | "postcss": "^8.4.14", 32 | "sass": "^1.51.0", 33 | "tailwindcss": "^3.0.24", 34 | "typescript": "^4.3.2", 35 | "vite": "^2.9.8", 36 | "vite-plugin-imp": "^2.1.8" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/demo-react-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/demo-react-2", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@ant-design/icons": "^4.7.0", 11 | "@packages/shared": "^0.0.1", 12 | "@types/md5": "^2.3.2", 13 | "ahooks": "^3.3.10", 14 | "antd": "^4.20.1", 15 | "axios": "^0.27.2", 16 | "hox": "1.0.2", 17 | "md5": "^2.3.0", 18 | "react": "^17.0.0", 19 | "react-dom": "^17.0.0", 20 | "react-router-dom": "^6.3.0", 21 | "react-i18next": "^11.16.9", 22 | "vite-plugin-qiankun": "^1.0.14" 23 | }, 24 | "devDependencies": { 25 | "@packages/build": "^0.0.1", 26 | "@types/react": "^17.0.0", 27 | "@types/react-dom": "^17.0.0", 28 | "@vitejs/plugin-react": "^1.3.2", 29 | "autoprefixer": "^10.4.7", 30 | "less": "^4.1.2", 31 | "postcss": "^8.4.14", 32 | "sass": "^1.51.0", 33 | "tailwindcss": "^3.0.24", 34 | "typescript": "^4.3.2", 35 | "vite": "^2.9.8", 36 | "vite-plugin-imp": "^2.1.8" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /templates/micro-react-vite/package.json.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/{{ name }}", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@ant-design/icons": "^4.7.0", 11 | "@packages/shared": "^0.0.1", 12 | "@types/md5": "^2.3.2", 13 | "ahooks": "^3.3.10", 14 | "antd": "^4.20.1", 15 | "axios": "^0.27.2", 16 | "hox": "1.0.2", 17 | "md5": "^2.3.0", 18 | "react": "^17.0.0", 19 | "react-dom": "^17.0.0", 20 | "react-router-dom": "^6.3.0", 21 | "react-i18next": "^11.16.9", 22 | "vite-plugin-qiankun": "^1.0.14" 23 | }, 24 | "devDependencies": { 25 | "@packages/build": "^0.0.1", 26 | "@types/react": "^17.0.0", 27 | "@types/react-dom": "^17.0.0", 28 | "@vitejs/plugin-react": "^1.3.2", 29 | "autoprefixer": "^10.4.7", 30 | "less": "^4.1.2", 31 | "postcss": "^8.4.14", 32 | "sass": "^1.51.0", 33 | "tailwindcss": "^3.0.24", 34 | "typescript": "^4.3.2", 35 | "vite": "^2.9.8", 36 | "vite-plugin-imp": "^2.1.8" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modules/app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@ant-design/icons": "^4.7.0", 11 | "@packages/assets": "^0.0.1", 12 | "@packages/shared": "^0.0.1", 13 | "@types/lodash": "^4.14.182", 14 | "@types/md5": "^2.3.2", 15 | "ahooks": "^3.3.10", 16 | "antd": "^4.20.1", 17 | "axios": "^0.27.2", 18 | "history": "^5.2.0", 19 | "hox": "1.0.2", 20 | "lodash": "^4.17.21", 21 | "md5": "^2.3.0", 22 | "qiankun": "^2.7.0", 23 | "react": "^17.0.0", 24 | "react-dom": "^17.0.0", 25 | "react-router-dom": "^6.3.0" 26 | }, 27 | "devDependencies": { 28 | "@packages/build": "^0.0.1", 29 | "@types/react": "^17.0.0", 30 | "@types/react-dom": "^17.0.0", 31 | "@vitejs/plugin-react": "^1.3.2", 32 | "autoprefixer": "^10.4.7", 33 | "less": "^4.1.2", 34 | "postcss": "^8.4.14", 35 | "sass": "^1.51.0", 36 | "tailwindcss": "^3.0.24", 37 | "typescript": "^4.3.2", 38 | "vite": "^2.9.8", 39 | "vite-plugin-imp": "^2.1.8" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /modules/demo-vue-1/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { 3 | renderWithQiankun, 4 | qiankunWindow, 5 | } from 'vite-plugin-qiankun/dist/helper' 6 | 7 | import App from './App.vue' 8 | import './index.scss' 9 | 10 | const appName = 'demo-vue-1' 11 | 12 | // @ts-ignore 13 | window.__POWERED_BY_QIANKUN__ = qiankunWindow.__POWERED_BY_QIANKUN__ 14 | 15 | let app: any 16 | async function start(props: any = {}) { 17 | const { container } = props 18 | app = createApp(App) 19 | app.mount( 20 | container 21 | ? container.querySelector(`#${appName}-app`) 22 | : document.querySelector(`#${appName}-app`) 23 | ) 24 | } 25 | 26 | function applyProps(props: any) {} 27 | 28 | renderWithQiankun({ 29 | bootstrap() { 30 | console.log(`[${appName}] bootstrap`) 31 | }, 32 | mount(props: any) { 33 | console.log(`[${appName}] mount`, props) 34 | applyProps(props) 35 | start(props) 36 | }, 37 | update(props: any) { 38 | console.log(`[${appName}] update`, props) 39 | applyProps(props?.props ?? props) 40 | }, 41 | unmount() { 42 | console.log(`[${appName}] unmount`) 43 | app.unmount() 44 | }, 45 | }) 46 | 47 | // @ts-ignore 48 | if (!window.__POWERED_BY_QIANKUN__) { 49 | start() 50 | } 51 | -------------------------------------------------------------------------------- /modules/demo-vue-2/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { 3 | renderWithQiankun, 4 | qiankunWindow, 5 | } from 'vite-plugin-qiankun/dist/helper' 6 | 7 | import App from './App.vue' 8 | import './index.scss' 9 | 10 | const appName = 'demo-vue-2' 11 | 12 | // @ts-ignore 13 | window.__POWERED_BY_QIANKUN__ = qiankunWindow.__POWERED_BY_QIANKUN__ 14 | 15 | let app: any 16 | async function start(props: any = {}) { 17 | const { container } = props 18 | app = createApp(App) 19 | app.mount( 20 | container 21 | ? container.querySelector(`#${appName}-app`) 22 | : document.querySelector(`#${appName}-app`) 23 | ) 24 | } 25 | 26 | function applyProps(props: any) {} 27 | 28 | renderWithQiankun({ 29 | bootstrap() { 30 | console.log(`[${appName}] bootstrap`) 31 | }, 32 | mount(props: any) { 33 | console.log(`[${appName}] mount`, props) 34 | applyProps(props) 35 | start(props) 36 | }, 37 | update(props: any) { 38 | console.log(`[${appName}] update`, props) 39 | applyProps(props?.props ?? props) 40 | }, 41 | unmount() { 42 | console.log(`[${appName}] unmount`) 43 | app.unmount() 44 | }, 45 | }) 46 | 47 | // @ts-ignore 48 | if (!window.__POWERED_BY_QIANKUN__) { 49 | start() 50 | } 51 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/src/main.ts.tpl: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { 3 | renderWithQiankun, 4 | qiankunWindow, 5 | } from 'vite-plugin-qiankun/dist/helper' 6 | 7 | import App from './App.vue' 8 | import './index.scss' 9 | 10 | const appName = '{{ name }}' 11 | 12 | // @ts-ignore 13 | window.__POWERED_BY_QIANKUN__ = qiankunWindow.__POWERED_BY_QIANKUN__ 14 | 15 | let app: any 16 | async function start(props: any = {}) { 17 | const { container } = props 18 | app = createApp(App) 19 | app.mount( 20 | container 21 | ? container.querySelector(`#${appName}-app`) 22 | : document.querySelector(`#${appName}-app`) 23 | ) 24 | } 25 | 26 | function applyProps(props: any) {} 27 | 28 | renderWithQiankun({ 29 | bootstrap() { 30 | console.log(`[${appName}] bootstrap`) 31 | }, 32 | mount(props: any) { 33 | console.log(`[${appName}] mount`, props) 34 | applyProps(props) 35 | start(props) 36 | }, 37 | update(props: any) { 38 | console.log(`[${appName}] update`, props) 39 | applyProps(props?.props ?? props) 40 | }, 41 | unmount() { 42 | console.log(`[${appName}] unmount`) 43 | app.unmount() 44 | }, 45 | }) 46 | 47 | // @ts-ignore 48 | if (!window.__POWERED_BY_QIANKUN__) { 49 | start() 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "engines": { 4 | "node": ">=12.17.0" 5 | }, 6 | "private": true, 7 | "scripts": { 8 | "init": "npx pnpm@6 i --ignore-scripts --filter=!./legacies && npx pnpm@6 run --filter=./legacies --parallel init", 9 | "clean": "rimraf ./**/node_modules", 10 | "kill-dev": "kill-port --port 8080,8081,8082,8083,8084", 11 | "dev": "npm run kill-dev && npx pnpm@6 run --filter=!root --parallel dev", 12 | "dev:no-legacy": "npx pnpm@6 run --filter=!root --filter=!@legacies/* --parallel dev", 13 | "patch": "npx pnpm@6 run --filter=!root --parallel patch", 14 | "build": "npx pnpm@6 run --filter=!root --parallel build && npm run after-build", 15 | "serve": "serve ./dist", 16 | "after-build": "node ./scripts/afterBuild.js", 17 | "create": "node ./scripts/create/index.js" 18 | }, 19 | "devDependencies": { 20 | "@umijs/utils": "^3.5.23", 21 | "ip": "^1.1.5", 22 | "kill-port": "^1.6.1", 23 | "prettier": "^2.5.1", 24 | "rimraf": "^3.0.2", 25 | "yargs": "^17.5.0" 26 | }, 27 | "dependencies": { 28 | "fs-extra": "^10.1.0", 29 | "serve": "^13.0.2" 30 | }, 31 | "stackblitz": { 32 | "installDependencies": false, 33 | "startCommand": "npm run init && npm run dev" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /modules/demo-vue-1/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 8 | 9 | 46 | 47 | 64 | -------------------------------------------------------------------------------- /modules/demo-vue-2/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 46 | 47 | 64 | -------------------------------------------------------------------------------- /templates/micro-vue-vite/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 46 | 47 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 微前端 monorepo 项目样例 2 | 3 | ![](https://visitor-badge.glitch.me/badge?page_id=cjy0208.mono-micro-project) 4 | 5 | ### 在线预览 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/mono-micro-project) 8 | [![Open in Codeanywhere](https://codeanywhere.com/img/open-in-codeanywhere-btn.svg)](https://app.codeanywhere.com/#https://github.com/edit/mono-micro-project) 9 | 10 | [StackBlitz](http://stackblitz.com/) 对 pnpm 支持不太好所以换用 lerna 来支持 workspace 功能 11 | 12 | ## 启动方式 13 | 14 | ```bash 15 | pnpm run init # 初始化,安装依赖 16 | pnpm run dev # 启动 dev 命令,默认基座在 8080 端口 17 | pnpm run build # 构建 18 | pnpm run serve # 产物预览 19 | ``` 20 | 21 | ## 给特定子项目安装依赖 22 | 23 | ```bash 24 | npm install -g pnpm # 全局安装 pnpm 25 | pnpm add xxx --filter @modules/xxx-1 --filter @modules/xxx-2 26 | ``` 27 | 28 | ## 创建子项目 29 | 30 | ```bash 31 | npm run create -- --name=<模块名> --template=<模板名> 32 | ``` 33 | 34 | 目前可用模板有: 35 | 36 | 1. `micro-react-vite` **(默认)**: 基于 vite 的 react(17) 子项目 37 | 2. `micro-vue-vite`:基于 vite 的 vue3 子项目 38 | 3. 待增加 39 | 40 | ### 踩坑记录 41 | 42 | 1. React.lazy 组件在 vite 中无法热更新 43 | 44 | 相关资料: 45 | https://github.com/vitejs/vite/issues/2719 46 | https://github.com/facebook/react/issues/21181 47 | 48 | 解决方式 49 | 50 | - 组件文件中仅保留 export default 部分,不要有 export const ... 导出 51 | - export default 导出具名函数,不要匿名导出,避免 export default () =>
...
52 | 53 | 2. 路由建议 54 | 55 | 基座用 Browser 路由,子应用使用 Hash 路由,互不冲突,若存在多应用协同共存且有各自路由需求,建议使用 Memory 路由 56 | 57 | 3. 父子路由、子子路由冲突 58 | 59 | 要点:让子路由的 history 操作仅限在特定地址条件下生效,可能需要改 node_modules 中关键处的源码,用 patch-package 保存改动 60 | -------------------------------------------------------------------------------- /modules/demo-react-1/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { renderWithQiankun } from 'vite-plugin-qiankun/dist/helper' 4 | import useAccount from '@packages/shared/hooks/useAccount' 5 | import useAppConfig from '@packages/shared/hooks/useAppConfig' 6 | import App from '@/routes' 7 | 8 | import './index.scss' 9 | 10 | const appName = 'demo-react-1' 11 | 12 | export default function start(props: any = {}) { 13 | const { container } = props 14 | 15 | ReactDOM.render( 16 | , 17 | container 18 | ? container.querySelector(`#${appName}-root`) 19 | : document.querySelector(`#${appName}-root`) 20 | ) 21 | } 22 | 23 | function applyProps(props: any) { 24 | useAccount.data?.setAccount(props?.account) 25 | useAppConfig?.data?.setLocale(props?.locale) 26 | } 27 | 28 | renderWithQiankun({ 29 | bootstrap() { 30 | console.log(`[${appName}] bootstrap`) 31 | }, 32 | mount(props: any) { 33 | console.log(`[${appName}] mount`, props) 34 | applyProps(props) 35 | start(props) 36 | }, 37 | update(props: any) { 38 | console.log(`[${appName}] update`, props) 39 | applyProps(props?.props ?? props) 40 | }, 41 | unmount(props: any) { 42 | console.log(`[${appName}] unmount`) 43 | const { container } = props 44 | ReactDOM.unmountComponentAtNode( 45 | container 46 | ? container.querySelector(`#${appName}-root`) 47 | : document.querySelector(`#${appName}-root`) 48 | ) 49 | }, 50 | }) 51 | 52 | // @ts-ignore 53 | if (!window.__POWERED_BY_QIANKUN__) { 54 | start() 55 | } 56 | 57 | // @ts-ignore 58 | if (process.env.NODE_ENV === 'development') { 59 | import('@/hmr.fix') 60 | } 61 | -------------------------------------------------------------------------------- /modules/demo-react-2/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { renderWithQiankun } from 'vite-plugin-qiankun/dist/helper' 4 | import useAccount from '@packages/shared/hooks/useAccount' 5 | import useAppConfig from '@packages/shared/hooks/useAppConfig' 6 | import App from '@/routes' 7 | 8 | import './index.scss' 9 | 10 | const appName = 'demo-react-2' 11 | 12 | export default function start(props: any = {}) { 13 | const { container } = props 14 | 15 | ReactDOM.render( 16 | , 17 | container 18 | ? container.querySelector(`#${appName}-root`) 19 | : document.querySelector(`#${appName}-root`) 20 | ) 21 | } 22 | 23 | function applyProps(props: any) { 24 | useAccount.data?.setAccount(props?.account) 25 | useAppConfig?.data?.setLocale(props?.locale) 26 | } 27 | 28 | renderWithQiankun({ 29 | bootstrap() { 30 | console.log(`[${appName}] bootstrap`) 31 | }, 32 | mount(props: any) { 33 | console.log(`[${appName}] mount`, props) 34 | applyProps(props) 35 | start(props) 36 | }, 37 | update(props: any) { 38 | console.log(`[${appName}] update`, props) 39 | applyProps(props?.props ?? props) 40 | }, 41 | unmount(props: any) { 42 | console.log(`[${appName}] unmount`) 43 | const { container } = props 44 | ReactDOM.unmountComponentAtNode( 45 | container 46 | ? container.querySelector(`#${appName}-root`) 47 | : document.querySelector(`#${appName}-root`) 48 | ) 49 | }, 50 | }) 51 | 52 | // @ts-ignore 53 | if (!window.__POWERED_BY_QIANKUN__) { 54 | start() 55 | } 56 | 57 | // @ts-ignore 58 | if (process.env.NODE_ENV === 'development') { 59 | import('@/hmr.fix') 60 | } 61 | -------------------------------------------------------------------------------- /templates/micro-react-vite/src/main.tsx.tpl: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { renderWithQiankun } from 'vite-plugin-qiankun/dist/helper' 4 | import useAccount from '@packages/shared/hooks/useAccount' 5 | import useAppConfig from '@packages/shared/hooks/useAppConfig' 6 | import App from '@/routes' 7 | 8 | import './index.scss' 9 | 10 | const appName = '{{ name }}' 11 | 12 | export default function start(props: any = {}) { 13 | const { container } = props 14 | 15 | ReactDOM.render( 16 | , 17 | container 18 | ? container.querySelector(`#${appName}-root`) 19 | : document.querySelector(`#${appName}-root`) 20 | ) 21 | } 22 | 23 | const applyProps = throttle(function applyProps(props: any) { 24 | useAccount.data?.setAccount(props?.account) 25 | useAppConfig?.data?.setLocale(props?.locale) 26 | }) 27 | 28 | renderWithQiankun({ 29 | bootstrap() { 30 | console.log(`[${appName}] bootstrap`) 31 | }, 32 | mount(props: any) { 33 | console.log(`[${appName}] mount`, props) 34 | applyProps(props) 35 | start(props) 36 | }, 37 | update(props: any) { 38 | console.log(`[${appName}] update`, props) 39 | applyProps(props?.props ?? props) 40 | }, 41 | unmount(props: any) { 42 | console.log(`[${appName}] unmount`) 43 | const { container } = props 44 | ReactDOM.unmountComponentAtNode( 45 | container 46 | ? container.querySelector(`#${appName}-root`) 47 | : document.querySelector(`#${appName}-root`) 48 | ) 49 | }, 50 | }) 51 | 52 | // @ts-ignore 53 | if (!window.__POWERED_BY_QIANKUN__) { 54 | start() 55 | } 56 | 57 | // @ts-ignore 58 | if (process.env.NODE_ENV === 'development') { 59 | import('@/hmr.fix') 60 | } 61 | -------------------------------------------------------------------------------- /packages/build/vite.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { mergeConfig } = require('vite') 3 | const qiankun = require('vite-plugin-qiankun') 4 | const { default: react } = require('@vitejs/plugin-react') 5 | const { default: vue } = require('@vitejs/plugin-vue') 6 | const { default: vitePluginImp } = require('vite-plugin-imp') 7 | // const LessNodeModules = require('less-plugin-import-node-modules') 8 | const nodeIP = require('ip') 9 | const ip = nodeIP.address() 10 | 11 | // https://vitejs.dev/config/ 12 | const getConfig = ({ 13 | type = 'react', 14 | micro = false, 15 | moduleName = '', 16 | dirname = process.cwd(), 17 | } = {}) => { 18 | const serverConfig = { 19 | strictPort: true, 20 | proxy: {}, 21 | } 22 | 23 | const initialPlugins = { 24 | vue: [vue()], 25 | react: [ 26 | react(), 27 | vitePluginImp({ 28 | libList: [ 29 | { 30 | libName: 'antd', 31 | style: (name) => `antd/es/${name}/style/css`, 32 | }, 33 | { 34 | libName: '@ant-design/icons', 35 | libDirectory: '', 36 | camel2DashComponentName: false, 37 | }, 38 | ], 39 | }), 40 | ], 41 | }[type] 42 | 43 | const sharedViteConfig = { 44 | root: dirname, 45 | server: serverConfig, 46 | preview: serverConfig, 47 | resolve: { 48 | alias: [ 49 | // fix less import by: @import ~ 50 | // less import no support webpack alias '~' · Issue #2185 · vitejs/vite 51 | // https://github.com/vitejs/vite/issues/2185 52 | { find: /^~/, replacement: '' }, 53 | { find: '@', replacement: path.resolve(dirname, './src') }, 54 | ], 55 | }, 56 | plugins: initialPlugins, 57 | css: { 58 | preprocessorOptions: { 59 | less: { 60 | javascriptEnabled: true, 61 | // plugins: [new LessNodeModules()], 62 | }, 63 | }, 64 | }, 65 | } 66 | 67 | if (!micro) { 68 | return sharedViteConfig 69 | } 70 | 71 | const microViteConfig = mergeConfig(sharedViteConfig, { 72 | base: `/${moduleName}`, 73 | plugins: [qiankun(moduleName, {})], 74 | build: { 75 | rollupOptions: { 76 | external: ['@/hmr.fix'], 77 | }, 78 | }, 79 | }) 80 | 81 | return microViteConfig 82 | } 83 | 84 | module.exports = { 85 | ip, 86 | getConfig, 87 | } 88 | -------------------------------------------------------------------------------- /modules/app/src/components/MicroApp.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect, memo, lazy } from 'react' 2 | import { loadMicroApp, FrameworkConfiguration, initGlobalState } from 'qiankun' 3 | import { useLocation } from 'react-router-dom' 4 | 5 | const Lazy = lazy(() => new Promise(() => null)) 6 | 7 | const microAppEntryCache: any = {} 8 | const actions = initGlobalState({ hash: '' }) 9 | 10 | function run(fn: any, ...params: any[]) { 11 | if (typeof fn === 'function') { 12 | return fn(...params) 13 | } 14 | 15 | return undefined 16 | } 17 | 18 | interface MicroAppProps extends FrameworkConfiguration { 19 | name: string 20 | entry?: string 21 | props?: any 22 | [key: string]: any 23 | } 24 | 25 | let prevAppUnmountPromise: Promise = Promise.resolve() 26 | 27 | // https://qiankun.umijs.org/zh/api#loadmicroappapp-configuration 28 | const MicroApp = memo(function MicroApp({ 29 | name, 30 | entry, 31 | sandbox = true, 32 | props = {}, 33 | }: MicroAppProps) { 34 | const location = useLocation() 35 | const [ready, setReady] = useState(false) 36 | const microApp = useRef() 37 | const container = useRef() 38 | 39 | // const { account } = useAccount() 40 | // const { locale } = useAppConfig() 41 | 42 | useEffect(() => { 43 | // debugger 44 | async function mount() { 45 | // debugger 46 | await prevAppUnmountPromise 47 | // debugger 48 | window[name as any] = microAppEntryCache[name] ?? window[name as any] 49 | 50 | microApp.current = loadMicroApp( 51 | { 52 | name, 53 | entry: entry!, 54 | container: container.current, 55 | props, 56 | }, 57 | { 58 | sandbox, 59 | } 60 | ) 61 | 62 | microApp.current.mountPromise.then(() => { 63 | if (window[name as any]) { 64 | microAppEntryCache[name] = window[name as any] 65 | } 66 | 67 | setReady(true) 68 | }) 69 | } 70 | mount() 71 | 72 | return () => { 73 | // debugger 74 | prevAppUnmountPromise = Promise.resolve(run(microApp.current?.unmount)).then( 75 | () => { 76 | // debugger 77 | } 78 | ) 79 | } 80 | }, []) 81 | 82 | useEffect(() => { 83 | if (!microApp.current) { 84 | return 85 | } 86 | run(microApp.current?.update, { 87 | container: container.current, 88 | props: { 89 | ...props, 90 | location, 91 | }, 92 | }) 93 | }, [location, Object.values(props)]) 94 | 95 | useEffect(() => { 96 | actions.setGlobalState(location) 97 | }, [location]) 98 | 99 | return ( 100 | <> 101 |
102 | {!ready && } 103 | 104 | ) 105 | }) 106 | 107 | export default function MicroAppWrapper(props: MicroAppProps) { 108 | return 109 | } 110 | -------------------------------------------------------------------------------- /modules/app/src/pages/login/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useNavigate } from 'react-router-dom' 3 | import { Form, Input, Button, Space, message, Avatar } from 'antd' 4 | import { LockOutlined, UserOutlined } from '@ant-design/icons' 5 | import useAccount from '@packages/shared/hooks/useAccount' 6 | 7 | import styles from './style.module.scss' 8 | 9 | export default function Login() { 10 | const [form] = Form.useForm() 11 | const { setAccount } = useAccount() 12 | const navigate = useNavigate() 13 | 14 | return ( 15 |
16 | 17 | 22 | 23 |

24 | Mono Micro Project 25 |

26 |
27 |
28 | 29 | 38 | 42 | } 43 | placeholder="用户名" 44 | /> 45 | 46 | 55 | 59 | } 60 | type="password" 61 | placeholder="密码" 62 | > 63 | 64 | 65 | 92 | 93 | 94 |
95 |
96 |
97 | ) 98 | } 99 | -------------------------------------------------------------------------------- /modules/app/src/routes/config.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import BlankLayout from '@packages/shared/layouts/Blank' 3 | import Lazy from '@packages/shared/components/Lazy' 4 | import useAccount from '@packages/shared/hooks/useAccount' 5 | import useAppConfig from '@packages/shared/hooks/useAppConfig' 6 | 7 | import MicroApp from '@/components/MicroApp' 8 | import CommonLayout from '@/layouts/Common' 9 | import Home from '@/pages/home' 10 | import { Navigate } from 'react-router-dom' 11 | 12 | export default function useRoutesConfig() { 13 | const { account } = useAccount() 14 | const { locale } = useAppConfig() 15 | 16 | const configs: any[] = [ 17 | { 18 | element: , 19 | children: [ 20 | { 21 | children: [ 22 | { 23 | index: true, 24 | title: '首页', 25 | element: , 26 | menu: true, 27 | }, 28 | { 29 | path: 'any', 30 | title: '404 页', 31 | element: , 32 | menu: true, 33 | }, 34 | { path: '*', element: '404' }, 35 | ], 36 | }, 37 | { 38 | path: 'micro', 39 | children: [ 40 | { 41 | menu: true, 42 | title: 'demo-react-1', 43 | path: 'demo-react-1', 44 | element: ( 45 | 55 | ), 56 | }, 57 | { 58 | menu: true, 59 | title: 'demo-vue-1', 60 | path: 'demo-vue-1', 61 | element: ( 62 | 72 | ), 73 | }, 74 | { 75 | menu: true, 76 | title: 'demo-react-2', 77 | path: 'demo-react-2', 78 | element: ( 79 | 89 | ), 90 | }, 91 | { 92 | menu: true, 93 | title: 'demo-vue-2', 94 | path: 'demo-vue-2', 95 | element: ( 96 | 106 | ), 107 | }, 108 | ], 109 | }, 110 | ], 111 | }, 112 | { 113 | element: , 114 | children: [ 115 | { 116 | path: 'login', 117 | element: import('@/pages/login')} />, 118 | }, 119 | { path: '*', element: '404' }, 120 | ], 121 | }, 122 | ] 123 | return configs 124 | } 125 | -------------------------------------------------------------------------------- /modules/app/src/layouts/Common/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo, useState, Suspense } from 'react' 2 | import { 3 | Outlet, 4 | useLocation, 5 | useMatch, 6 | useNavigate, 7 | useOutlet, 8 | matchRoutes, 9 | // useOutletContext 10 | } from 'react-router-dom' 11 | import { flatten } from 'lodash' 12 | import { 13 | Menu, 14 | Layout, 15 | Dropdown, 16 | Spin, 17 | Avatar, 18 | Space, 19 | Modal, 20 | message, 21 | } from 'antd' 22 | import { 23 | MenuUnfoldOutlined, 24 | MenuFoldOutlined, 25 | UserOutlined, 26 | VideoCameraOutlined, 27 | UploadOutlined, 28 | GlobalOutlined, 29 | LogoutOutlined, 30 | LockOutlined, 31 | } from '@ant-design/icons' 32 | 33 | import useRoutesConfig from '@/routes/config' 34 | import useAppConfig from '@packages/shared/hooks/useAppConfig' 35 | import useAccount from '@packages/shared/hooks/useAccount' 36 | 37 | import './style.scss' 38 | 39 | const { Header, Sider, Content } = Layout 40 | 41 | const languageLabels: any = { 42 | 'zh-CN': '简体中文', 43 | 'en-US': 'English', 44 | } 45 | 46 | export default function CommonLayout() { 47 | const navigate = useNavigate() 48 | const { account } = useAccount() 49 | const { locale, setLocale } = useAppConfig() 50 | const configs = useRoutesConfig() 51 | const [collapsed, setCollapsed] = useState(false) 52 | 53 | const menuConfig = useMemo(() => { 54 | const targetConfig = configs.find((item: any) => { 55 | return item?.element?.type?.displayName === 'CommonLayout' 56 | }) 57 | 58 | return filterNav(targetConfig?.children ?? []) 59 | }, [configs]) 60 | 61 | // @ts-ignore 62 | window.navigate = navigate 63 | 64 | return ( 65 |
66 | 67 | 76 |
77 | { 82 | // console.log(item) 83 | navigate(item?.key) 84 | }} 85 | onOpenChange={(item) => { 86 | // console.log(item) 87 | // navigate(item?.key) 88 | }} 89 | /> 90 | 91 | 92 |
93 |
94 | {React.createElement( 95 | collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, 96 | { 97 | className: 'trigger', 98 | onClick: () => setCollapsed(!collapsed), 99 | } 100 | )} 101 | 105 | 108 | { 111 | Modal.confirm({ 112 | title: '操作确认', 113 | content: '确定要登出吗?', 114 | onOk: async () => { 115 | message.success('登出成功') 116 | navigate('/login') 117 | }, 118 | onCancel() {}, 119 | }) 120 | }} 121 | > 122 | 123 | 124 | 登出 125 | 126 | 127 | 128 | 129 | 130 | 修改密码 131 | 132 | 133 |
134 | } 135 | > 136 | 137 | 138 | {account?.username} 139 | 140 | 141 | 142 | { 148 | setLocale(key) 149 | }} 150 | items={['zh-CN', 'en-US'].map((key: any) => ({ 151 | key, 152 | label: languageLabels[key], 153 | }))} 154 | > 155 | } 156 | placement="bottomRight" 157 | > 158 |
159 | 160 |
161 |
162 | 163 |
164 | 165 | 170 |
171 | }> 172 | 173 | 174 |
175 |
176 | 177 | 178 |
179 | ) 180 | } 181 | 182 | CommonLayout.displayName = 'CommonLayout' 183 | 184 | function filterNav(list: any[], parents: any[] = []): any[] { 185 | return flatten( 186 | [...list] 187 | .map(({ ...item }: any) => { 188 | if (Array.isArray(item.children)) { 189 | item.children = filterNav(item.children, [...parents, item]) 190 | 191 | if (item.children.length === 0) { 192 | return undefined 193 | } 194 | 195 | if (item.children.length === 1) { 196 | return item.children[0] 197 | } 198 | 199 | if (!(item.label ?? item.title)) { 200 | return item.children 201 | } 202 | } else { 203 | if (!item.menu) { 204 | return undefined 205 | } 206 | } 207 | 208 | return item 209 | }) 210 | .flat(Infinity) 211 | ) 212 | .filter(Boolean) 213 | .map(({ ...item }: any) => { 214 | if (Array.isArray(item.children)) { 215 | item.key = item.key ?? item.path 216 | delete item.path 217 | } else { 218 | item.key = 219 | item.key ?? 220 | `/${[...parents, item] 221 | .map((item) => item.path) 222 | .filter(Boolean) 223 | .join('/') 224 | .replace(/\/\//g, '/')}` 225 | } 226 | 227 | item.label = item.label ?? item.title 228 | 229 | delete item.title 230 | delete item.index 231 | delete item.element 232 | delete item.menu 233 | 234 | return item 235 | }) 236 | } 237 | --------------------------------------------------------------------------------