1

前端公共模块替换

 2 years ago
source link: https://segmentfault.com/a/1190000040873491
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

公司中后台管理系统经历几年的迭代维护,其公共模块部分(HeaderSlider)的主题与现在UI风格不一致,需要更换。但是,该项目几年前的老项目了,技术栈在逐步的演进,初始是Freemaker引擎搭建的前后端未分离项目,后面部分旧页面采用React重构,新页面采用React开发,使用iframe标签嵌套React页面,再后来引入了微前端的概念,使用single-spa-react改造项目中的React页面。

目前,采用这三种技术栈的页面都存在,需要全部更换。

Freemarker

卖家中心中后台项目,每个页面都有相同的部分,比如顶部、导航、底部,如果每个页面都写一遍,等到项目庞大时,某天需要修改这些公共模块了,你需要将所有页面都改一遍,这真的很崩溃。但是sitemesh会让你轻松应对!

什么是sitemesh

SiteMesh is a lightweight and flexible Java web application framework that applies the Gang of Four decorator pattern to allow a clean separation of content from presentation.

SiteMesh 是一个网页布局和修饰的框架,利用它可以将网页的内容和页面结构分离,以达到页面结构共享的目的。

Sitemesh是由一个基于Web页面布局、装饰以及与现存Web应用整合的框架。它能帮助我们在由大
量页面构成的项目中创建一致的页面布局和外观,如一致的导航条,一致的banner,一致的版权,等等。
它不仅仅能处理动态的内容,如jsp,php,asp等产生的内容,它也能处理静态的内容,如htm的内容,
使得它的内容也符合你的页面结构的要求。甚至于它能将HTML文件象include那样将该文件作为一个面板
的形式嵌入到别的文件中去。所有的这些,都是GOFDecorator模式的最生动的实现。尽管它是由java语言来实现的,但它能与其他Web应用很好地集成。

image-20211027140755232.png

sitemesh如何工作?

SiteMesh 充当 Servlet 过滤器,拦截返回到 Web 浏览器的HTML,提取相关内容并将其合并到称为装饰器的模板中。过滤器将任何 html、jsp 或其他 Web 框架页面的内容放入称为装饰器的预定义模板中

image-20211027140538325.png

sitemesh在卖家中心中的应用

在公司卖家中心的中后台项目中,我们将sitemesh拦截器与Application拆成独立的服务,分别对应着seller-center拦截服务和agentBuy应用服务,因为使用sitemeshweb容器可能有多个,将sitemesh独立拆出来可以复用。因此,架构图变成如下形式:

image-20211027142027697.png

卖家中心的执行流转时序图如下:

卖家中心执行流程 (1).png

