├── mock-component
├── lib
│ ├── img
│ │ ├── logo.82b9c7a5.png
│ │ └── timg.5296162f.jpeg
│ ├── fonts
│ │ ├── element-icons.535877f5.woff
│ │ └── element-icons.732389de.ttf
│ ├── issue3.test2.js
│ ├── issue3.test1.js
│ └── vueComponent3.umd.js
└── server.js
├── src
├── index.ts
└── micro-frame
│ ├── react-frame.tsx
│ ├── vue-frame.tsx
│ └── base.tsx
├── config
├── rollup
│ ├── rollup.prod.conf.js
│ ├── rollup.dev.conf.js
│ └── rollup.base.conf.js
└── webpack
│ └── webpack.config.js
├── demo
├── index.html
├── src
│ ├── components
│ │ └── index.vue
│ └── index.tsx
├── issues
│ └── issue.3.demo
│ │ └── index.tsx
└── external-lib
│ ├── index.js
│ └── index.js.map
├── jest.config.js
├── .editorconfig
├── .babelrc
├── tsconfig.json
├── tslint.js
├── .gitignore
├── index.d.ts
├── package.json
├── README-zh_CN.md
├── README.md
└── lib
├── index.d.ts
├── index.js
└── index.js.map
/mock-component/lib/img/logo.82b9c7a5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/y805939188/react-vue-micro-frame/HEAD/mock-component/lib/img/logo.82b9c7a5.png
--------------------------------------------------------------------------------
/mock-component/lib/img/timg.5296162f.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/y805939188/react-vue-micro-frame/HEAD/mock-component/lib/img/timg.5296162f.jpeg
--------------------------------------------------------------------------------
/mock-component/lib/fonts/element-icons.535877f5.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/y805939188/react-vue-micro-frame/HEAD/mock-component/lib/fonts/element-icons.535877f5.woff
--------------------------------------------------------------------------------
/mock-component/lib/fonts/element-icons.732389de.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/y805939188/react-vue-micro-frame/HEAD/mock-component/lib/fonts/element-icons.732389de.ttf
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import VueFrame from './micro-frame/vue-frame';
2 | import ReactFrame from './micro-frame/react-frame';
3 |
4 | export default VueFrame;
5 |
6 | export { VueFrame, ReactFrame };
7 |
--------------------------------------------------------------------------------
/config/rollup/rollup.prod.conf.js:
--------------------------------------------------------------------------------
1 | import merge from 'lodash/merge';
2 | import baseConf from './rollup.base.conf';
3 |
4 | const prodConf = { };
5 |
6 | export default merge(baseConf, prodConf);
7 |
8 |
--------------------------------------------------------------------------------
/mock-component/server.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HttpServer = require('http-server');
3 |
4 | const server = HttpServer.createServer({ root: path.join(__dirname, 'lib') });
5 | server.listen(20522, () => {
6 | console.log('正在监听20522端口的mock组件')
7 | });
8 |
9 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Document
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | collectCoverage: true,
3 | "transform": {
4 | "^.+\\.(t|j)sx?$": "ts-jest"
5 | },
6 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
7 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
8 | }
9 |
--------------------------------------------------------------------------------
/.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 | charset = utf-8
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [{*.json,*.md,*.yml,*.*rc}]
15 | indent_style = space
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": [ "ie >= 11", "last 2 versions" ]
7 | }
8 | }],
9 | "@babel/preset-react"
10 | ],
11 | "plugins": [
12 | "@babel/plugin-external-helpers",
13 | "@babel/plugin-syntax-dynamic-import",
14 | "@babel/plugin-proposal-optional-chaining"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/demo/src/components/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
28 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "",
4 | "jsx": "react",
5 | "target": "es5",
6 | "module": "ES2015",
7 | "lib": [
8 | "es2015",
9 | "dom",
10 | "scripthost",
11 | "dom.iterable"
12 | ],
13 | "moduleResolution": "node",
14 | "sourceMap": true,
15 | "strict": true,
16 | "allowSyntheticDefaultImports": true,
17 | "esModuleInterop": true,
18 | "allowJs": true,
19 | "baseUrl": "./src"
20 | },
21 | "include": ["src", "typings"],
22 | "exclude": ["node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------
/tslint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": ["tslint-config-airbnb"],
3 | "jsRules": {},
4 | "rules": {
5 | "indent": [true, "spaces", 2],
6 | "align": [true, "parameters", "statements"], // 这个就是会让函数调用参数自动对齐的sb配置
7 | "ter-indent": [true, 2, { "SwitchCase": 1 }],
8 | "import-name": false,
9 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"],
10 | "function-name": false,
11 | "max-line-length": [true, 100],
12 | "no-increment-decrement": false,
13 | },
14 | "rulesDirectory": [],
15 | "linterOptions": {
16 | "exclude": ["node_modules", "src/**/*.test.*"]
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/config/rollup/rollup.dev.conf.js:
--------------------------------------------------------------------------------
1 | import clear from 'rollup-plugin-clear';
2 | import merge from 'lodash/merge';
3 | import rollupBaseConf from './rollup.base.conf';
4 | // import serve from 'rollup-plugin-serve';
5 | // import livereload from 'rollup-plugin-livereload';
6 |
7 | const originPath = process.cwd();
8 | const devConfig = {
9 | output: {
10 | file: `${originPath}/demo/external-lib/index.js`,
11 | name: 'react-vue-mirco-frame',
12 | format: 'esm',
13 | sourcemap: true,
14 | },
15 | plugins: [
16 | // clear({
17 | // targets: [`${originPath}/demo/external-lib`],
18 | // })
19 | // serve({
20 | // open: true,
21 | // contentBase: `${originPath}/demo`,
22 | // host: 'localhost',
23 | // port: 3190,
24 | // }),
25 | // livereload(),
26 | ]
27 | };
28 |
29 | const config = merge(rollupBaseConf, devConfig);
30 |
31 | export default config;
32 |
--------------------------------------------------------------------------------
/demo/issues/issue.3.demo/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from 'react';
2 | import { ReactFrame } from '../../../src';
3 | const TestComponent: React.FC = (props) => {
4 | const [ file, setReview ] = useState(null);
5 |
6 | const submit = useCallback(() => {
7 | setReview(null);
8 | setTimeout(() => {
9 | const component = (
10 |
11 | )
12 | setReview(component);
13 | }, 1000);
14 | }, [file]);
15 |
16 | const preview = () => {
17 | const component = (
18 |
19 | )
20 | setReview(component);
21 | }
22 |
23 | return (
24 |
25 |
26 |
27 |
28 | preview:
29 |
30 | {file}
31 |
32 |
33 |
34 | )
35 | }
36 |
37 | export default TestComponent;
38 |
--------------------------------------------------------------------------------
/config/rollup/rollup.base.conf.js:
--------------------------------------------------------------------------------
1 | // import resolve from 'rollup-plugin-node-resolve';
2 | import commonjs from 'rollup-plugin-commonjs';
3 | import json from 'rollup-plugin-json';
4 | import babel from 'rollup-plugin-babel';
5 | import typescript from 'rollup-plugin-typescript2';
6 | import sourcemaps from 'rollup-plugin-sourcemaps';
7 | import { DEFAULT_EXTENSIONS } from '@babel/core';
8 |
9 | const mode = process.env.NODE_ENV;
10 | const isDev = mode === 'development';
11 | const originPath = process.cwd();
12 |
13 | export default {
14 | input: `${originPath}/src/index.ts`,
15 | output: {
16 | file: `${originPath}/lib/index.js`,
17 | name: 'react-vue-mirco-frame',
18 | format: 'esm',
19 | sourcemap: true,
20 | },
21 | watch: {
22 | include: [`${originPath}/demo/**`, `${originPath}/src/**`],
23 | },
24 | plugins: [
25 | typescript({ declaration: true }),
26 | babel({
27 | exclude: `${originPath}/node_modules/**`,
28 | extensions: [
29 | ...DEFAULT_EXTENSIONS,
30 | '.ts',
31 | '.tsx',
32 | ]
33 | }),
34 | json(),
35 | commonjs(),
36 | // resolve(),
37 | sourcemaps(),
38 | ]
39 | };
40 |
--------------------------------------------------------------------------------
/src/micro-frame/react-frame.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import singleSpaReact from 'single-spa-react';
4 | import BaseFrame, { IProps } from './base';
5 |
6 | const httpReg = new RegExp("^https?://[\\w-.]+(:\\d+)?", 'i');
7 |
8 | export default class ReactIframe extends BaseFrame {
9 | framework: typeof React;
10 | currentUrl: string;
11 | currentPublicPath: string;
12 | component: any;
13 |
14 | constructor(props: IProps) {
15 | super(props);
16 | const { jsurl, component } = props;
17 | this.framework = React;
18 | // 获取到外部传进来的vue组件
19 | this.component = component;
20 | // 获取到外部传来的url
21 | this.currentUrl = jsurl || '';
22 | // 获取传进来的url的协议+域名+端口
23 | this.currentPublicPath = `${(httpReg.exec(this.currentUrl) || [''])[0]}/`;
24 | }
25 |
26 | getDom = () => this.oWrapper2;
27 |
28 | registerComponent = (el: string | HTMLElement, reactComponent: object, id: string) => {
29 | const reactInstance = singleSpaReact({
30 | React: React as any,
31 | ReactDOM: ReactDOM as typeof ReactDOM,
32 | rootComponent: reactComponent as React.ComponentClass,
33 | });
34 | return ({
35 | bootstrap: reactInstance.bootstrap,
36 | mount: reactInstance.mount,
37 | unmount: reactInstance.unmount,
38 | update: (reactInstance as any).update,
39 | });
40 | }
41 |
42 | isComponent = (component: any): boolean => {
43 | return component && typeof component === 'function' ||
44 | (typeof component === 'object' && component.$$typeof);
45 | }
46 |
47 | executeOriginCode = (code: string) => {}
48 | }
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import Vue from 'vue';
3 |
4 | export interface IProps0 {
5 | cssurl?: string;
6 | name?: string;
7 | id?: string;
8 | visible?: boolean;
9 | extraProps?: { [propName: string]: any };
10 | loadType?: 'xhr' | 'script';
11 | instable_publicPath?: string;
12 | }
13 |
14 | export interface IProps1 extends IProps0 {
15 | jsurl: string;
16 | component?: object;
17 | }
18 |
19 | export interface IProps2 extends IProps0 {
20 | component: object;
21 | jsurl?: string;
22 | }
23 |
24 | export interface Self {
25 | Vue: typeof Vue;
26 | [ propName: string ]: any;
27 | }
28 |
29 | export type IProps = IProps1 | IProps2;
30 |
31 | declare class VueFrame extends React.Component {
32 | private loadType: IProps['loadType'];
33 | private currentName: string;
34 | private visible: boolean;
35 | private currentUrl: string;
36 | private currentCSSUrl: string;
37 | private currentPublicPath: string;
38 | private publicPathKey: string;
39 | private publicPathReg: RegExp;
40 | private rootNodeWrapper: RefObject;;
41 | private component: any;
42 | private parcel: any;
43 | private vueWrapper1: HTMLDivElement;
44 | private vueWrapper2: HTMLDivElement;
45 | private styleElements: HTMLLinkElement[] | HTMLStyleElement[];
46 |
47 | constructor(props: IProps);
48 | componentDidMount(): void;
49 | componentDidUpdate(prevProps: IProps): void;
50 | componentWillUnmount(): void;
51 | private initHack(): void;
52 | private initHackSelector(name: keyof ISelecotr, originSelectorFn: ((id: string) => HTMLElement | null) |
53 | ((id: string) => NodeListOf) |
54 | HTMLCollectionOf): void;
55 | private internalHackSelector(selectorMap: { [name: string]: Function },name: keyof ISelecotr,
56 | codeIsExecuting: boolean,): void;
57 | private internalHackCSSsandbox(originAppendChild: (this: any, newChild: T) => T,
58 | codeIsExecuting: boolean,): void;
59 | private registerComponentAndMount(component: object): void;
60 | private addComponentToPage(rootEleWrapper: HTMLDivElement): void;
61 | private registerVueComponent(el: string | HTMLElement, vueComponent: object, id: string): object;
62 | private isVueComponent(component: any): boolean;
63 | private getOriginCode(url: string, method: string, data?: any): Promise;
64 | private getCurrentName(self: any): string;
65 | private executeOriginCode(code: string): Self;
66 | private getOriginVueComponent(): object;
67 |
68 | render(): JSX.Element;
69 | }
70 |
71 | export default VueFrame;
72 |
--------------------------------------------------------------------------------
/demo/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactDOM from 'react-dom';
3 | // @ts-ignore
4 | import VueIFrame, { ReactFrame } from '../../src/index';
5 | import { Tabs } from 'antd';
6 | import 'antd/dist/antd.css';
7 | // @ts-ignore
8 | import VueComponent from './components/index.vue';
9 |
10 | // import Issue3 from '../issues/issue.3.demo';
11 |
12 | const TabPane = Tabs.TabPane;
13 | const Test: React.FC<{}> = () => {
14 | const [ number1, setNumber1 ] = useState(6);
15 | const [ active2, setActive2 ] = useState(true);
16 | const [ active3, setActive3 ] = useState(true);
17 | const [ active4, setActive4 ] = useState(false);
18 | const handleClick1 = () => setNumber1(number1 + 1);
19 | const handleClick2 = () => setActive2(!active2);
20 | const handleClick3 = () => setActive3(!active3);
21 | const handleClick4 = () => setActive4(!active4);
22 | return (
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
54 |
55 |
56 |
57 | )
58 | }
59 |
60 |
61 | // const Test: React.FC<{}> = () => {
62 | // return (
63 | //
64 | // )
65 | // }
66 |
67 |
68 | ReactDOM.render(
69 | ,
70 | document.querySelector("#react"),
71 | )
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/config/webpack/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const { CheckerPlugin } = require('awesome-typescript-loader');
5 | const VueLoaderPlugin = require('vue-loader/lib/plugin');
6 |
7 | const originPath = process.cwd();
8 | const demoPath = `${originPath}/demo`;
9 | module.exports = {
10 | mode: 'development',
11 | entry: `${demoPath}/src/index.tsx`,
12 | output: {
13 | filename: 'app.js',
14 | path: `${demoPath}/dist`,
15 | },
16 | devtool: 'source-map',
17 | externals: [],
18 | devServer: {
19 | port: 19411,
20 | contentBase: `${demoPath}/src`,
21 | historyApiFallback: true,
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.css$/,
27 | use: ['style-loader', 'css-loader']
28 | },
29 | {
30 | test: /\.(js|jsx|ts|tsx)$/,
31 | include: `${demoPath}/src`,
32 | exclude: /\.vue$/,
33 | use: {
34 | loader: 'babel-loader',
35 | options: {
36 | presets: [
37 | ["@babel/preset-env", {
38 | "modules": false,
39 | "targets": {
40 | "browsers": [ "ie >= 11", "last 2 versions" ]
41 | }
42 | }],
43 | '@babel/preset-react'
44 | ],
45 | plugins: [path.join(originPath, 'node_modules', '@babel/plugin-syntax-dynamic-import')],
46 | }
47 | },
48 | },
49 | // {
50 | // test: /\.vue$/,
51 | // include: `${demoPath}/src`,
52 | // use: {
53 | // loader: 'babel-loader',
54 | // options: {
55 | // presets: [
56 | // ["@babel/preset-env", {
57 | // "modules": false,
58 | // "targets": {
59 | // "browsers": [ "ie >= 11", "last 2 versions" ]
60 | // }
61 | // }],
62 | // ],
63 | // plugins: [path.join(originPath, 'node_modules', '@babel/plugin-syntax-dynamic-import')],
64 | // }
65 | // },
66 | // },
67 | {
68 | test: /\.vue$/,
69 | loader: 'vue-loader',
70 | // include: path.join(originPath, 'demo', 'src')
71 | },
72 | {
73 | test: /\.tsx?$/,
74 | use: 'awesome-typescript-loader',
75 | exclude: /node_modules/
76 | },
77 | ],
78 | },
79 | resolve: {
80 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
81 | alias: {
82 | 'external-lib': path.join(demoPath, 'external-lib'),
83 | },
84 | },
85 | plugins: [
86 | new HtmlWebpackPlugin({
87 | template: path.join(demoPath, 'index.html')
88 | }),
89 | new CheckerPlugin(),
90 | new VueLoaderPlugin(),
91 | ],
92 | };
93 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-vue-micro-frame",
3 | "version": "0.1.7",
4 | "description": "react-vue-micro-frame",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "start": "concurrently \"npm run start:demo\" \"npm run start:rollup\" \"node ./mock-component/server.js\"",
8 | "start:rollup": "NODE_ENV=development rollup --config ./config/rollup/rollup.dev.conf.js -w",
9 | "start:demo": "node_modules/.bin/webpack-dev-server --config config/webpack/webpack.config.js --open",
10 | "start2": "NODE_ENV=development rollup --config ./config/rollup.dev.conf.js -w && livereload 'src/' ",
11 | "build": "NODE_ENV=production rollup --config ./config/rollup/rollup.prod.conf.js",
12 | "test": "jest --config",
13 | "publish:component": "npm run build && npm publish"
14 | },
15 | "author": "",
16 | "license": "ISC",
17 | "devDependencies": {
18 | "@babel/core": "^7.8.4",
19 | "@babel/plugin-external-helpers": "^7.8.3",
20 | "@babel/plugin-proposal-optional-chaining": "^7.8.3",
21 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
22 | "@babel/plugin-transform-runtime": "^7.8.3",
23 | "@babel/preset-env": "^7.7.6",
24 | "@babel/preset-react": "^7.8.3",
25 | "@types/jest": "^24.0.23",
26 | "antd": "^3.26.9",
27 | "awesome-typescript-loader": "^5.2.1",
28 | "babel-loader": "^8.0.6",
29 | "babel-plugin-external-helpers": "^6.22.0",
30 | "clean-webpack-plugin": "^3.0.0",
31 | "concurrently": "^5.1.0",
32 | "css-loader": "^3.4.2",
33 | "custom-event": "^1.0.1",
34 | "dtsmake": "0.0.10",
35 | "express": "^4.17.1",
36 | "html-webpack-plugin": "^3.2.0",
37 | "http-server": "^0.12.0",
38 | "jest": "^24.9.0",
39 | "lodash": "^4.17.15",
40 | "prettier": "^1.19.1",
41 | "rollup": "^1.27.13",
42 | "rollup-plugin-babel": "^4.3.3",
43 | "rollup-plugin-clear": "^2.0.7",
44 | "rollup-plugin-commonjs": "^10.1.0",
45 | "rollup-plugin-json": "^4.0.0",
46 | "rollup-plugin-live-server": "^1.0.3",
47 | "rollup-plugin-livereload": "^1.0.4",
48 | "rollup-plugin-node-resolve": "^5.2.0",
49 | "rollup-plugin-serve": "^1.0.1",
50 | "rollup-plugin-sourcemaps": "^0.4.2",
51 | "rollup-plugin-typescript": "^1.0.1",
52 | "rollup-plugin-typescript2": "^0.25.3",
53 | "style-loader": "^1.1.3",
54 | "ts-jest": "^24.2.0",
55 | "tslint": "^6.0.0-beta0",
56 | "tslint-config-airbnb": "^5.11.2",
57 | "tslint-config-prettier": "^1.18.0",
58 | "typescript": "^3.7.3",
59 | "vue-loader": "^15.9.0",
60 | "vue-template-compiler": "^2.6.11",
61 | "webpack": "^4.41.6",
62 | "webpack-cli": "^3.3.11",
63 | "webpack-dev-server": "^3.10.3"
64 | },
65 | "dependencies": {
66 | "@types/lodash": "^4.14.149",
67 | "@types/react": "^16.9.19",
68 | "@types/react-dom": "^16.9.5",
69 | "@types/single-spa-react": "^2.8.3",
70 | "babel-runtime": "^6.26.0",
71 | "react": "^16.12.0",
72 | "react-dom": "^16.12.0",
73 | "single-spa": "^4.4.3",
74 | "single-spa-react": "^2.11.0",
75 | "single-spa-vue": "1.7.0",
76 | "vue": "^2.6.11"
77 | },
78 | "repository": {
79 | "type": "git",
80 | "url": "git@github.com:y805939188/react-vue-micro-frame.git"
81 | },
82 | "keywords": [
83 | "react-micro-frontends",
84 | "vue-micro-frontends",
85 | "react-vue-micro-frontends",
86 | "micro-frontends",
87 | "vue-micro-frontends",
88 | "react-micro-frontends",
89 | "react-vue-micro-frame",
90 | "vue-in-react",
91 | "vue in react",
92 | "react-vue"
93 | ]
94 | }
95 |
--------------------------------------------------------------------------------
/src/micro-frame/vue-frame.tsx:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import singleSpaVue from 'single-spa-vue';
3 | import BaseFrame, { IProps } from './base';
4 |
5 | const __VUE_INTERNAL_INIT__ = Vue.prototype._init;
6 | Vue.prototype._init = function(options: any) {
7 | /**
8 | * TODO: 留个口儿 用来以后支持加载整个Vue应用
9 | */
10 | __VUE_INTERNAL_INIT__.call(this, options);
11 | };
12 |
13 | interface Self {
14 | Vue: typeof Vue;
15 | [ propName: string ]: any;
16 | }
17 |
18 | const httpReg = new RegExp("^https?://[\\w-.]+(:\\d+)?", 'i');
19 |
20 | export default class VueIframe extends BaseFrame {
21 | framework: typeof Vue;
22 | currentUrl: string; // 传进来的url
23 | currentPublicPath: string; // 传进来的url的协议+域名+端口
24 | component: any;
25 |
26 | constructor(props: IProps) {
27 | super(props);
28 | const { jsurl, component } = props;
29 | this.framework = Vue;
30 | // 获取到外部传进来的vue组件
31 | this.component = component;
32 | // 获取到外部传来的url
33 | this.currentUrl = jsurl || '';
34 | // 获取传进来的url的协议+域名+端口
35 | this.currentPublicPath = `${(httpReg.exec(this.currentUrl) || [''])[0]}/`;
36 | }
37 |
38 | registerComponent = (el: string | HTMLElement, vueComponent: object, id: string) => {
39 | const vueInstance = singleSpaVue({
40 | Vue,
41 | appOptions: {
42 | el: typeof el === 'string' ? `#${el}` : el,
43 | render: (h: any) => h('div', { attrs: { id } }, [h(vueComponent, {
44 | attrs: { ...this.extraProps },
45 | })]),
46 | },
47 | });
48 | return ({
49 | bootstrap: vueInstance.bootstrap,
50 | mount: vueInstance.mount,
51 | unmount: vueInstance.unmount,
52 | update: vueInstance.update,
53 | })
54 | }
55 |
56 | isComponent = (component: any): boolean => {
57 | return component && typeof component === 'object' && typeof component.render === 'function';
58 | }
59 |
60 | executeOriginCode = (code: string): Self => {
61 | const internalSelf: Self = { Vue };
62 | const reg = this.publicPathReg;
63 | const publicPath = this.currentPublicPath;
64 | const url = this.currentUrl;
65 | if (reg.test(code)) {
66 | /**
67 | * 自己开发组件的时候使用可以配置publicPath
68 | * 为了让react-vue-mirco-frame支持加载静态资源
69 | * 可以给publicPath设置为一个约定好的值
70 | * 然后这里用传进来的origin替换掉这个约定好的值
71 | * 这个约定好的值默认是 __WILL_BE_REPLACED_PUBLIC_PATH__
72 | */
73 | const codeStr = code.replace(reg, publicPath);
74 | const originCodeFn = new Function("self", codeStr);
75 | originCodeFn(internalSelf);
76 | } else {
77 | /**
78 | * 如果没有配置这个值的话 就以hack的方式注入origin
79 | * webpack打包出来的umd代码里面会动态监测document.currentScript
80 | * 通过临时给这个currentScript换掉的方式让webpack将origin注入进代码
81 | */
82 | const temporaryDocument = (window.document as any);
83 | const originCurrentScript = window.document.currentScript;
84 | const temporaryScript = document.createElement('script');
85 | const defineProperty = Object.defineProperty;
86 | temporaryScript.src = url;
87 | defineProperty(temporaryDocument, 'currentScript', {
88 | value: temporaryScript,
89 | writable: true,
90 | });
91 | const originCodeFn = new Function("self", code);
92 | originCodeFn(internalSelf);
93 | defineProperty(temporaryDocument, 'currentScript', {
94 | value: originCurrentScript,
95 | writable: false,
96 | });
97 | temporaryScript.remove && temporaryScript.remove();
98 | };
99 | return internalSelf;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/README-zh_CN.md:
--------------------------------------------------------------------------------
1 | ## [English](./README.md) | 简体中文
2 |
3 | # React加载vue的微前端组件
4 | ##### 基于single-spa的react微前端组件
5 | 我有一个梦想, 希望可以在react项目中像加载一个```