├── qiankun-react ├── .env ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── setupTests.js │ ├── App.test.js │ ├── index.css │ ├── reportWebVitals.js │ ├── App.js │ ├── index.js │ ├── App.css │ └── logo.svg ├── .gitignore ├── config-overrides.js ├── .eslintcache ├── package.json └── README.md ├── qiankun-vue ├── .npmrc ├── babel.config.js ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ └── logo.png │ ├── views │ │ ├── About.vue │ │ └── Home.vue │ ├── public-path.js │ ├── router │ │ └── index.js │ ├── App.vue │ ├── main.js │ └── components │ │ └── HelloWorld.vue ├── .eslintrc.js ├── .editorconfig ├── .gitignore ├── README.md ├── vue.config.js └── package.json ├── qiankun-box ├── vue.config.js ├── babel.config.js ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ └── logo.png │ ├── views │ │ ├── Home │ │ │ └── index.vue │ │ ├── Login │ │ │ └── index.vue │ │ └── Main │ │ │ └── index.vue │ ├── App.vue │ ├── router │ │ └── index.js │ ├── main.js │ └── components │ │ └── HelloWorld.vue ├── .gitignore ├── README.md ├── .eslintrc.js └── package.json └── README.md /qiankun-react/.env: -------------------------------------------------------------------------------- 1 | PORT = 20000 2 | WDS_SOCKET_PORT = 20000 -------------------------------------------------------------------------------- /qiankun-vue/.npmrc: -------------------------------------------------------------------------------- 1 | @aixuexi:registry=http://npm.aixuexi.com/ -------------------------------------------------------------------------------- /qiankun-box/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lintOnSave: false 3 | } -------------------------------------------------------------------------------- /qiankun-box/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /qiankun-react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /qiankun-box/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-box/public/favicon.ico -------------------------------------------------------------------------------- /qiankun-box/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-box/src/assets/logo.png -------------------------------------------------------------------------------- /qiankun-vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /qiankun-vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-vue/public/favicon.ico -------------------------------------------------------------------------------- /qiankun-vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-vue/src/assets/logo.png -------------------------------------------------------------------------------- /qiankun-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-react/public/favicon.ico -------------------------------------------------------------------------------- /qiankun-react/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-react/public/logo192.png -------------------------------------------------------------------------------- /qiankun-react/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/li1164267803/qiankun-demo/HEAD/qiankun-react/public/logo512.png -------------------------------------------------------------------------------- /qiankun-vue/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [require.resolve("@aixuexi/growth-eslint-config")] 4 | }; 5 | -------------------------------------------------------------------------------- /qiankun-vue/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /qiankun-vue/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /qiankun-vue/src/public-path.js: -------------------------------------------------------------------------------- 1 | if (window.__POWERED_BY_QIANKUN__) { 2 | // eslint-disable-next-line no-undef 3 | __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; 4 | } 5 | -------------------------------------------------------------------------------- /qiankun-box/src/views/Home/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /qiankun-react/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /qiankun-box/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /qiankun-react/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /qiankun-box/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /qiankun-vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /qiankun-vue/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Home from "../views/Home.vue"; 2 | 3 | const routes = [ 4 | { 5 | path: "/", 6 | name: "Home", 7 | component: Home 8 | }, 9 | { 10 | path: "/about", 11 | name: "About", 12 | component: () => 13 | import(/* webpackChunkName: "about" */ "../views/About.vue") 14 | } 15 | ]; 16 | 17 | export default routes; 18 | -------------------------------------------------------------------------------- /qiankun-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /qiankun-vue/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /qiankun-react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /qiankun-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /qiankun-box/README.md: -------------------------------------------------------------------------------- 1 | # qiankun-box 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /qiankun-react/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /qiankun-vue/README.md: -------------------------------------------------------------------------------- 1 | # qiankun-app1 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /qiankun-react/config-overrides.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpack: (config) => { 3 | config.output.library = 'reactApp'; 4 | config.output.libraryTarget = 'umd'; 5 | config.output.publicPath = 'http://localhost:20000/'; 6 | return config; 7 | }, 8 | devServer: (configFunction) => { 9 | return function (proxy, allowedHost) { 10 | const config = configFunction(proxy, allowedHost); 11 | config.headers = { 12 | "Access-Control-Allow-Origin": '*' 13 | } 14 | return config 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /qiankun-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /qiankun-react/src/App.js: -------------------------------------------------------------------------------- 1 | import logo from './logo.svg'; 2 | import './App.css'; 3 | 4 | function App() { 5 | return ( 6 |
7 |
8 | logo 9 |

