React and Vue remotely call Demo to each other

  • React
  • Vue

What is a micro front end

It’s not easy to do front-end development well, but it’s even harder to scale it up so that multiple teams can simultaneously develop and maintain a large, complex product. To address this challenge, there is a growing trend in the front-end domain to break up large front-end projects into many small, easily managed, independently deployed applications and share application-level resources (UI components/tool functions/business modules), much like microservices in the back-end domain. In response to this trend, Micro Frontends has launched a Micro front-end concept on its website: (from: micro-frontends.org/)

What is EMP

  • EMP is a set of new micro-front-end framework based on Webpack5 and Module Federation (different from the previous micro-front-end based on dom isolation and iframe technology stack), but also a new way of development.
  • EMP is a microfront-end framework that can intercall any resource in a project (including but not limited to third-party dependencies, components, functions, image resources, and so on) across projects and frameworks.
  • EMP is the latest open source front-end framework of Web technology group in YY business. Before open source EMP, THE Web technology group of YY business Center opened source Flutter- UI and other star bursting projects.
  • EMP has been widely used in YY and applied To many Zhongtai projects as well as To C and To B projects.
  • EMP – CLI is the core of the EMP framework. For more details, see the EMP – CLI source code.

Microfront-end service discovery for EMP

EMP’s micro front-end service discovery allows local projects to call remote projects knowing the functions, components, specific function pass-throughs, component pass-throughs, and so on. Service discovery similar to back-end microservices.

The general structure is as follows:

For more details, see the Webpack Plugin emp-tune-dts-Plugin.

EMP’s micro front-end service discovery solves the problem that Module Federation can’t share types with remote projects in Typescript. , can be said to be the industry’s advanced solution to this problem.

How to start EMP project quickly

Use emp-cli init command:npx @efox/emp-cli init

Start the CLI init command and select the EMP template for the technology stack you want.

  • react
  • vue2
  • vue3
  • react-base
  • react-project
  • vue3-base
  • vue3-project

(Support will be extended to all major technology stacks)

React and Vue remote components call each other’s practices

practice

Create emP React and Vue projects respectively

React

Emp -cli, NPX @efox/emp-cli init, select the React template

Write a simple React components/SRC/components/Hello. New TSX

import React from 'react'
import './common.scss'
import './common.less'
import './common.css'
const Hello = ({title}: {title: string}) = > (
  <>
    <h1>{title}</h1>
  </>
)

export default Hello
Copy the code

Modify emp.config.js in the project (emp. Config. js is the emP project configuration file) :

  • Expose the React component for remote invocation
  • Introducing remote Vue components (Vue will be written below)
const path = require('path')
const packagePath = path.join(path.resolve('/'), 'package.json')
const {dependencies} = require(packagePath)
console.log(packagePath)

module.exports = ({config, env}) = > {
  const port = 8001
  const projectName = 'ReactComponents'
  const publicPath = `http://localhost:${port}/ `
  config.plugin('mf').tap(args= > {
    args[0] = {
      ...args[0],
      ...{
        // Project name
        name: projectName,
        // Expose the project's global variable name
        library: {type: 'var'.name: projectName},
        // The name of the file to be imported remotely
        filename: 'emp.js'.// Remote project alias: the name of the project imported remotely
        remotes: {
          '@emp/vueComponents': 'vueComponents',},// Something to expose
        exposes: {
          // Alias: the path of the component
          './configs/index': 'src/configs/index'.'./components/Hello': 'src/components/Hello',},// shared: ['react', 'react-dom'],
        shared: {... dependencies}, }, }return args
  })
  config.output.publicPath(publicPath)
  config.devServer.port(port)
  config.plugin('html').tap(args= > {
    args[0] = {
      ...args[0],
      ...{
        files: {
          js: ['http://localhost:8006/emp.js'],}}},return args
  })
}

Copy the code

The remote Vue component is introduced in/SRC /bootstrap. TSX, vuera is introduced, and VueInReact is used to wrap the remote Vue component

import * as React from 'react'
import * as ReactDOM from 'react-dom'

import Hello from 'src/components/Hello'
import Content from '@emp/vueComponents/Content.vue'
import {VueInReact} from 'vuera'

const VueComponent = VueInReact(Content)

ReactDOM.render(
  <>
    <Hello title="I am React Project" />
    <div style={{backgroundColor: '#eee', padding: '20px'}}>
      <VueComponent title="React use Remote Vue Component" />
    </div>
  </>,
  document.getElementById('emp-root'),
)
Copy the code

Start the projectyarn dev, you can see the components of this project and the Vue components that are referenced remotely

Vue (currently not supported with Vue3)

Using emp-cli, NPX @efox/emp-cli init, select the Vue2 template

Write a simple Vue components/SRC/components/content. Vue

<template>
  <div style="color: red">{{ title }}</div>
</template>

<script>
export default {
  name:'Content'.props: ['title'].data() {
    return{}; }};</script>
Copy the code

Modify emp.config.js in the project (emp. Config. js is the emP project configuration file) :

  • Expose the Vue component for remote invocation
  • Introduce the remote React component
const path = require('path')
const {VueLoaderPlugin} = require('vue-loader')
//
const ProjectRootPath = path.resolve('/')
// const packagePath = path.join(ProjectRootPath, 'package.json')
// const {dependencies} = require(packagePath)
//
const {getConfig} = require(path.join(ProjectRootPath, './src/config'))
//
module.exports = ({config, env, empEnv}) = > {
  const confEnv = env === 'production' ? 'prod' : 'dev'
  const conf = getConfig(empEnv || confEnv)
  console.log('config', conf)
  //
  const srcPath = path.resolve('./src')
  config.entry('index').clear().add(path.join(srcPath, 'main.js'))
  //
  config.resolve.alias.set('vue'.'@vue/runtime-dom')
  config.plugin('vue').use(VueLoaderPlugin, [])
  config.module
    .rule('vue')
    .test(/\.vue$/)
    .use('vue-loader')
    .loader('vue-loader')
  //
  const host = conf.host
  const port = conf.port
  const projectName = 'vueComponents'
  const publicPath = conf.publicPath
  config.output.publicPath(publicPath)
  config.devServer.port(port)
  //
  config.plugin('mf').tap(args= > {
    args[0] = {
      ...args[0],
      ...{
        name: projectName,
        library: {type: 'var'.name: projectName},
        filename: 'emp.js'.remotes: {
          ReactComponents: 'ReactComponents',},exposes: {
          './Content.vue': './src/components/Content',},/* shared: { ... dependencies, }, */}},return args
  })

  config.resolve.alias
  .set('vue$'.'vue/dist/vue.esm.js')
  .clear()

  //
  config.plugin('html').tap(args= > {
    args[0] = {
      ...args[0],
      ...{
        title: 'EMP Vue Components'.files: {
          js: ['http://localhost:8001/emp.js'],}}},return args
  })
}
Copy the code

/ SRC/app. vue introduces the remote React component, introduces vuera, and uses ReactInVue to wrap the remote React component

<template>
  <div>
    <Content title="I am Vue Project" />
    <hello-react title="Vue use Remote React Component" />
  </div>
</template>

<script>

import { ReactInVue } from "vuera";
import Content from "./components/Content";

import Vue from "vue";
const HelloReact = () = > ({
  // The component to load (should be a 'Promise' object)
  component: import('ReactComponents/components/Hello').then(res= >{
    return ReactInVue(res.default)
  }),
  // Display the component delay time when loading. The default is 200 (ms)
  delay: 0.// If a timeout is provided and the component loads time out,
  // Use the component used when the load failed. The default value is' Infinity '
  timeout: 3000
})

export default {
  name: "APP".components: {
    Content,
    "hello-react":HelloReact
  },
  data() {
    return {};
  },
  created(){}};</script>

<style scoped>
img {
  width: 200px;
}

h1 {
  font-family: Arial, Helvetica, sans-serif;
}
</style>
Copy the code

Start the projectyarn dev, you can see the components for this project and the React component that is remotely referenced

The principle of analytic

Compilation and distribution of remote components

EMP compiles the component into a separate closure based on the exposure field configuration of emp.config.js, and then packages the component separately into a JS, which is loaded on demand in the form of emp.js as a reference index.

The React Hello component compiles the following code:

(self["webpackChunk_empreactvue_react"] = self["webpackChunk_empreactvue_react"] || []).push([["src_components_Hello_tsx"] and {/ * * * / "./src/components/Hello.tsx":
/ *! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *! * \! *** ./src/components/Hello.tsx ***! A \ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
/ *! namespace exports */
/ *! export default [provided] [maybe used in ReactComponents (runtime-defined); used in index] [usage prevents renaming] */
/ *! other exports [not provided] [maybe used in ReactComponents (runtime-defined)] */
/ *! runtime requirements: __webpack_require__, __webpack_require__.n, __webpack_exports__, __webpack_require__.r, __webpack_require__.* */
/ * * * / (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/ *! react */ "webpack/sharing/consume/default/react/react");
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _common_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/ *! ./common.scss */ "./src/components/common.scss");
/* harmony import */ var _common_scss__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_common_scss__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _common_less__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/ *! ./common.less */ "./src/components/common.less");
/* harmony import */ var _common_less__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_common_less__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _common_css__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/ *! ./common.css */ "./src/components/common.css");
/* harmony import */ var _common_css__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_common_css__WEBPACK_IMPORTED_MODULE_3__);
var Hello = function Hello(_ref) {
  var title = _ref.title;
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null./*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h1".null, title));
};
/* harmony default export */ __webpack_exports__["default"] = (Hello);
/ * * * /}}));//# sourceMappingURL=data:application/json; charset=utf-8; base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9AZW1wcmVhY3R2dWUvcmVhY3QvLi9zcmMvY29tcG9uZW50cy9IZWxsby50c3giXSwib mFtZXMiOlsiSGVsbG8iLCJ0aXRsZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBOztBQUNBLElBQU1BL EtBQUssR0FBRyxTQUFSQSxLQUFRO0FBQUEsTUFBRUMsS0FBRixRQUFFQSxLQUFGO0FBQUEsc0JBQ1osdUlBQ0UsdUVBQUtBLEtBQUwsQ0FERixDQURZO0FBQ UEsQ0FBZDs7QUFNQSwrREFBZUQsS0FBZixFIiwiZmlsZSI6ImpzL3NyY19jb21wb25lbnRzX0hlbGxvX3RzeC5lMzAzYzRjNC5qcyIsInNvdXJjZXNDb250Z W50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCAnLi9jb21tb24uc2NzcydcbmltcG9ydCAnLi9jb21tb24ubGVzcydcbmltcG9ydCAnL i9jb21tb24uY3NzJ1xuY29uc3QgSGVsbG8gPSAoe3RpdGxlfToge3RpdGxlOiBzdHJpbmd9KSA9PiAoXG4gIDw+XG4gICAgPGgxPnt0aXRsZX08L2gxPlxuI CA8Lz5cbilcblxuZXhwb3J0IGRlZmF1bHQgSGVsbG9cbiJdLCJzb3VyY2VSb290IjoiIn0=
Copy the code

The components of other frameworks are compiled into the current framework when called remotely

Vuera is a library used to compile components from other frameworks at call time, helping us compile components from other frameworks into the current framework.

Compare with other micro front-end technologies

Compared with the micro-front-end framework based on DOM isolation, qiankun was taken as an example

  • State. The micro front end developed by Qiankun could not isolate the base station project and sub-project excessively, resulting in inconsistent context, and shared state had to be transmitted through the bus, which was very troublesome. EMP makes state sharing convenient by invoking remote state management.
  • Call implementation across frameworks. With DOM isolation, cross-framework implementations are easy, but they cannot call each other, and the granularity can only be rendered in the specified DOM area. The EMP implementation’s cross-framework call granularity is down to function and is easy to use.
  • Volume. Because Qiankun was realized through DOM isolation, dependency sharing was not perfect and needed to rely on SystemJS. In addition, sharing was not convenient, resulting in possible repetition of dependency and larger volume. EMP implements dependency sharing through Module Federation so that dependencies do not repeat themselves (they become global variables and only one of the same dependencies is left), so they are smaller.

Compared to iframe-based microfrontend

  • State. The micro front end of iframe, with no real state management, communicates via postMessage.
  • Invoke aspects across frameworks. The micro front end of iframe cannot be invoked across frameworks.
  • Volume. The iframe microfront does not share dependencies.

conclusion

  • EMP is the latest open source micro front-end framework of YY Business Taiwan Web Technology group. At present, it has been widely used in the company, which has greatly improved the status management, mutual call and volume optimization of multiple projects.
  • Cross-framework remote calls are convenient. It’s so easy to call each other across frameworks, let alone the technology stack.
  • To improve multi-project collaboration is a new way of development.