在软件开发的早期,前端和后端代码是由同一团队使用相同的运行时环境和部署过程来维护的,如今,我们将这些代码称为整体应用程序。
随着系统变得越来越复杂,我们开始将代码分成两个小组,分别由两个具有专门技能的团队维护:前端和后端,后端团队可以专注于构建弹性和高可用性的系统,而前端团队可以专注于浏览器的兼容性, UI
设计和UX
。但这还不够,随着复杂性的增长,我们开始将后端的业务逻辑拆分为更多可维护的服务,这就是微服务的出现。
微服务提高了团队所有权,使团队可以专注于特定的业务领域,无论其他服务的构建方式如何,都可以选择最佳的技术堆栈来解决问题,微前端将微服务的概念扩展到了前端世界。
Micro-frontends
背后的想法是将网站或Web
应用程序视为独立团队拥有的功能的组合。每个团队都有自己关心和专长的特定业务或任务领域。一个团队具有跨职能,并从数据库到用户界面端到端地开发功能。
我们有两个微前端,第一个负责使用React.js渲染Navbar,第二个负责使用Vue.js渲染产品列表。
两者都是同构Web应用程序,因此视图是在服务器端呈现的,并在客户端进行了水合处理,以使它们在客户端具有动态性。
此仓库中提供了代码示例的代码。
https://github.com/marconi1992/hypernova-micro-frontends
该项目包含三个文件夹:
hypernova-server-vue
:包含使用Vue.js构建的产品列表组件。hypernova-server-react
:包含使用React.js构建的Navbar组件hypernova-aggregator
:向提供数据的超新星服务器请求视图,并在HTML文档中组成所有视图。我们在内部有 hypernova-server-vue
一个超新星服务器的入口点 src/index.js
您可以使用yarn dev
或运行它 npm run dev
index.js
import hypernova from 'hypernova/server'
import {
renderVue, Vue } from 'hypernova-vue'
import express from 'express'
import path from 'path'
import ProductList from './components/ProductList.vue'
hypernova({
devMode: true,
getComponent (name, context) {
if (name === 'ProductList') {
return renderVue(name, Vue.extend(ProductList))
}
},
port: 3030,
createApplication () {
const app = express()
app.use(express.static(path.join(process.cwd(), 'dist')))
return app
}
})
在 getComponent
提供正确的成分与它的装饰 renderVue
功能 hypernova-vue
ProductList.vue
<template>
<div class="k-product-list">
<h2 class="k-product-list__header">{
{
title}}</h2>
<ul>
<li v-for="(item, idx) in items" :key="idx" class="k-product-item" @click="select(item)">
<img :src="item.imageUrl" class="k-product-item__image">
<h4 class="k-product-item__title">{
{
item.title }}</h4>
</li>
</ul>
<button @click="addItem">Add Item</button>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
},
items: {
type: Array,
default: () => []
}
},
methods: {
addItem() {
this.items.push({
title: `Product ${
this.items.length + 1}`, imageUrl: 'https://via.placeholder.com/400' })
},
select(item) {
const event = new CustomEvent('itemSelected', {
detail: item });
document.dispatchEvent(event)
}
}
}
</script>
这是一个简单的vue
组件,可接收诸如之类的道具title
,items
但是如您所见,它具有一个名为的方法select
,该方法可创建CustomEvent
并使用文档引用进行分派。通信微前端的机制与微服务非常相似,只是微前端使用DOM API
作为发布/订阅。
client.js
import {
renderVue, Vue } from 'hypernova-vue'
import ProductList from './components/ProductList.vue'
renderVue('ProductList', Vue.extend(ProductList))()
该client.js
脚本是我们客户端水合的入口点。
我们具有hypernova-server-react
与hypernova-server-vue
相似的设置
您可以使用yarn dev
或运行它npm run dev
index.js
import express from 'express';
import path from 'path';
import hypernova from 'hypernova/server';
import {
renderReact } from 'hypernova-react';
import Header from './components/Header';
hypernova({
devMode: true,
getComponent(name) {
if (name === 'Header') {
return renderReact(name, Header);
}
return null;
},
port: 3031,
createApplication() {
const app = express();
app.use(express.static(path.join(process.cwd(), 'dist')));
return app;
},
});
如您所见,入口点与入口点非常相似,但是它使用renderReact
了hypernova-react
代替功能。
Header.jsx
import React from 'react';
import PropTypes from 'prop-types';
import NavBar from './NavBar';
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
itemsSelected: 0,
item: null,
};
this.itemSelected = this.itemSelected.bind(this);
}
componentDidMount() {
document.addEventListener('itemSelected', this.itemSelected);
}
componentWillUnmount() {
document.removeEventListener('itemSelected', this.itemSelected);
}
itemSelected({
detail: item }) {
const {
itemsSelected } = this.state;
this.setState({
itemsSelected: itemsSelected + 1, item });
}
render() {
const {
title, links } = this.props;
const {
itemsSelected, item } = this.state;
return (
<header className="k-header">
<div className="k-header__brand">{
title}</div>
{
item && <span className="k-header__last-item">{
`Last Item: ${
item.title}`}</span> }
<span className="k-header__space" />
<span>{
`Items Clicked: ${
itemsSelected}`}</span>
<NavBar links={
links} />
</header>
);
}
}
Header.propTypes = {
title: PropTypes.string.isRequired,
links: NavBar.propTypes.links,
};
Header.defaultProps = {
links: [],
};
export default Header;
的Header.jsx
部件订阅该itemSelected
事件componentDidMount
的方法,因此,每一个时间itemSelected
事件被调度,则增加itemsSelected
的状态变量,并存储选择的最后一项。
client.js
import {
renderReact } from 'hypernova-react';
import Header from './components/Header';
renderReact('Header', Header)();
聚合器是一个基本的快速应用程序,它使用axios向超新星服务器请求视图,并使用插值法将结果HTML放入模板中,并从每个微前端添加必要的客户端脚本。
您可以使用yarn start
或运行它npm run start
index.js
const express = require('express')
const path = require('path')
const {
getContent } = require('./services/content')
const {
getLayout } = require('./services/layout')
const app = express()
app.use(express.static(path.join(__dirname, 'public')))
app.get('/', async (req, res) => {
const promises = [getContent(), getLayout()]
const [content, {
header }] = await Promise.all(promises)
const html = `
Document
${
header.html}
${
content.html}
`
return res.send(html)
})
app.listen(8080, () => console.log('Aggregator Running'))
最后一步是在浏览器中打开http://localhost:8080/
,您将看到呈现的页面,您可以通过查看页面源代码来确认是服务器端呈现的。
正如您已经注意到的,使用超新星可以实现微前端的主要设计原则,我们为每个微前端使用超新星服务器来推广团队所有权和技术不可知论原则 ,以便呈现与某个业务领域或功能相关的组件。此外,我们推广具有同构Web应用程序的弹性站点原理,即使javascript脚本失败,该应用程序也将可用,最后,我们使用DOM事件推广本机浏览器功能以传达我们的微前端
参考资料:https://medium.com/js-dojo/micro-frontends-using-vue-js-react-js-and-hypernova-af606a774602