(1)webagent网关服务中的路由配置如下,/agentBuy/seller/*请求会经过seller-center拦截,而seller-center服务中没有/agentBuy/seller/*请求的controller层,会继续匹配网关路由,走到agentBuy服务中

image-20211027144957601.png

image-20211027145018176.png

(2)agentBuy服务中有/agentBuy/seller/*请求的controller层,返回被装饰的内容区域html页面

(3)seller-center拦截返回到web浏览器的html,提取其中相关内容(headbody)并将其合到装饰页面模板中(<sitemesh:write property='head' /><sitemesh:write property='body' />

carbon.png

公共模块是怎么注入的?

在上面模板尾部中有注入以下脚本:

<script src="https://mstatic.cassmall.com/www/${profiles}/seller-react-frame/seller-react-frame.js"></script>

翻阅代码发现页面中的Header、Slider部分并不是在上面模板中实现的,而是通过script引入seller-react-frame.js,并执行脚本将Header DOM、Slider DOM分别注入到<div id="seller-topbar"></div><div id="seller-sidebar" class="menus"></div>

seller-react-frame.js是在seller-react-frameReact)项目中生成的,该项目中实现了HeaderSlider组件并指定挂载的元素,构建项目会生成jscss资源文件。构建完后再执行一个脚本,该脚本会写入一个自执行函数到seller-react-frame.js文件,自执行函数的作用是将asset-manifest.json资源清单中的jscss动态注入的页面中

// seller-react-frame项目
// 1、构建命令
"build": "cross-env PUBLIC_URL=./ react-app-rewired build && node ./scripts/build-entry-js.js",

// 2、入口文件
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import { TopBar, SideBar } from "@casstime/seller-components";
import "@casstime/seller-components/style.scss";

ReactDOM.render(<TopBar />, document.getElementById("seller-topbar"));
ReactDOM.render(<SideBar />, document.getElementById("seller-sidebar"));

// 3、执行build-entry-js.js生成seller-react-frame.js
const fs = require("fs");

const assetManifest = require("../build/asset-manifest.json");
const entries = assetManifest.entrypoints;

fs.writeFileSync(
  "./build/seller-react-frame.js",
  `;(function loadAssets() {
  var scripts = Array.from(document.getElementsByTagName('script'));
  var targetScripts = scripts.filter(function(item) {return item.src.indexOf('seller-react-frame.js') > -1});
  var prePath = '';
  if (targetScripts.length) {
    var targetScript = targetScripts[0];
    prePath = targetScript.src.slice(0, targetScript.src.indexOf('seller-react-frame.js'));
  }
  ${JSON.stringify(entries)}.forEach(function(asset) {
    if (/\\.css$/.test(asset)) {
      var head = document.getElementsByTagName("head")[0];
      var link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = prePath + asset;
      head.appendChild(link);
    }
    if (/\\.js$/.test(asset)) {
      var body = document.getElementsByTagName("body")[0];
      var script = document.createElement("script");
      script.src = prePath + asset;
      body.appendChild(script);
    }
  });
})();
`
);

因此,如果我们需要调整公共模块,只需要修改HeaderSlider组件,并重新构建、部署seller-react-frame。如果遇到缓存问题拉取的不是最新seller-react-frame.js,则需要刷一下CDN

Freemarker + iframe

Freemarker + iframe跟纯使用`Freemarker唯一的不同点就是内容变化的区域只有一个架子,里面使用iframe页面嵌套React页面,比如:

// 内容变化区域 ftl
<!DOCTYPE html>
<html lang="en">
<head>
    <#include "../includes/head_storemgr.ftl" >
    <#assign userLoginId = User.getUserId()>
    <link rel="stylesheet" href="${Global.getConfig("web.app.static.url")}/css/storeResolveManage.css" type="text/css">
</head>
<body id="ng-app">
        <div class="right" style="margin-top: 16px;margin-right: 16px;border: 10px solid #FFF;box-shadow: 0px 2px 8px #e4e4e4;background: #f1f1f1">
            <iframe id="iframeCon" name="iframeCon" src="/seller#/decode/decode-page" scrolling="no" allowtransparency="yes" marginwidth="0" marginheight="0" frameborder="no" border="0" onload="loadFrame();" style="width: 100%; height: 700px;"></iframe>
        </div>
    <script type="text/javascript" src="${Global.getConfig("web.app.static.url")}/js/storeResolveManage.js"></script>
</body>
</html>

其他公共模块还是由seller-react-frame.js脚本注入的。因此,调整公共模块还是需要修改HeaderSlider组件,并重新构建、部署seller-react-frame

microfe

公司微前端项目稍微有点特殊,基座项目(microfe-seller-base)中并没有包含公共模块,而是将公共模块也抽离成子应用。基座项目根据路由首先加载渲染公共子应用(microfe-seller-common),然后再加载渲染其他子应用,但是如何控制加载渲染子应用的顺序呢?毕竟其他子应用是挂载在公共子应用里面的。

image-20211027173420066.png

因此,调整公共模块只需要修改公共子应用(microfe-seller-common)工程,然后重新构建、部署。

https://docs.huihoo.com/sitem...

https://en.wikipedia.org/wiki...

http://www.blogjava.net/over1...

https://zhuanlan.zhihu.com/p/...

https://blog.csdn.net/xuanwug...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK