
Security News
OpenClaw Skill Marketplace Emerges as Active Malware Vector
Security researchers report widespread abuse of OpenClaw skills to deliver info-stealing malware, exposing a new supply chain risk as agent ecosystems scale.
mcr-bridge 能够让你以一种比较简单的方式实现微前端项目的组件嵌入功能, ta基于的是前端 三大框架(react,angular,vue)的动态创建组件的方法。
mcr-bridge 能够让你以一种比较简单的方式实现微前端项目的组件嵌入功能, ta基于的是前端 三大框架(react,angular,vue)的动态创建组件的方法。
在基于三大框架的前端项目中, 我们要使用一个组件一般都是将组件作为一个标签写在html模板中。 框架在解析模板时会为组件创建实例并挂载组件视图, 这时候''创建组件实例和挂载组件视图''这 个过程是由框架来完成的, 而当这个过程是由我们开发者的业务代码来实现时, 那便是动态创建组 件。
......
import Vue from 'vue';
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
mounted() {
const componentContainer = document.getElementById('hello-world-container');
const componentConstructor = Vue.extend(HelloWorld); // 生成组件构造函数
new componentContainer({
parent: this
}).$mount(componentContainer); // 实例化组件并挂载组件视图
}
}
......
import { ApplicationRef, Component, ComponentFactoryResolver, ElementRef, Injector, ViewChild } from '@angular/core';
import { CpntComponent } from './cpnt/cpnt.component';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
@ViewChild('container') container: ElementRef = null as any
title = 'bridge-ng';
constructor(
private injector: Injector,
private applicationRef: ApplicationRef,
private resolver: ComponentFactoryResolver
) {
}
ngAfterViewInit() {
const componentFactory = this.resolver.resolveComponentFactory(CpntComponent); // 创建组件工厂
const componentRef = componentFactory.create(this.injector); // 生成ComponentRef
this.applicationRef.attachView(componentRef.hostView); // 缺了这一步组件的changes detection 会失效
this.container.nativeElement.appendChild(componentRef.location.nativeElement); // 挂载组件视图,就是普通的dom操作,把组件视图的根节点插入到项目的dom结构中
}
}
import * as ReactDom from 'react-dom';
import * as React from 'react';
import { App } from './App';
const appContainer = document.getElementById('app-container')
ReactDom.render(React.createElement(App), appContainer)
假如有基于angular的主项目ng_app和基于vue的子项目vue_app, vue_app启动时需要加载js 文件http://localhost:8080/chunk-vendor.js和http://localhost:8080/app.js。
......
export default {
name: 'App',
components: {
HelloWorld
},
created() {
window.vueComponentFactory = () => {
let componentConstructor = Vue.extend(HelloWorld);
return new componentConstructor({
parent: this
})
}
if (window.loadResolver) window.loadResolver();
}
}
......
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
@ViewChild('container') container: ElementRef = null as any
......
ngAfterViewInit() {
this.loadProject().then(res => {
let vueComponentInstance = (window as any).vueComponentFactory(); // 调用vueComponentFactory获取vue_app的HelloWorld组件实例
vueComponentInstance.$mount(this.container.nativeElement); // vue_app的HelloWorld组件嵌入到ng_app的app-root组件中
})
}
loadProject() {
const assetList = [
'http://localhost:8080/chunk-vendor.js',
'http://localhost:8080/app.js'
]
return new Promise((resolve, reject) => {
(window as any).loadResolver = resolve; // 在vue_app中调用window.loadResolver()表明window.vueComponentFactory已添加
const doLoad = (index: number) => {
if (index >= assetList.length) {
return
}
const scriptEle = document.createElement('script');
const assetPath = assetList[index];
scriptEle.onload = () => {
doLoad(index + 1) // vue_app的js资源按顺序逐个加载并运行
}
scriptEle.onerror = (err) => {
reject(err)
}
scriptEle.src = assetPath;
document.body.appendChild(scriptEle);
}
doLoad(0)
})
}
}
npm install --save mcr-bridge
import 'mcr-bridge';
......
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
@ViewChild('container') container: ElementRef = null as any
......
ngAfterViewInit() {
const global: any = window;
global.$bridge.instance.setConfig({
projects: {
'vue_app': {
path: 'http://localhost:8080',
jsonp: 'loadChunks.js'
}
}
})
global.$bridge.instance.loadProject('vue_app'/*子项目唯一标识*/).then(() => {
global.$bridge.instance.mountCpnt(
'vue_app',
'helloWorld', // 子项目组件唯一标识
this.container.nativeElement)
});
}
}
import Vue from 'vue';
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
created() {
if (window.$bridge) {
const componentFactory = window.$bridge.resolveVueCpntFactory(Vue.extend(HelloWorld), this);
window.$bridge.instance.registerCpnt('vue_app', 'helloWorld', componentFactory);
global.$bridge.instance.loaded('vue_app') // 通知主项目该子项目已加载完成
}
}
}
import * as ReactDom from 'react-dom';
import * as React from 'react';
import { App } from './App';
// ReactDom.render(<App name="hello"></App>, document.getElementById('app'));
const global: any = window as any;
if (global.$bridge) {
const componentFactory = global.$bridge.resolveReactCpntFactory(App, React, ReactDom);
global.$bridge.instance.registerCpnt('React_app', 'app', componentFactory);
global.$bridge.instance.loaded('React_app') // 通知主项目该子项目已加载完成
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
......
constructor(
private injector: Injector,
private applicationRef: ApplicationRef,
private resolver: ComponentFactoryResolver,
private zone: NgZone
) {
}
ngAfterViewInit() {
const global: any = window;
if (global.$bridge) {
const factory = global.$bridge.resolveNgCpntFactory(CpntComponent, this.resolver, this.applicationRef, this.injector, this.zone);
global.$bridge.instance.registerCpnt(
'ng_app', // 子项目的唯一标识
'cpnt', // 子项目组件的唯一标识
factory
)
global.$bridge.instance.loaded('ng_app') // 通知主项目该子项目已加载完成
}
}
}
import {
......
ɵNG_COMP_DEF,
ɵRender3ComponentFactory,
......
} from '@angular/core';
......
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private injector: Injector,
private applicationRef: ApplicationRef,
private resolver: ComponentFactoryResolver,
private zone: NgZone
) {
}
......
ngAfterViewInit() {
const global: any = window;
if (global.$bridge) {
const factory = global.$bridge.resolveNgCpntFactory2(
CpntComponent[ɵNG_COMP_DEF],
ɵRender3ComponentFactory,
this.applicationRef,
this.injector,
this.zone
);
global.$bridge.instance.registerCpnt(
'ng_app', // 子项目的唯一标识
'cpnt', // 子项目组件的唯一标识
factory
)
global.$bridge.instance.loaded('ng_app') // 通知主项目该子项目已加载完成
}
}
}
window.$bridge.instance.loadProjectJsonp("vue_app", {
js:["js/chunk-vendors.f4b13c22.js", 'js/app.8c4d252c.js'],
css:["css/app.30bf6194.css"]}
);
通过webpack生成模式打包编译生成的js文件都会拼上hash代码,这就导致每一次打包编译生成的文件名都不一样, 而每次打包后都手动更改loadChunks.js的内容显然是很麻烦的事情。
const MicroBridgePlugin = require('mcr-bridge/plugin/mcr-bridge-plugin')
module.exports = {
plugins: [
new MicroBridgePlugin({
projectName: 'vue_app' //项目标识,
js: {
files: ['lodash.js'] // 添加除了打包生成的额外js文件,
exclude: ['zone.js', /\.shared.js$/],
sort: function(file1, file2) {
if (file1 === 'lodash.js') return 1;
if (file2 === 'lodash.js') return -1;
return 0
}
},
css: {}
})
]
}
设置配置项。
获取配置项。
加载子项目。
在子项目中调用,告诉主项目自已已加载完成。
在子项目中注册组件到mcr-bridge。
把子项目的组件挂载带主项目中。
FAQs
mcr-bridge 能够让你以一种比较简单的方式实现微前端项目的组件嵌入功能, ta基于的是前端 三大框架(react,angular,vue)的动态创建组件的方法。
We found that mcr-bridge demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Security researchers report widespread abuse of OpenClaw skills to deliver info-stealing malware, exposing a new supply chain risk as agent ecosystems scale.

Security News
Claude Opus 4.6 has uncovered more than 500 open source vulnerabilities, raising new considerations for disclosure, triage, and patching at scale.

Research
/Security News
Malicious dYdX client packages were published to npm and PyPI after a maintainer compromise, enabling wallet credential theft and remote code execution.