10 | Edit src/App.js and save to reload. 11 |

12 | 18 | Learn React 19 | 20 |
21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /qiankun-react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | function render(){ 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | } 14 | 15 | if(!window.__POWERED_BY_QIANKUN__){ 16 | render(); 17 | } 18 | 19 | export async function bootstrap(){ 20 | 21 | } 22 | export async function mount(props) { 23 | console.log(props) 24 | render() 25 | } 26 | export async function unmount(){ 27 | ReactDOM.unmountComponentAtNode( document.getElementById('root')); // 卸载节点 28 | } 29 | 30 | 31 | -------------------------------------------------------------------------------- /qiankun-vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /qiankun-react/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /qiankun-react/.eslintcache: -------------------------------------------------------------------------------- 1 | [{"G:\\mydemo\\qiankun\\qiankun-react\\src\\index.js":"1","G:\\mydemo\\qiankun\\qiankun-react\\src\\App.js":"2"},{"size":538,"mtime":1621998660053,"results":"3","hashOfConfig":"4"},{"size":528,"mtime":1621998660050,"results":"5","hashOfConfig":"4"},{"filePath":"6","messages":"7","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"8"},"b4tnnn",{"filePath":"9","messages":"10","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"G:\\mydemo\\qiankun\\qiankun-react\\src\\index.js",[],["11","12"],"G:\\mydemo\\qiankun\\qiankun-react\\src\\App.js",[],{"ruleId":"13","replacedBy":"14"},{"ruleId":"15","replacedBy":"16"},"no-native-reassign",["17"],"no-negated-in-lhs",["18"],"no-global-assign","no-unsafe-negation"] -------------------------------------------------------------------------------- /qiankun-box/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 20 | 21 | 22 | 26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /qiankun-box/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import Main from "../views/Main"; 4 | import Login from "../views/Login"; 5 | Vue.use(VueRouter); 6 | const routes = [ 7 | { 8 | path: "/", 9 | name: "Login", 10 | component: Login 11 | }, 12 | { 13 | path: "/main", 14 | name: "Main", 15 | component: Main, 16 | children: [ 17 | { 18 | path: "/home", 19 | name: "Home", 20 | component: () => import(/* webpackChunkName: "about" */ "../views/Home") 21 | } 22 | ] 23 | }, 24 | { 25 | path: "/app/*", 26 | name: "Main", 27 | component: Main 28 | } 29 | ]; 30 | 31 | const router = new VueRouter({ 32 | mode: "history", 33 | // base: process.env.BASE_URL, 34 | base: "", 35 | routes 36 | }); 37 | 38 | export default router; 39 | -------------------------------------------------------------------------------- /qiankun-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qiankun-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.6", 7 | "@testing-library/react": "^11.2.2", 8 | "@testing-library/user-event": "^12.5.0", 9 | "http-proxy-middleware": "^1.0.6", 10 | "react": "^17.0.1", 11 | "react-app-rewired": "^2.1.6", 12 | "react-dom": "^17.0.1", 13 | "react-scripts": "4.0.1", 14 | "web-vitals": "^0.2.4" 15 | }, 16 | "scripts": { 17 | "start": "set BROWSER=none && react-app-rewired start", 18 | "serve": "npm run start", 19 | "build": "react-app-rewired build", 20 | "test": "react-app-rewired test", 21 | "eject": "react-app-rewired eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } -------------------------------------------------------------------------------- /qiankun-box/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | "plugin:vue/essential", 8 | "eslint:recommended", 9 | "@vue/prettier", 10 | "plugin:prettier/recommended" 11 | ], 12 | parserOptions: { 13 | parser: "babel-eslint" 14 | }, 15 | rules: { 16 | "no-unused-vars": [ 17 | 2, 18 | { 19 | // 允许声明未使用变量 20 | vars: "local", 21 | // 参数不检查 22 | args: "none" 23 | } 24 | ], 25 | // "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 26 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off", 27 | "prettier/prettier": [ 28 | "error" 29 | // { 30 | // singleQuote: true, 31 | // trailingComma: "none", 32 | // bracketSpacing: true, 33 | // jsxBracketSameLine: true, 34 | // parser: "flow" 35 | // } 36 | ], 37 | // 允许catch出现空代码块 38 | // https://blog.csdn.net/cccccccccz/article/details/105253558 39 | 'no-empty': [2, { 'allowEmptyCatch': true }] 40 | } 41 | }; 42 | //"plugin:vue/essential", "eslint:recommended", "@vue/prettier" 43 | -------------------------------------------------------------------------------- /qiankun-box/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import router from "./router"; 3 | import App from "./App.vue"; 4 | import ElementUI from "element-ui"; 5 | import "element-ui/lib/theme-chalk/index.css"; 6 | Vue.use(ElementUI); 7 | 8 | import { registerMicroApps, start, initGlobalState } from "qiankun"; 9 | let propsData = { 10 | sex: "男", 11 | age: 18, 12 | userName: "小东" 13 | }; 14 | const actions = initGlobalState(propsData); 15 | // 主项目项目监听和修改(在项目中任何需要监听的地方进行监听) 16 | actions.onGlobalStateChange((state, prev) => { 17 | // state: 变更后的状态; prev 变更前的状态 18 | console.log("改变前的值 ", prev); 19 | console.log("改变后的值 ", state); 20 | }); 21 | // 将actions对象绑到Vue原型上,为了项目中其他地方使用方便 22 | Vue.prototype.$actions = actions; 23 | 24 | Vue.config.productionTip = false; 25 | registerMicroApps([ 26 | { 27 | name: "vueApp", // 应用的名字 28 | entry: "//localhost:3001", // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch 29 | container: "#container", // 容器id 30 | activeRule: "/app/vue" // 根据路由 激活的路径 31 | }, 32 | { 33 | name: "reactApp", 34 | entry: "//localhost:20000", 35 | container: "#container", 36 | activeRule: "/app/react" 37 | }, 38 | ]); 39 | 40 | start(); 41 | 42 | new Vue({ 43 | router, 44 | render: h => h(App) 45 | }).$mount("#app"); 46 | -------------------------------------------------------------------------------- /qiankun-vue/vue.config.js: -------------------------------------------------------------------------------- 1 | const { name } = require("./package"); 2 | 3 | const devServer = { 4 | port: 3001, 5 | // 微应用的 devTools 中开启开启 hot、disableHostCheck 6 | hot: true, 7 | disableHostCheck: true, 8 | overlay: { 9 | warnings: false, 10 | errors: true 11 | }, 12 | headers: { 13 | //因为qiankun内部请求都是fetch来请求资源,所以子应用必须允许跨域 14 | "Access-Control-Allow-Origin": "*" 15 | } 16 | }; 17 | // vue.config.js 18 | const vueConfig = { 19 | lintOnSave: false, 20 | publicPath: 21 | process.env.NODE_ENV === "production" 22 | ? "https://fe-sta.aixuexi.com/tol/axxol-omo-web/" 23 | : "/", 24 | 25 | assetsDir: "assets", 26 | configureWebpack: { 27 | output: { 28 | // 把子应用打包成 umd 库格式 29 | library: `${name}-[name]`, 30 | libraryTarget: "umd", 31 | jsonpFunction: `webpackJsonp_${name}` 32 | } 33 | }, 34 | css: { 35 | loaderOptions: { 36 | less: { 37 | modifyVars: { 38 | // less vars,customize ant design theme 39 | "primary-color": "#15D494", 40 | "link-color": "#15D494" 41 | }, 42 | // DO NOT REMOVE THIS LINE 43 | javascriptEnabled: true 44 | } 45 | } 46 | }, 47 | 48 | devServer 49 | }; 50 | 51 | module.exports = vueConfig; 52 | -------------------------------------------------------------------------------- /qiankun-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qiankun-app1", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "vue": "^2.6.11", 13 | "vue-router": "^3.2.0" 14 | }, 15 | "devDependencies": { 16 | "@vue/cli-plugin-babel": "~4.5.0", 17 | "@vue/cli-plugin-eslint": "~4.5.0", 18 | "@vue/cli-plugin-router": "~4.5.0", 19 | "@vue/cli-service": "~4.5.0", 20 | "@vue/eslint-config-standard": "^5.1.2", 21 | "babel-eslint": "^10.1.0", 22 | "@aixuexi/growth-eslint-config": "^1.0.9", 23 | "eslint": "^6.7.2", 24 | "eslint-plugin-import": "^2.20.2", 25 | "eslint-plugin-node": "^11.1.0", 26 | "eslint-plugin-promise": "^4.2.1", 27 | "eslint-plugin-standard": "^4.0.0", 28 | "eslint-plugin-vue": "^6.2.2", 29 | "vue-template-compiler": "^2.6.11" 30 | }, 31 | "eslintConfig": { 32 | "root": true, 33 | "env": { 34 | "node": true 35 | }, 36 | "extends": [ 37 | "plugin:vue/essential", 38 | "@vue/standard" 39 | ], 40 | "parserOptions": { 41 | "parser": "babel-eslint" 42 | }, 43 | "rules": {} 44 | }, 45 | "browserslist": [ 46 | "> 1%", 47 | "last 2 versions", 48 | "not dead" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /qiankun-box/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qiankun-box", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "element-ui": "^2.15.1", 13 | "less-loader": "^5.0.0", 14 | "qiankun": "^2.4.1", 15 | "vue": "^2.6.11", 16 | "vue-router": "^3.2.0" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "~4.5.0", 20 | "@vue/cli-plugin-eslint": "~4.5.0", 21 | "@vue/cli-service": "~4.5.0", 22 | "@vue/eslint-config-prettier": "^6.0.0", 23 | "babel-eslint": "^10.0.3", 24 | "eslint": "^6.7.2", 25 | "eslint-config-prettier": "^6.10.1", 26 | "eslint-plugin-prettier": "^3.1.3", 27 | "eslint-plugin-vue": "^6.2.2", 28 | "less": "^4.1.1", 29 | "prettier": "^1.19.1", 30 | "vue-template-compiler": "^2.6.11" 31 | }, 32 | "eslintConfig": { 33 | "root": true, 34 | "env": { 35 | "node": true 36 | }, 37 | "extends": [ 38 | "plugin:vue/essential", 39 | "eslint:recommended", 40 | "@vue/prettier" 41 | ], 42 | "parserOptions": { 43 | "parser": "babel-eslint" 44 | }, 45 | "rules": {} 46 | }, 47 | "browserslist": [ 48 | "> 1%", 49 | "last 2 versions", 50 | "not dead" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /qiankun-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import "./public-path"; 2 | import Vue from "vue"; 3 | import VueRouter from "vue-router"; 4 | import App from "./App.vue"; 5 | import routes from "./router"; 6 | 7 | Vue.config.productionTip = false; 8 | 9 | let router = null; 10 | let instance = null; 11 | function render(props = {}) { 12 | const { container } = props; 13 | router = new VueRouter({ 14 | base: window.__POWERED_BY_QIANKUN__ ? "/app/vue" : "/", 15 | mode: "history", 16 | routes 17 | }); 18 | 19 | instance = new Vue({ 20 | router, 21 | render: h => h(App) 22 | }).$mount(container ? container.querySelector("#app") : "#app"); 23 | 24 | // vue-devtools 调试 25 | if (window.__POWERED_BY_QIANKUN__ && process.env.NODE_ENV === "development") { 26 | // vue-devtools 加入此处代码即可 27 | // After you create app 28 | window.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = instance.constructor; 29 | let instanceDiv = document.createElement("div"); 30 | instanceDiv.__vue__ = instance; 31 | document.body.appendChild(instanceDiv); 32 | } 33 | } 34 | 35 | 36 | // 独立运行时 37 | if (!window.__POWERED_BY_QIANKUN__) { 38 | render(); 39 | } 40 | 41 | export async function bootstrap() { 42 | console.log("[vue] vue app bootstraped"); 43 | } 44 | export async function mount(props) { 45 | console.log("[vue] props from main framework", props); 46 | render(props); 47 | } 48 | export async function unmount() { 49 | instance.$destroy(); 50 | instance.$el.innerHTML = ""; 51 | instance = null; 52 | router = null; 53 | } 54 | 55 | 56 | -------------------------------------------------------------------------------- /qiankun-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /qiankun-box/src/views/Login/index.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 69 | 70 | 82 | -------------------------------------------------------------------------------- /qiankun-box/src/views/Main/index.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 59 | 60 | 94 | -------------------------------------------------------------------------------- /qiankun-react/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /qiankun-box/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 97 | 98 | 99 | 115 | -------------------------------------------------------------------------------- /qiankun-vue/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 105 | 106 | 107 | 123 | -------------------------------------------------------------------------------- /qiankun-react/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 使用 qiankun(乾坤)搭建 微服务 2 | 3 | ## 背景 4 | 5 | 一个 WEB 端管理系统包含好几个单独的模块,相互之间没有耦合,如果放到同一个项目里,同时好几个人去维护,不利于管理,单独子模块的上线会对整个项目全量上线风险比较大,并且容易代码冲突 6 | 7 | ## 一、什么是微前端 8 | 9 | 微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。微前端的核心在于**拆**,拆完后在**合**! 10 | 11 | ## 二、为什么使用微前端 12 | 13 | 1. 技术栈无关 14 | 主框架不限制接入应用的技术栈,微应用具备完全自主权 15 | 2. 独立开发、独立部署 16 | 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 17 | 3. 增量升级 18 | 在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略 19 | 4. 独立运行时 20 | 每个微应用之间状态隔离,运行时状态不共享 21 | 22 | 我们可以将一个应用划分成若干个子应用,将子应用打包成一个个的 lib 。当路径切换 时加载不同的子应用。这样每个子应用都是独立的,技术栈也不用做限制了!从而解决了前端协同开发问题。 23 | 24 | ## 三、qiankun 框架 25 | 26 | [https://qiankun.umijs.org/zh](https://qiankun.umijs.org/zh) 27 | 28 | 2018 年 Single-SPA 诞生了, single-spa 是一个用于前端微服务化的 JavaScript 前端解决方案 ( 本身没有处理样式隔离, js 执行隔离 ) 实现了路由劫持和应用加载。 29 | 30 | 2019 年 qiankun 基于 Single-SPA, 提供了更加开箱即用的 API ( single-spa + sandbox + import-html-entry ) 做到了,技术栈无关、并且接入简单(像 i frame 一样简单)。 31 | 32 | ## 四、qiankun 框架实例 33 | 34 | **[demo 地址:https://github.com/li1164267803/qiankun-demo](https://github.com/li1164267803/qiankun-demo)** 35 | **查看 demo 的时候三个项目都要跑起来,然后在主服务里面看效果** 36 | 37 | **这里我们打算建立三个项目进行实操,一个 Vue 项目充当主应用,另一个 Vue 和 React 应用充当子应用** 38 | 39 | ### 1、创建三个应用 40 | 41 | #### 1)创建基座 42 | 43 | ```js 44 | vue create qiankun-box 45 | ``` 46 | 47 | #### 2)创建子应用 1 48 | 49 | ```js 50 | vue create qiankun-vue 51 | ``` 52 | 53 | #### 3)创建子应用 2 54 | 55 | ```JavaScript 56 | cnpm install -g create-react-app 57 | create-react-app qiankun-react 58 | ``` 59 | 60 | - 三个项目 61 | 62 | 基座:qiankun-base 子应用:qiankun-vue、qiankun-react 63 | 64 | ### 2、项目配置(主要) 65 | 66 | #### 1)基座 qiankun-base 配置 67 | 68 | > ​ 项目创建好后我们首先进行主应用 qiankun-base 的配置,进入 man.js 文件进行配置, 在 main.js 中加入以下代码,要注意的是,entry 这项配置是我们两个子项目的域名和端口,我们必须确保两字子项目运行在这两个端口上面,container 就是我们的容器名,就是我们子应用挂载的节点,相当于 Vue 项目里面的 app 节点,activeRule 就是我们的激活路径,根据路径来显示不同的子应用。 69 | 70 | - 引入 qiankun 插件 71 | 72 | ```js 73 | npm i qiankun -S 74 | ``` 75 | 76 | - main.js 配置 77 | 78 | ```js 79 | // 引入qiankun 80 | import { registerMicroApps, start } from "qiankun"; 81 | registerMicroApps([ 82 | { 83 | name: "vueApp", // 应用的名字 84 | entry: "//localhost:3001", // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch 85 | container: "#container", // 容器id 86 | activeRule: "/app/vue", // 根据路由 激活的路径 87 | }, 88 | { 89 | name: "reactApp", 90 | entry: "//localhost:20000", 91 | container: "#container", 92 | activeRule: "/app/react", 93 | }, 94 | ]); 95 | start(); 96 | ``` 97 | 98 | - 配置完之后我们去到 qiankun-base 的 app.vue 文件进行主应用的页面编写,这里我安装了 element-ui 来进行页面美化 99 | 100 | ```javascript 101 | npm i element-ui -S 102 | ``` 103 | 104 | 在 main.js 中引入 element-ui: 105 | 106 | ```js 107 | import ElementUI from "element-ui"; 108 | import "element-ui/lib/theme-chalk/index.css"; 109 | 110 | Vue.use(ElementUI); 111 | ``` 112 | 113 | - 修改 app.vue 的组件代码 114 | 115 | ```html 116 | 134 | ``` 135 | 136 | - router.js 代码 137 | 138 | ```js 139 | import Vue from "vue"; 140 | import VueRouter from "vue-router"; 141 | import Home from "../views/Home.vue"; 142 | 143 | Vue.use(VueRouter); 144 | 145 | const routes = [ 146 | { 147 | path: "/", 148 | name: "Home", 149 | component: Home, 150 | }, 151 | { 152 | path: "/about", 153 | name: "About", 154 | component: () => 155 | import(/* webpackChunkName: "about" */ "../views/About.vue"), 156 | }, 157 | ]; 158 | 159 | const router = new VueRouter({ 160 | mode: "history", 161 | // base: process.env.BASE_URL, 162 | base: "", 163 | routes, 164 | }); 165 | 166 | export default router; 167 | ``` 168 | 169 | #### 2)子应用 qiankun-vue 配置 170 | 171 | - main.js 配置 172 | 173 | ```js 174 | import Vue from "vue"; 175 | import App from "./App.vue"; 176 | import router from "./router"; 177 | 178 | // Vue.config.productionTip = false 179 | 180 | let instance = null; 181 | function render(props) { 182 | instance = new Vue({ 183 | router, 184 | render: (h) => h(App), 185 | }).$mount("#qkApp"); // 这里是挂载到自己的html中 基座会拿到这个挂载后的html 将其插入进去 186 | } 187 | 188 | if (window.__POWERED_BY_QIANKUN__) { 189 | // 动态添加publicPath 190 | __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; 191 | } 192 | if (!window.__POWERED_BY_QIANKUN__) { 193 | // 默认独立运行 194 | render(); 195 | } 196 | 197 | // 父应用加载子应用,子应用必须暴露三个接口:bootstrap、mount、unmount 198 | // 子组件的协议就ok了 199 | export async function bootstrap(props) {} 200 | 201 | export async function mount(props) { 202 | render(props); 203 | } 204 | 205 | export async function unmount(props) { 206 | instance.$destroy(); 207 | } 208 | ``` 209 | 210 | - router.js 配置 211 | 212 | ```js 213 | import Vue from "vue"; 214 | import VueRouter from "vue-router"; 215 | import Home from "../views/Home.vue"; 216 | 217 | Vue.use(VueRouter); 218 | 219 | const routes = [ 220 | { 221 | path: "/", 222 | name: "Home", 223 | component: Home, 224 | }, 225 | { 226 | path: "/about", 227 | name: "About", 228 | component: () => 229 | import(/* webpackChunkName: "about" */ "../views/About.vue"), 230 | }, 231 | ]; 232 | 233 | const router = new VueRouter({ 234 | mode: "history", 235 | base: "/vue", 236 | routes, 237 | }); 238 | 239 | export default router; 240 | ``` 241 | 242 | - Vue.config.js 配置 243 | 244 | 在子应用的根目录下面新建一个 Vue.config.js 文件 245 | 246 | ```js 247 | module.exports = { 248 | lintOnSave: false, // 关闭eslint检测 249 | devServer: { 250 | port: 3001, //这里的端口是必须和父应用配置的子应用端口一致 251 | headers: { 252 | //因为qiankun内部请求都是fetch来请求资源,所以子应用必须允许跨域 253 | "Access-Control-Allow-Origin": "*", 254 | }, 255 | }, 256 | configureWebpack: { 257 | output: { 258 | //资源打包路径 259 | library: "vueApp", 260 | libraryTarget: "umd", 261 | }, 262 | }, 263 | }; 264 | ``` 265 | 266 | #### 3)子应用 qiankun-react 配置 267 | 268 | - src 目录下 index.js 文件 269 | 270 | ```js 271 | import React from "react"; 272 | import ReactDOM from "react-dom"; 273 | import "./index.css"; 274 | import App from "./App"; 275 | 276 | function render() { 277 | ReactDOM.render( 278 | 279 | 280 | , 281 | document.getElementById("root") 282 | ); 283 | } 284 | 285 | if (!window.__POWERED_BY_QIANKUN__) { 286 | render(); 287 | } 288 | 289 | export async function bootstrap() {} 290 | export async function mount() { 291 | render(); 292 | } 293 | export async function unmount() { 294 | ReactDOM.unmountComponentAtNode(document.getElementById("root")); // 卸载节点 295 | } 296 | ``` 297 | 298 | - config-overrides.js 配置 299 | 300 | 先引入 react-app-rewired,在修改 package.json 启动命令 301 | 302 | ```js 303 | npm install react-app-rewired 304 | ``` 305 | 306 | 修改 package.json 启动命令 307 | 308 | ```js 309 | "scripts": { 310 | "start": "react-app-rewired start", 311 | "build": "react-app-rewired build", 312 | "test": "react-app-rewired test", 313 | "eject": "react-app-rewired eject" 314 | }, 315 | ``` 316 | 317 | 再进行 dev 以及打包的配置,根目录下创建 config-overrides.js 318 | 319 | ```js 320 | module.exports = { 321 | webpack: (config) => { 322 | config.output.library = "reactApp"; 323 | config.output.libraryTarget = "umd"; 324 | config.output.publicPath = "http://localhost:20000/"; // 此应用自己的端口号 325 | return config; 326 | }, 327 | devServer: (configFunction) => { 328 | return function (proxy, allowedHost) { 329 | const config = configFunction(proxy, allowedHost); 330 | config.headers = { 331 | "Access-Control-Allow-Origin": "*", 332 | }; 333 | return config; 334 | }; 335 | }, 336 | }; 337 | ``` 338 | 339 | ### 3、注意点 340 | 341 | #### 1)如何在主应用的某个路由页面加载微应用 342 | 343 | **`react` + `react-router` 技术栈的主应用:只需要让子应用的 `activeRule` 包含主应用的这个路由即可。** 344 | 345 | **`vue` + `vue-router` 技术栈的主应用:** 346 | 347 | > 例如:主应用需要在 login 页面登录,登录成功后跳转到 main 后台管理界面,在 main 管理界面下可以显示子应用。 348 | 349 | 修改主应用 router.js: 350 | 351 | ```js 352 | // 如果这个路由有其他子路由,需要另外注册一个路由,任然使用这个组件即可。 353 | // 本案例就是有子路由,所以需要才后面重新定义main页面的路由 354 | const routes = [ 355 | { 356 | path: "/", 357 | name: "Login", 358 | component: Login, 359 | }, 360 | { 361 | path: "/main", 362 | name: "Main", 363 | component: Main, 364 | children: [ 365 | { 366 | path: "/home", 367 | name: "Home", 368 | component: () => 369 | import(/* webpackChunkName: "about" */ "../views/Home"), 370 | }, 371 | ], 372 | }, 373 | { 374 | path: "/app/*", // 所有的子应用要走的路径 375 | name: "Main", 376 | component: Main, 377 | }, 378 | ]; 379 | ``` 380 | 381 | 修改主应用 main.js 的文件: 382 | 383 | ````js 384 | // 子应用的 activeRule 需要包含主应用的这个路由 path 385 | import { registerMicroApps, start} from "qiankun"; 386 | registerMicroApps([ 387 | { 388 | name: "vueApp", // 应用的名字 389 | entry: "//localhost:3001", // 默认会加载这个html 解析里面的js 动态的执行 (子应用必须支持跨域)fetch 390 | container: "#container", // 容器id 391 | activeRule: "/app/vue" // 根据路由 激活的路径 392 | }, 393 | { 394 | name: "reactApp", 395 | entry: "//localhost:20000", 396 | container: "#container", 397 | activeRule: "/app/react" 398 | }, 399 | ]); 400 | 401 | start(); 402 | 403 | 修改主应用 main.vue 页面代码: 404 | 405 | ```javascript 406 | // 在 Main.vue 这个组件的 mounted 周期调用 start 函数,注意不要重复调用。 407 | 425 | 426 | 439 | ```` 440 | 441 | ### 4、解决在 vue 子应用中无法使用 vue-devtools 调试功能 442 | 443 | 微应用的 main.js 中一定要加上测试环境开启 devTools 的代码 444 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210526114710404.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDMwOTM3NA==,size_16,color_FFFFFF,t_70) 445 | 写入到 render 函数中 446 | 447 | ```js 448 | // vue-devtools 调试 449 | if (window.__POWERED_BY_QIANKUN__ && process.env.NODE_ENV === "development") { 450 | // vue-devtools 加入此处代码即可 451 | // After you create app 452 | window.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = instance.constructor; 453 | let instanceDiv = document.createElement("div"); 454 | instanceDiv.__vue__ = instance; 455 | document.body.appendChild(instanceDiv); 456 | } 457 | ``` 458 | 459 | 在 vue.config.js 中配置 460 | 461 | ```js 462 | devServer: { 463 | hot: true, 464 | disableHostCheck: true, 465 | overlay: { 466 | warnings: false, 467 | errors: true 468 | }, 469 | headers: { 470 | "Access-Control-Allow-Origin": "*" // 子应用必须支持跨域 471 | }, 472 | } 473 | ``` 474 | --------------------------------------------------------------------------------