├── .eslintignore
├── sub-vue
├── .env
├── .browserslistrc
├── babel.config.js
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ └── logo.png
│ ├── views
│ │ ├── About.vue
│ │ └── Home.vue
│ ├── store
│ │ └── index.js
│ ├── public-path.js
│ ├── router
│ │ └── index.js
│ ├── main.js
│ ├── App.vue
│ └── components
│ │ └── HelloWorld.vue
├── .editorconfig
├── .gitignore
├── .eslintrc.js
├── README.md
├── vue.config.js
└── package.json
├── common
├── .gitignore
├── .npmignore
├── src
│ ├── api
│ │ └── index.js
│ ├── sdk
│ │ └── index.js
│ ├── store
│ │ ├── index.js
│ │ └── global-register.js
│ └── index.js
└── package.json
├── main
├── .browserslistrc
├── public
│ ├── favicon.ico
│ └── index.html
├── babel.config.js
├── src
│ ├── assets
│ │ └── logo.png
│ ├── micro-app.js
│ ├── store.js
│ ├── main.js
│ └── App.vue
├── .editorconfig
├── .env.development
├── .env.production
├── vue.config.js
├── .gitignore
├── .eslintrc.js
├── README.md
└── package.json
├── .gitignore
├── sub-react
├── .env
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── public-path.js
│ ├── setupTests.js
│ ├── App.test.js
│ ├── index.css
│ ├── App.js
│ ├── App.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
├── .gitignore
├── config-overrides.js
├── package.json
└── README.md
├── sub-html
├── scripts
│ └── build.sh
├── README.md
├── package.json
├── public
│ └── index.html
├── index.html
├── js
│ └── main.js
└── package-lock.json
├── .vscode
└── settings.json
├── README.md
├── scripts
└── bundle.sh
├── .editorconfig
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | /examples
2 |
--------------------------------------------------------------------------------
/sub-vue/.env:
--------------------------------------------------------------------------------
1 | VUE_APP_PORT=7777
2 |
--------------------------------------------------------------------------------
/common/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/common/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/common/src/api/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/common/src/sdk/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/main/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 |
4 | dist
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sub-vue/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/sub-react/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
2 | PORT=7788
3 | PUBLIC_URL=/subapp/sub-react
4 |
--------------------------------------------------------------------------------
/main/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/main/public/favicon.ico
--------------------------------------------------------------------------------
/sub-react/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/main/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/main/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/main/src/assets/logo.png
--------------------------------------------------------------------------------
/sub-react/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/sub-react/public/favicon.ico
--------------------------------------------------------------------------------
/sub-react/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/sub-react/public/logo192.png
--------------------------------------------------------------------------------
/sub-react/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/sub-react/public/logo512.png
--------------------------------------------------------------------------------
/sub-vue/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/sub-vue/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/sub-vue/public/favicon.ico
--------------------------------------------------------------------------------
/sub-vue/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengxianqi/qiankun-example/HEAD/sub-vue/src/assets/logo.png
--------------------------------------------------------------------------------
/common/src/store/index.js:
--------------------------------------------------------------------------------
1 | import globalRegister from './global-register'
2 | export default {
3 | globalRegister
4 | }
5 |
--------------------------------------------------------------------------------
/sub-vue/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/main/.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 |
--------------------------------------------------------------------------------
/sub-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 |
--------------------------------------------------------------------------------
/main/.env.development:
--------------------------------------------------------------------------------
1 | VUE_APP_SUB_VUE=//localhost:7777/subapp/sub-vue/
2 | VUE_APP_SUB_REACT=//localhost:7788/subapp/sub-react/
3 | VUE_APP_SUB_HTML=//localhost:7799/
4 |
--------------------------------------------------------------------------------
/common/src/index.js:
--------------------------------------------------------------------------------
1 | import store from './store'
2 | import sdk from './sdk'
3 | import api from './api'
4 |
5 | export {
6 | store,
7 | sdk,
8 | api
9 | }
10 |
--------------------------------------------------------------------------------
/sub-html/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 把当前项目对外输出的文件都放到dist目录
4 |
5 | rm -rf ./dist
6 |
7 | mkdir ./dist
8 |
9 | cp -r ./public/* ./dist/
10 |
11 | cp -r ./js ./dist/js
12 |
13 |
--------------------------------------------------------------------------------
/main/.env.production:
--------------------------------------------------------------------------------
1 | VUE_APP_SUB_VUE=//qiankun.fengxianqi.com/subapp/sub-vue/
2 | VUE_APP_SUB_REACT=//qiankun.fengxianqi.com/subapp/sub-react/
3 | VUE_APP_SUB_HTML=//qiankun.fengxianqi.com/subapp/sub-html/
4 |
--------------------------------------------------------------------------------
/sub-react/src/public-path.js:
--------------------------------------------------------------------------------
1 | if (window.__POWERED_BY_QIANKUN__) {
2 | // eslint-disable-next-line
3 | __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
4 | //__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
5 | }
6 |
--------------------------------------------------------------------------------
/sub-vue/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | Vue.use(Vuex)
5 |
6 | export default new Vuex.Store({
7 | state: {
8 | },
9 | mutations: {
10 | },
11 | actions: {
12 | },
13 | modules: {
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/main/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transpileDependencies: ['common'],
3 | chainWebpack: config => {
4 | config.plugin('html')
5 | .tap((args) => {
6 | args[0].title = 'qiankun-example'
7 | return args
8 | })
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/sub-html/README.md:
--------------------------------------------------------------------------------
1 | # 原生HTML项目
2 |
3 | ## 启动
4 | ```
5 | npm start
6 | ```
7 | 本地服务通过 `http-server` 提供。
8 |
9 | ## 参考
10 | 官方例子(jQuery):[https://github.com/umijs/qiankun/tree/master/examples/purehtml](https://github.com/umijs/qiankun/tree/master/examples/purehtml)
11 |
--------------------------------------------------------------------------------
/common/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "common",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "src/index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "fengxianqi",
10 | "license": "MIT"
11 | }
12 |
--------------------------------------------------------------------------------
/sub-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/extend-expect';
6 |
--------------------------------------------------------------------------------
/sub-react/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | const { getByText } = render( );
7 | const linkElement = getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/main/.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 |
--------------------------------------------------------------------------------
/sub-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 |
--------------------------------------------------------------------------------
/sub-vue/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/main/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: [
7 | 'plugin:vue/essential',
8 | '@vue/standard'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sub-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 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [
3 | "./main",
4 | "./sub-vue",
5 | "./sub-react",
6 | "./common"
7 | ],
8 | "eslint.enable": true,
9 | "editor.formatOnSave": false,
10 | "editor.codeActionsOnSave": {
11 | "source.fixAll.eslint": true
12 | },
13 | "search.useIgnoreFiles": false,
14 | "search.exclude": {
15 | "**/dist": true
16 | },
17 | "eslint.debug": true,
18 | }
19 |
--------------------------------------------------------------------------------
/main/README.md:
--------------------------------------------------------------------------------
1 | # main
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 |
--------------------------------------------------------------------------------
/sub-vue/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: [
7 | 'plugin:vue/essential',
8 | '@vue/standard'
9 | ],
10 | parserOptions: {
11 | parser: 'babel-eslint'
12 | },
13 | rules: {
14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sub-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 |
--------------------------------------------------------------------------------
/sub-vue/README.md:
--------------------------------------------------------------------------------
1 | # sub-vue
2 |
3 | ## Project setup
4 |
5 | ```
6 | npm install
7 | ```
8 |
9 | ### Compiles and hot-reloads for development
10 |
11 | ```
12 | npm run serve
13 | ```
14 |
15 | ### Compiles and minifies for production
16 |
17 | ```
18 | npm run build
19 | ```
20 |
21 | ### Lints and fixes files
22 |
23 | ```
24 | npm run lint
25 | ```
26 |
27 | ### Customize configuration
28 |
29 | See [Configuration Reference](https://cli.vuejs.org/config/).
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qiankun-example
2 |
3 | qiankun 实战 demo,父应用 vue,子应用使用 `react`, `vue` 和 `原生HTML`。
4 |
5 | [微前端qiankun从搭建到部署的实践](https://juejin.im/post/6875462470593904653)
6 |
7 | ## 开始
8 | 安装根目录工程依赖
9 | ```
10 | npm i
11 | ```
12 | 一键安装所有主子应用的依赖
13 | ```
14 | npm run install
15 | ```
16 |
17 | 一键启动所有所有应用
18 | ```
19 | npm start
20 | ```
21 |
22 | 通过 [http://localhost:8080/](http://localhost:8080/) 访问主应用。
23 |
24 | ## 发布
25 | 一键构建并打包所有主子应用
26 | ```
27 | npm run build
28 | ```
29 |
30 |
31 |
--------------------------------------------------------------------------------
/scripts/bundle.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -rf ./dist
4 |
5 | mkdir ./dist
6 | mkdir ./dist/subapp
7 |
8 | # sub-react子应用
9 | cp -r ./sub-react/build/ ./dist/subapp/sub-react/
10 |
11 | # sub-vue子应用
12 | cp -r ./sub-vue/dist/ ./dist/subapp/sub-vue/
13 |
14 | # sub-html子应用
15 | cp -r ./sub-html/dist/ ./dist/subapp/sub-html/
16 |
17 | # main基座
18 | cp -r ./main/dist/ ./dist/main/
19 |
20 | # cd ./dist
21 | # zip -r mp$(date +%Y%m%d%H%M%S).zip *
22 | # cd ..
23 | echo 'bundle.sh execute success.'
24 |
--------------------------------------------------------------------------------
/sub-html/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sub-html",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.html",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "cross-env PUBLIC_PATH=/ http-server ./ -p 7799 --cors",
9 | "build": "bash ./scripts/build.sh"
10 | },
11 | "author": "fengxianqi",
12 | "license": "MIT",
13 | "devDependencies": {
14 | "cross-env": "^7.0.3",
15 | "http-server": "^13.0.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sub-vue/src/public-path.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | if (window.__POWERED_BY_QIANKUN__) {
3 | if (process.env.NODE_ENV === 'development') {
4 | // eslint-disable-next-line
5 | __webpack_public_path__ = `//localhost:${process.env.VUE_APP_PORT}${process.env.BASE_URL}`
6 | return
7 | }
8 | // eslint-disable-next-line
9 | __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
10 | // __webpack_public_path__ = `${process.env.BASE_URL}/`
11 | }
12 | })()
13 |
--------------------------------------------------------------------------------
/sub-vue/vue.config.js:
--------------------------------------------------------------------------------
1 | const { name } = require('../package.json')
2 |
3 | module.exports = {
4 | publicPath: '/subapp/sub-vue',
5 | transpileDependencies: ['common'],
6 | chainWebpack: config => config.resolve.symlinks(false),
7 | configureWebpack: {
8 | output: {
9 | // 把子应用打包成 umd 库格式
10 | library: `${name}-[name]`,
11 | libraryTarget: 'umd',
12 | jsonpFunction: `webpackJsonp_${name}`
13 | }
14 | },
15 | devServer: {
16 | port: process.env.VUE_APP_PORT,
17 | headers: {
18 | 'Access-Control-Allow-Origin': '*'
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sub-vue/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import Home from '../views/Home.vue'
4 | import About from '../views/About.vue'
5 |
6 | Vue.use(VueRouter)
7 |
8 | const routes = [
9 | {
10 | path: '/',
11 | name: 'Home',
12 | component: Home
13 | },
14 | {
15 | path: '/about',
16 | name: 'About',
17 | // route level code-splitting
18 | // this generates a separate chunk (about.[hash].js) for this route
19 | // which is lazy-loaded when the route is visited.
20 | component: About
21 | }
22 | ]
23 |
24 | export default routes
25 |
--------------------------------------------------------------------------------
/sub-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 |
--------------------------------------------------------------------------------
/sub-html/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sub-html子应用生产环境
6 |
11 |
12 |
13 |
14 | 纯原生HTML子应用, 当前处于 独立运行 环境。
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sub-react/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import logo from './logo.svg';
3 | import './App.css';
4 |
5 | function App() {
6 | return (
7 |
23 | );
24 | }
25 |
26 | export default App;
27 |
--------------------------------------------------------------------------------
/main/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/sub-html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sub-html子应用开发环境
6 |
11 |
12 |
13 |
14 | 纯原生HTML子应用, 当前处于 独立运行 环境。
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sub-vue/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
12 | We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: https://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | # Matches multiple files with brace expansion notation
12 | # Set default charset
13 | [*.{js,py}]
14 | charset = utf-8
15 |
16 | # 4 space indentation
17 | [*.py]
18 | indent_style = space
19 | indent_size = 2
20 |
21 | # Indentation override for all JS under lib directory
22 | [src/**.{js,vue}]
23 | indent_style = space
24 | indent_size = 2
25 |
26 | # Matches the exact files either package.json or .travis.yml
27 | [{package.json,vue.config.js}]
28 | indent_style = space
29 | indent_size = 2
30 |
--------------------------------------------------------------------------------
/sub-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 |
--------------------------------------------------------------------------------
/main/src/micro-app.js:
--------------------------------------------------------------------------------
1 | import store from './store'
2 |
3 | const microApps = [
4 | {
5 | name: 'sub-vue',
6 | entry: process.env.VUE_APP_SUB_VUE,
7 | activeRule: '/sub-vue'
8 | },
9 | {
10 | name: 'sub-react',
11 | entry: process.env.VUE_APP_SUB_REACT,
12 | activeRule: '/sub-react'
13 | },
14 | {
15 | name: 'sub-html',
16 | entry: process.env.VUE_APP_SUB_HTML,
17 | activeRule: '/sub-html'
18 | }
19 | ]
20 |
21 | const apps = microApps.map(item => {
22 | return {
23 | ...item,
24 | container: '#subapp-viewport', // 子应用挂载的div
25 | props: {
26 | routerBase: item.activeRule, // 下发基础路由
27 | getGlobalState: store.getGlobalState // 下发getGlobalState方法
28 | }
29 | }
30 | })
31 |
32 | export default apps
33 |
--------------------------------------------------------------------------------
/main/src/store.js:
--------------------------------------------------------------------------------
1 | import { initGlobalState } from 'qiankun'
2 | import Vue from 'vue'
3 |
4 | // 父应用的初始state
5 | // Vue.observable是为了让initialState变成可响应:https://cn.vuejs.org/v2/api/#Vue-observable。
6 | const initialState = Vue.observable({
7 | user: {
8 | name: 'zhangsan'
9 | }
10 | })
11 |
12 | const actions = initGlobalState(initialState)
13 |
14 | actions.onGlobalStateChange((newState, prev) => {
15 | // state: 变更后的状态; prev 变更前的状态
16 | console.log('main change', JSON.stringify(newState), JSON.stringify(prev))
17 |
18 | for (const key in newState) {
19 | initialState[key] = newState[key]
20 | }
21 | })
22 |
23 | // 定义一个获取state的方法下发到子应用
24 | actions.getGlobalState = (key) => {
25 | // 有key,表示取globalState下的某个子级对象
26 | // 无key,表示取全部
27 |
28 | return key ? initialState[key] : initialState
29 | }
30 |
31 | export default actions
32 |
--------------------------------------------------------------------------------
/sub-react/config-overrides.js:
--------------------------------------------------------------------------------
1 | const { name } = require('./package.json');
2 | console.log(name)
3 |
4 | module.exports = {
5 | webpack: function override(config, env) {
6 | config.entry = config.entry.filter(
7 | (e) => !e.includes('webpackHotDevClient')
8 | );
9 |
10 | config.output.library = `${name}-[name]`;
11 | config.output.libraryTarget = 'umd';
12 | config.output.jsonpFunction = `webpackJsonp_${name}`;
13 | return config;
14 | },
15 | devServer: (configFunction) => {
16 | return function (proxy, allowedHost) {
17 | const config = configFunction(proxy, allowedHost);
18 | config.open = false;
19 | config.hot = false;
20 | config.headers = {
21 | 'Access-Control-Allow-Origin': '*',
22 | };
23 | // Return your customised Webpack Development Server config.
24 | return config;
25 | };
26 | },
27 | };
28 |
--------------------------------------------------------------------------------
/sub-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sub-react",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^4.2.4",
7 | "@testing-library/react": "^9.3.2",
8 | "@testing-library/user-event": "^7.1.2",
9 | "react": "^16.13.1",
10 | "react-dom": "^16.13.1",
11 | "react-scripts": "3.4.3"
12 | },
13 | "scripts": {
14 | "start": "react-app-rewired start",
15 | "build": "react-app-rewired build",
16 | "test": "react-app-rewired test --env=jsdom",
17 | "eject": "react-scripts eject"
18 | },
19 | "eslintConfig": {
20 | "extends": "react-app"
21 | },
22 | "browserslist": {
23 | "production": [
24 | ">0.2%",
25 | "not dead",
26 | "not op_mini all"
27 | ],
28 | "development": [
29 | "last 1 chrome version",
30 | "last 1 firefox version",
31 | "last 1 safari version"
32 | ]
33 | },
34 | "devDependencies": {
35 | "react-app-rewired": "^2.1.6"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/main/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "main",
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 | "start": "npm run serve"
10 | },
11 | "dependencies": {
12 | "core-js": "^3.6.5",
13 | "nprogress": "^0.2.0",
14 | "qiankun": "^2.0.22",
15 | "vue": "^2.6.11"
16 | },
17 | "devDependencies": {
18 | "@vue/cli-plugin-babel": "~4.5.0",
19 | "@vue/cli-plugin-eslint": "~4.5.0",
20 | "@vue/cli-service": "~4.5.0",
21 | "@vue/eslint-config-standard": "^5.1.2",
22 | "babel-eslint": "^10.1.0",
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 | "sass": "^1.26.10",
30 | "sass-loader": "^10.0.2",
31 | "vue-template-compiler": "^2.6.11"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/sub-vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sub-vue",
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 | "start": "npm run serve"
10 | },
11 | "dependencies": {
12 | "common": "file:../common",
13 | "core-js": "^3.6.5",
14 | "vue": "^2.6.11",
15 | "vue-router": "^3.2.0",
16 | "vuex": "^3.4.0"
17 | },
18 | "devDependencies": {
19 | "@vue/cli-plugin-babel": "~4.5.0",
20 | "@vue/cli-plugin-eslint": "~4.5.0",
21 | "@vue/cli-plugin-router": "~4.5.0",
22 | "@vue/cli-plugin-vuex": "~4.5.0",
23 | "@vue/cli-service": "~4.5.0",
24 | "@vue/eslint-config-standard": "^5.1.2",
25 | "babel-eslint": "^10.1.0",
26 | "eslint": "^6.7.2",
27 | "eslint-plugin-import": "^2.20.2",
28 | "eslint-plugin-node": "^11.1.0",
29 | "eslint-plugin-promise": "^4.2.1",
30 | "eslint-plugin-standard": "^4.0.0",
31 | "eslint-plugin-vue": "^6.2.2",
32 | "vue-template-compiler": "^2.6.11"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/sub-html/js/main.js:
--------------------------------------------------------------------------------
1 | const render = (options) => {
2 | // options是基座下发的参数,可以保存到全局的状态管理或其他地方,用于后面与基座进行通信
3 |
4 | // 可通过 options.getGlobalState() 获取基座下发的数据
5 | // options.setGlobalState({user: {name: ''}}) 改变全局的数据
6 | // options.onGlobalStateChange 监听全局数据的变化
7 |
8 |
9 | document.querySelector('#current-env').innerHTML = 'qiankun'
10 | const globalState = options.getGlobalState()
11 |
12 | // 展示基座下发的状态
13 | const node = document.createElement('div')
14 | node.innerHTML = `基座下发的globalState: ${JSON.stringify(globalState)}。打开独立运行环境 `
15 |
16 | document.querySelector('.container').appendChild(node)
17 |
18 | return Promise.resolve();
19 | };
20 |
21 | (global => {
22 | global['prehtml'] = {
23 | bootstrap: () => {
24 | console.log('purehtml bootstrap');
25 | return Promise.resolve();
26 | },
27 | mount: (options) => {
28 | console.log('purehtml mount', options);
29 | return render(options);
30 | },
31 | unmount: () => {
32 | console.log('purehtml unmount');
33 | return Promise.resolve();
34 | },
35 | };
36 | })(window);
37 |
--------------------------------------------------------------------------------
/main/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import { registerMicroApps, start, setDefaultMountApp } from 'qiankun'
4 | import microApps from './micro-app'
5 | import 'nprogress/nprogress.css'
6 |
7 | Vue.config.productionTip = false
8 |
9 | const instance = new Vue({
10 | render: h => h(App)
11 | }).$mount('#app')
12 |
13 | // 定义loader方法,loading改变时,将变量赋值给App.vue的data中的isLoading
14 | function loader (loading) {
15 | if (instance && instance.$children) {
16 | // instance.$children[0] 是App.vue,此时直接改动App.vue的isLoading
17 | instance.$children[0].isLoading = loading
18 | }
19 | }
20 |
21 | // 给子应用配置加上loader方法
22 | const apps = microApps.map(item => {
23 | return {
24 | ...item,
25 | loader
26 | }
27 | })
28 |
29 | registerMicroApps(apps, {
30 | beforeLoad: app => {
31 | console.log('before load app.name====>>>>>', app.name)
32 | },
33 | beforeMount: [
34 | app => {
35 | console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name)
36 | }
37 | ],
38 | afterMount: [
39 | app => {
40 | console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name)
41 | }
42 | ],
43 | afterUnmount: [
44 | app => {
45 | console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name)
46 | }
47 | ]
48 | })
49 | setDefaultMountApp('/sub-vue')
50 | start()
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qiankun-example",
3 | "version": "0.0.1",
4 | "description": "qiankun demo",
5 | "main": "index.js",
6 | "dependencies": {},
7 | "devDependencies": {
8 | "npm-run-all": "^4.1.5"
9 | },
10 | "scripts": {
11 | "install": "npm-run-all --serial install:*",
12 | "install:main": "cd main && npm i",
13 | "install:sub-vue": "cd sub-vue && npm i",
14 | "install:sub-react": "cd sub-react && npm i",
15 | "install:sub-html": "cd sub-html && npm i",
16 | "start": "npm-run-all --parallel start:*",
17 | "start:sub-react": "cd sub-react && npm start",
18 | "start:sub-vue": "cd sub-vue && npm start",
19 | "start:main": "cd main && npm start",
20 | "start:sub-html": "cd sub-html && npm start",
21 | "build": "npm-run-all build:* && bash ./scripts/bundle.sh",
22 | "build:sub-react": "cd sub-react && npm run build",
23 | "build:sub-vue": "cd sub-vue && npm run build",
24 | "build:sub-html": "cd sub-html && npm run build",
25 | "build:main": "cd main && npm run build",
26 | "test": "echo \"Error: no test specified\" && exit 1"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git@fengxianqi.github.com:fengxianqi/qiankun-example.git"
31 | },
32 | "keywords": [
33 | "qiankun",
34 | "qiankun-example",
35 | "demo"
36 | ],
37 | "author": "fengxianqi",
38 | "license": "MIT"
39 | }
40 |
--------------------------------------------------------------------------------
/sub-vue/src/main.js:
--------------------------------------------------------------------------------
1 | import './public-path'
2 | import Vue from 'vue'
3 | import App from './App.vue'
4 | import routes from './router'
5 | import { store as commonStore } from 'common'
6 | import store from './store'
7 | import VueRouter from 'vue-router'
8 |
9 | Vue.config.productionTip = false
10 | let instance = null
11 |
12 | function render (props = {}) {
13 | const { container, routerBase } = props
14 | const router = new VueRouter({
15 | base: window.__POWERED_BY_QIANKUN__ ? routerBase : process.env.BASE_URL,
16 | mode: 'history',
17 | routes
18 | })
19 |
20 | instance = new Vue({
21 | router,
22 | store,
23 | render: (h) => h(App)
24 | }).$mount(container ? container.querySelector('#app') : '#app')
25 | }
26 |
27 | if (!window.__POWERED_BY_QIANKUN__) {
28 | // 这里是子应用独立运行的环境,实现子应用的登录逻辑
29 |
30 | // 独立运行时,也注册一个名为global的store module
31 | commonStore.globalRegister(store)
32 | // 模拟登录后,存储用户信息到global module
33 | const userInfo = { name: '我是独立运行时名字叫张三' } // 假设登录后取到的用户信息
34 | store.commit('global/setGlobalState', { user: userInfo })
35 |
36 | render()
37 | }
38 |
39 | export async function bootstrap () {
40 | console.log('[vue] vue app bootstraped')
41 | }
42 |
43 | export async function mount (props) {
44 | console.log('[vue] props from main framework', props)
45 |
46 | commonStore.globalRegister(store, props)
47 |
48 | render(props)
49 | }
50 |
51 | export async function unmount () {
52 | instance.$destroy()
53 | instance.$el.innerHTML = ''
54 | instance = null
55 | }
56 |
--------------------------------------------------------------------------------
/common/src/store/global-register.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | *
4 | * @param {vuex实例} store
5 | * @param {qiankun下发的props} props
6 | */
7 | function registerGlobalModule (store, props = {}) {
8 |
9 | if (!store || !store.hasModule) {
10 | return;
11 | }
12 |
13 | // 获取初始化的state
14 | const initState = props.getGlobalState && props.getGlobalState() || {
15 | menu: [],
16 | user: {}
17 | };
18 |
19 | // 将父应用的数据存储到子应用中,命名空间固定为global
20 | if (!store.hasModule('global')) {
21 | const globalModule = {
22 | namespaced: true,
23 | state: initState,
24 | actions: {
25 | // 子应用改变state并通知父应用
26 | setGlobalState ({ commit }, payload) {
27 | commit('setGlobalState', payload);
28 | commit('emitGlobalState', payload);
29 | },
30 | // 初始化,只用于mount时同步父应用的数据
31 | initGlobalState ({ commit }, payload) {
32 | commit('setGlobalState', payload);
33 | },
34 | },
35 | mutations: {
36 | setGlobalState (state, payload) {
37 | // eslint-disable-next-line
38 | state = Object.assign(state, payload);
39 | },
40 | // 通知父应用
41 | emitGlobalState (state) {
42 | if (props.setGlobalState) {
43 | props.setGlobalState(state);
44 | }
45 | },
46 | },
47 | };
48 | store.registerModule('global', globalModule);
49 | } else {
50 | // 每次mount时,都同步一次父应用数据
51 | store.dispatch('global/initGlobalState', initState);
52 | }
53 | };
54 |
55 | export default registerGlobalModule;
56 |
--------------------------------------------------------------------------------
/sub-react/src/index.js:
--------------------------------------------------------------------------------
1 | import './public-path'
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import './index.css';
5 | import App from './App';
6 | import * as serviceWorker from './serviceWorker';
7 |
8 |
9 |
10 |
11 |
12 | function render() {
13 | ReactDOM.render(
14 | ,
15 | document.getElementById('root')
16 | );
17 | }
18 |
19 | if (!window.__POWERED_BY_QIANKUN__) {
20 | render();
21 | }
22 | /**
23 | * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
24 | * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
25 | */
26 | export async function bootstrap() {
27 | console.log('react app bootstraped');
28 | }
29 | /**
30 | * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
31 | */
32 | export async function mount(props) {
33 | console.log('基座下发的能力:', props);
34 |
35 | // 可通过 props.getGlobalState() 获取基座下发的数据
36 |
37 | // props.setGlobalState({user: {name: ''}}) 改变全局的数据
38 |
39 | // props.onGlobalStateChange 监听全局数据的变化
40 | render();
41 | }
42 | /**
43 | * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
44 | */
45 | export async function unmount() {
46 | ReactDOM.unmountComponentAtNode(document.getElementById('root'));
47 | }
48 | /**
49 | * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
50 | */
51 | export async function update(props) {
52 | console.log('update props', props);
53 | }
54 |
55 | // If you want your app to work offline and load faster, you can change
56 | // unregister() to register() below. Note this comes with some pitfalls.
57 | // Learn more about service workers: https://bit.ly/CRA-PWA
58 | serviceWorker.unregister();
59 |
--------------------------------------------------------------------------------
/sub-react/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/sub-vue/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Home |
5 | About
6 |
7 |
8 |
当前处于{{ isInQiankun ? 'qiankun' : '独立运行'}}环境
9 |
vuex的`global module`的user state: {{ JSON.stringify(user) }}
10 |
11 |
12 |
13 | 从当前子应用内跳转到`sub-react`子应用
14 | 独立打开sub-vue子应用
15 |
16 | 改变全局的用户名称
17 |
18 |
19 |
20 |
21 |
22 |
58 |
59 |
89 |
--------------------------------------------------------------------------------
/sub-vue/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
61 |
--------------------------------------------------------------------------------
/sub-react/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/sub-react/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `yarn start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `yarn test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `yarn build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `yarn eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | 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.
35 |
36 | 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.
37 |
38 | 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.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `yarn build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/main/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
93 |
94 |
133 |
--------------------------------------------------------------------------------
/sub-react/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/sub-html/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sub-html",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "async": {
8 | "version": "2.6.3",
9 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
10 | "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
11 | "dev": true,
12 | "requires": {
13 | "lodash": "^4.17.14"
14 | }
15 | },
16 | "basic-auth": {
17 | "version": "1.1.0",
18 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz",
19 | "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=",
20 | "dev": true
21 | },
22 | "call-bind": {
23 | "version": "1.0.2",
24 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
25 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
26 | "dev": true,
27 | "requires": {
28 | "function-bind": "^1.1.1",
29 | "get-intrinsic": "^1.0.2"
30 | }
31 | },
32 | "colors": {
33 | "version": "1.4.0",
34 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
35 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
36 | "dev": true
37 | },
38 | "corser": {
39 | "version": "2.0.1",
40 | "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
41 | "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=",
42 | "dev": true
43 | },
44 | "cross-env": {
45 | "version": "7.0.3",
46 | "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
47 | "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
48 | "dev": true,
49 | "requires": {
50 | "cross-spawn": "^7.0.1"
51 | }
52 | },
53 | "cross-spawn": {
54 | "version": "7.0.3",
55 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
56 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
57 | "dev": true,
58 | "requires": {
59 | "path-key": "^3.1.0",
60 | "shebang-command": "^2.0.0",
61 | "which": "^2.0.1"
62 | }
63 | },
64 | "debug": {
65 | "version": "3.2.7",
66 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
67 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
68 | "dev": true,
69 | "requires": {
70 | "ms": "^2.1.1"
71 | }
72 | },
73 | "eventemitter3": {
74 | "version": "4.0.7",
75 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
76 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
77 | "dev": true
78 | },
79 | "follow-redirects": {
80 | "version": "1.14.1",
81 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
82 | "integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
83 | "dev": true
84 | },
85 | "function-bind": {
86 | "version": "1.1.1",
87 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
88 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
89 | "dev": true
90 | },
91 | "get-intrinsic": {
92 | "version": "1.1.1",
93 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
94 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
95 | "dev": true,
96 | "requires": {
97 | "function-bind": "^1.1.1",
98 | "has": "^1.0.3",
99 | "has-symbols": "^1.0.1"
100 | }
101 | },
102 | "has": {
103 | "version": "1.0.3",
104 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
105 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
106 | "dev": true,
107 | "requires": {
108 | "function-bind": "^1.1.1"
109 | }
110 | },
111 | "has-symbols": {
112 | "version": "1.0.2",
113 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
114 | "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
115 | "dev": true
116 | },
117 | "he": {
118 | "version": "1.2.0",
119 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
120 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
121 | "dev": true
122 | },
123 | "http-proxy": {
124 | "version": "1.18.1",
125 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
126 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
127 | "dev": true,
128 | "requires": {
129 | "eventemitter3": "^4.0.0",
130 | "follow-redirects": "^1.0.0",
131 | "requires-port": "^1.0.0"
132 | }
133 | },
134 | "http-server": {
135 | "version": "13.0.0",
136 | "resolved": "https://registry.npmjs.org/http-server/-/http-server-13.0.0.tgz",
137 | "integrity": "sha512-tqOx2M1CiZ3aVaE7Ue/0lup9kOG+Zqg6wdT1HygvxFnvPpU9doBMPcQ1ffT0/QS3J9ua35gipg0o3Dr8N0K0Tg==",
138 | "dev": true,
139 | "requires": {
140 | "basic-auth": "^1.0.3",
141 | "colors": "^1.4.0",
142 | "corser": "^2.0.1",
143 | "he": "^1.1.0",
144 | "http-proxy": "^1.18.0",
145 | "mime": "^1.6.0",
146 | "minimist": "^1.2.5",
147 | "opener": "^1.5.1",
148 | "portfinder": "^1.0.25",
149 | "secure-compare": "3.0.1",
150 | "union": "~0.5.0",
151 | "url-join": "^2.0.5"
152 | }
153 | },
154 | "isexe": {
155 | "version": "2.0.0",
156 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
157 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
158 | "dev": true
159 | },
160 | "lodash": {
161 | "version": "4.17.21",
162 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
163 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
164 | "dev": true
165 | },
166 | "mime": {
167 | "version": "1.6.0",
168 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
169 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
170 | "dev": true
171 | },
172 | "minimist": {
173 | "version": "1.2.5",
174 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
175 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
176 | "dev": true
177 | },
178 | "mkdirp": {
179 | "version": "0.5.5",
180 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
181 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
182 | "dev": true,
183 | "requires": {
184 | "minimist": "^1.2.5"
185 | }
186 | },
187 | "ms": {
188 | "version": "2.1.3",
189 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
190 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
191 | "dev": true
192 | },
193 | "object-inspect": {
194 | "version": "1.11.0",
195 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz",
196 | "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==",
197 | "dev": true
198 | },
199 | "opener": {
200 | "version": "1.5.2",
201 | "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz",
202 | "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
203 | "dev": true
204 | },
205 | "path-key": {
206 | "version": "3.1.1",
207 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
208 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
209 | "dev": true
210 | },
211 | "portfinder": {
212 | "version": "1.0.28",
213 | "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
214 | "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
215 | "dev": true,
216 | "requires": {
217 | "async": "^2.6.2",
218 | "debug": "^3.1.1",
219 | "mkdirp": "^0.5.5"
220 | }
221 | },
222 | "qs": {
223 | "version": "6.10.1",
224 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz",
225 | "integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==",
226 | "dev": true,
227 | "requires": {
228 | "side-channel": "^1.0.4"
229 | }
230 | },
231 | "requires-port": {
232 | "version": "1.0.0",
233 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
234 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
235 | "dev": true
236 | },
237 | "secure-compare": {
238 | "version": "3.0.1",
239 | "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
240 | "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=",
241 | "dev": true
242 | },
243 | "shebang-command": {
244 | "version": "2.0.0",
245 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
246 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
247 | "dev": true,
248 | "requires": {
249 | "shebang-regex": "^3.0.0"
250 | }
251 | },
252 | "shebang-regex": {
253 | "version": "3.0.0",
254 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
255 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
256 | "dev": true
257 | },
258 | "side-channel": {
259 | "version": "1.0.4",
260 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
261 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
262 | "dev": true,
263 | "requires": {
264 | "call-bind": "^1.0.0",
265 | "get-intrinsic": "^1.0.2",
266 | "object-inspect": "^1.9.0"
267 | }
268 | },
269 | "union": {
270 | "version": "0.5.0",
271 | "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
272 | "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
273 | "dev": true,
274 | "requires": {
275 | "qs": "^6.4.0"
276 | }
277 | },
278 | "url-join": {
279 | "version": "2.0.5",
280 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz",
281 | "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=",
282 | "dev": true
283 | },
284 | "which": {
285 | "version": "2.0.2",
286 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
287 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
288 | "dev": true,
289 | "requires": {
290 | "isexe": "^2.0.0"
291 | }
292 | }
293 | }
294 | }
295 |
--------------------------------------------------------------------------------