关于前端:门户可视化搭建与Low-Code实践

38次阅读

共计 7928 个字符,预计需要花费 20 分钟才能阅读完成。

我的项目背景

偏中后盾管理系统或者集成平台的利用入口,常常须要在利用入口进行 个性化的展现。从用户角度来看,反对个性化的定制,能够无效晋升用户的幸福感,晋升应用或工作效率;从产品提供商来看,提供个性化定制服务的能力,也是晋升产品力的一种形式。

如何提供个性化定制能力

  • 定制开发(也就是始终被开发者诟病的二次开发)

    • 优:疾速响应共性需要
    • 劣:

      • 开发成本高,阐明本身产品力无限
      • 源码管控上的危险
  • 凋谢配置项

    • 优:无需二次开发,节约老本
    • 劣:

      • 配置项难形象,粒度难把握,常常有配置项无奈笼罩需要的中央
      • Do everything then do nothing.

解决方案

有没有一种形式,既能疾速响应共性需要,也能管制开发成本?这里抛出两个词儿:可视化搭建Low Code 低代码

  • 可视化搭建:通过在指定配置平台,进行利落拽页面模块,过程可视化,输入个性化页面或模块。
  • Low Code 低代码:开发人员通过 大量编码 或 No Code,即可疾速满足共性需要,并将 coding 成绩物利用于配置中。

其实这两个概念比不是陈腐词,远在 Dreamwaver 时代就有影子(返祖现象?),且 Low Code 计划很难来到可视化搭建,相辅相成。在门户“千人千面”的需要中,就采纳了以上的计划。

指标受众

这套计划给谁用?职责怎么划分?

可视化搭建 Coding 开发
受众 技术支持 / 项目经理 /… 基线开发人员 / 我的项目开发人员
职责 利落拽页面 / 个性化配置 疾速模块开发 / 定制化开发

门户实际计划

在实际落地中,门户采纳的是:将整个页面拆分成各个模块(部件),就像积木搭建一样,可通过不同的排列组合、布局形式,搭建个性化页面。

部件 widget – 可视化搭建最小单元

部件 (widget) 其实就是在这套计划里的“积木”,可灵便摆放,也可共性开发。

当咱们去思考如何拆分整个页面的时候,咱们天然会发现,与前端 组件化 的思维不约而同。任意一个拆分后的模块单元,都有本人的 视图 HTML,交互逻辑 JavaScript,款式 CSS
一个最根本的部件,就是由 HTML/Javascript/CSS 组成,再加上一些动态资源(如图片 / 语言文件 /…)。

当然,咱们心愿这个部件有一下特点:

  • 独立开发:开发者能够在宿主环境(门户)以外进行 Coding 开发
  • 独立部署:即能够在任意中央部署,只有能通过地址拜访到该部件资源,即可加载
  • 动静加载:用到的时候加载,不必不加载。

如何实现

古代的前端框架(React/Vue/Angular)基本上能做到组件化思维开发,但现实的计划是 framework free,即不受框架束缚。
出于开发成本思考,联合外部前端对立技术栈,门户的部件采纳的是 Vue.js 动静组件 的计划。即一个部件,就是一个 SFC 单文件组件(Single File Component),通过 Webpack 模块化打包工具,将部件源码打包,输入一个资源包。
在宿主环境,通过接口获取部件资源地址,动静解析和执行字符串,再通过 Vue 动静组件的形式进行动静加载(<component>is attribute)。

<component v-bind:is="widgetActiveComponent"></component>

可视化搭建布局设计

目前门户采纳 xy 坐标系,作为前端页面布局的规定。
如图所示,部件容器左上角的顶点坐标作为该部件定位点。

PS:这里的布局设计其实咱们思考过 栅格布局 ,但各有优劣。坐标布局颗粒度更细,实用于个性化更强的场景。但栅格布局搭建老本较小,效率较高。最终咱们通过加上“ 网格吸附 ”的性能来 优化坐标布局的体验 这价值一个亿的代码有须要我能够无偿提供。

计划整体流程

如下图所示,从页面模块形象,通过打包构建部署,最终加载渲染,造成闭环。:)

小结

通过部件利落拽配置(可视化搭建 )和Low Code 低代码开发(利用提供的脚手架疾速开发部件,可独立开发,独立部署,动静加载),实现门户的个性化需要。

技术积淀 – 可视化搭建

这块在门户中的实现不在难,在繁琐。列几个可能会踩坑的点。

1. 画布内利落拽 drag

这里的难点,除了要思考到原生 onmousedown/onmousemove/onmouseup 等鼠标事件,阻止事件冒泡,以及一些边界问题,特地是鼠标在不同画布 缩放比例 的状况下的 漂移景象。上面的伪代码中,都会提到。

// 伪代码
<div class="dragItemBox">
  <div class="dragCover" @mousemove="drag($event)" @click.stop></div>
</div>

<script>
  drag(e) {
        e.onmousedown = e => {
      // 记录鼠标按下时的坐标
        document.onmousemove = e => {
        // 记录鼠标挪动时的 xy 坐标差
        // 重点来了,这里差值要思考画布的缩放比例,不然在不同的缩放比例下,会造成鼠标漂移哦
        // 边界问题,这里能够束缚拖拽的范畴
          document.onmouseup = e => {// 将坐标差赋给指标元素,扭转其布局款式}
      }
      document.onmouseup = e => {// 按下不挪动鼠标,对事件状态进行重置}
    }
    }
</script>

2. 画布内利落拽 resize

以及对部件进行 resize 的时候,每一个锚点(共 8 个锚点)的逻辑都须要严格形象。举个栗子就懂了。

部件有 width/height/top/left 这四个布局属性,先看锚点 1 和锚点 8,当拖拽 锚点 8 的时候,扭转的只是 width/height 两个属性,而当拖拽 锚点 1 的时候,扭转的却是 width/height/top/left 四个属性,所以每个锚点的逻辑是不同的,怎么形象,怎么封装,看本人施展,这里要特地留心,不然会呈现各种莫名其妙的 漂移问题。

3. 纯 CSS 实现虚点线网格

不是虚线,也不是点线,而是 虚点线 。这名字我本人想的,看下图,在纵横线的 交叉点 会比其余中央要大一点!
看到这个视觉稿,差点掏出了桌子下 40 米的砍刀。这里不是作死,为啥不必图片?次要是思考到这里的方格大小和色彩可调节,所以应该只能用纯 CSS 实现,吧。
感兴趣能够先本人尝试实现下。这里的代码,真的,没 500 块我不卖 :(

波及到的 CSS API:

  • linear-gradient()):线性突变
  • radial-gradient()):径向突变
  • background-size 属性规定背景图像的尺寸。
  • background-position 属性设置背景图像的起始地位。

实现合成

  • Step.1 条纹
  • Step.2 实线网格
  • Step.3 虚线网格
  • Step.4 虚点线网格

如下为下文图例的 DOM 构造及前置款式。

<style>
.bg {
  width: 100px;
  height: 100px;
  background-color: black;
}
.basegrid {
  width: 100%;
  height: 100%;
}
</style>
<div class="bg">
  <div class="grid basegrid"></div>
</div>
  1. Step.1 先利用 linear-gradient() 画出条纹。重点:边界显著(失常状况下 linear-gradient 是会产生突变成果的,但这里的条纹须要边界显著,那么就须要前一个渐变色在后一个渐变色地位之后,具体能够参考这篇文章)

  1. Step.2 网格能够了解为横竖条纹组合而成。重点:background-image 能够设置多个突变叠加,横竖条纹能够各自实现后叠加。

网格线的粗细问题:能够管制 background-sizelinear-gradient渐变色的差值,来管制网格线的粗细。

  1. Step.3 虚线的实现能够持续叠加与背景色统一的条纹来叠加。能够用两个突变叠加(横竖条纹)。


这里我想到的是能够用斜条纹来实现虚线,能够节约一个突变叠加。

  1. Step.4 虚线网格曾经实现了,那么 虚点线 网格怎么搞?拆分!拆分为 点阵 虚线网格 ,使其两者重合。点阵就用到了radial-gradient()) 径向突变。

两者叠加,Finally !

Low Code 实际

上面会介绍 low code 及微前端的相干内容,过程中会插入门户部件的落地计划(题目前带 *)。从后果来看,low-code 低代码 是后果,条条小道通罗马,但总离不开一些关键点,如上述提到的可视化搭建、模块隔离等;而微前端在模块隔离方面,提供了一些很多值得借鉴的计划。相辅相成,技术的诞生就是为了解决 具体问题

Low-Code 介绍

A low-code development platform (LCDP) is software that provides a development environment used to create application software through graphical user interfaces and configuration instead of traditional hand-coded computer programming. A low-code model enables developers of varied experience levels to create applications using a visual user interface in combination with model-driven logic.

一句话概括就是:实现低代码平台的要害因素是 模型驱动设计 代码主动生成 可视化编程

场景

从最早的通过模块化搭建解决营销流动畛域的问题,倒退到当初,能够通过 low-code 来解决外部简单的中后盾业务需要,既实用于面向 C 侧用户的产品经营,也贴合 B 侧用户的数据管理需要。

外围能力

  • 根底物料的搭建和接入

    • 定制化组件接入
    • 自定义组件
  • 编排能力:页面排版 / 逻辑编排

    • 实时可视化
    • 多端适配
    • 多场景适配
  • Pro-code 和 low-code 的代码转换能力

    • 对输入的编程产物进行二次开发的能力
  • 合作能力
  • 数据分析能力

二八准则

  • 通过 low-code 平台实现对 80% 业务场景的笼罩
  • 20% 的能力通过 pro-code 自定义实现

与微前端的交加

为什么会提到微前端?因为在微前端架构中,前端利用能够 独立运行、独立开发、独立部署 。与门户的部件计划 不约而同,当然,咱们在模块隔离的计划中,也参考了 Single-SPA 和 qiankun 等微前端框架。

微前端计划

微前端架构是一种相似于微服务的架构,它将微服务的理念利用于浏览器端,行将 Web 利用由繁多的单体利用转变为多个小型前端利用聚合为一的利用。

形式 开发成本 保护老本 可行性 同一框架要求 实现难度
路由散发
iFrame
利用微服务化 ★★★★
微件化 ★★★★★
微利用化 ★★★
纯 Web Components ★★
联合 Web Components ★★

微件化 – 组合式集成

在前端组件(或利用)集成计划中,有以下几种形式:

  1. 独立构建组件和利用,生成 chunk 文件,在构建后应用脚本合并。
  2. 开发时独立开发组件或利用,集成时合并组件和利用,最初生成单体的利用。
  3. 在运行时,加载利用的 Runtime,随后加载对应的利用代码和模板。

* 门户部件组合集成计划

门户的部件集成计划相似形式二和三,基于 脚手架工具 独立开发部件,打包构建后输入动态成绩物,在宿主环境能够依据动态资源门路,动静加载 Vue 组件。如果把首页了解成一个利用,那么通过首页主题包整个导出,将部件成绩物集成在整个主题中,就和上述提到的形式二很靠近了。

模块隔离

多个模块(部件)在同一个页面集成,就必须要思考到模块隔离,包含子模块与宿主环境的隔离,以及子模块之间的隔离。

可能导致的问题

  • 子模块可能会批改宿主环境的逻辑,从而影响整个零碎的运行,xss 平安问题;
  • 子模块之间能够互相拜访且批改,难以定位问题;
  • 模块间的款式净化;

利用集成

在微前端的利用集成中,如果要做到利用的独立部署及与技术栈解耦,就不得不思考子利用的 运行时加载

App Entry 阐明 长处 毛病
JS Entry 子利用将资源打成一个 entry script,子利用的所有资源打包到一个 js bundler 里。 主利用和子利用独特构建,不便做 bundler 优化。 1. 子利用更新会造成整个主利用更新,更新老本较高 2. 子利用的动态资源须要打成 bundler,加载效率较低
HTML Entry 将子利用打进去 HTML 作为入口,主框架能够通过 fetch html 的形式获取子利用的动态资源,同时将 HTML document 作为子节点塞到主框架的容器中。 子利用独立开发,独立部署公布; 1. 网络申请开销较大 2. bundler 构建优化难度较大,如公共依赖抽取

js 隔离

JavaScript 沙箱
沙箱机制的外围是让部分的 JavaScript 运行时,对外部对象的拜访和批改处在可控的范畴内,即无论外部怎么运行,都不会影响内部的对象。通常在 Node.js 端能够采纳 `vm` 模块,而对于浏览器,则须要联合 `with` 关键字和 `window.Proxy` 对象来实现浏览器端的沙箱。以下为繁难的实现:
function sandbox(code) {code = 'with (sandbox) {' + code + '}';
    const fn = new Function('sandbox', code);
    return (sandbox) => {
        const proxy = new Proxy(sandbox, {has(target, key) {return true;},
            get(target, key, receiver) {if (key === Symbol.unscopables) {return undefined;}
            }
        });
        return fn(proxy);
    }
}
let str = 'let a = 10;console.log(a)'
sandbox(str)({})
Web Worker

具体内容可参考文章 浅探 Web Worker 与 JavaScript 沙箱

Web Worker 子线程的模式也是一种人造的沙箱隔离,借鉴了 Browser-VM 思路,在编译阶段通过 Webpack 插件为每个子利用包裹一层创立 Worker 对象的代码,让子利用运行在其对应的单个 Worker 实例中,比方:

__WRAP_WORKER__(`/* 打包代码 */}`);

function __WRAP_WORKER__(appCode) {var blob = new Blob([appCode]);
    var appWorker = new Worker(window.URL.createObjectURL(blob));
} 

但有几个缺点:

  • 不反对 DOM 操作,必须通过 postMessage 告诉 UI 主线程来实现
  • 无法访问 windowdocument 之类的浏览器全局对象
  • 无法访问页面全局变量和函数、无奈调用 alertconfirmBOM API

Module Federation – Webpack 5

webpack5 – Module Federation 中文文档 中是这么定义的:多个独立的构建能够造成一个应用程序。这些独立的构建不会相互依赖,因而能够独自开发和部署它们。这通常被称为微前端,但并不仅限于此。

事实上,MF 基于模块,实质上就是 JS 代码片段,也就是 chunk。为了解决依赖问题,webpack5 的实现形式是重写了加载 chunk 的 webpack_require.e,从而前置加载依赖;为了解决 modules 的共享问题,应用了全局变量来 hook。但有利有弊:

  • 长处:做到运行时加载,shared 代码无需本人手动打包构建
  • 毛病:可能会对页面运行时的性能造成影响;须要思考模块的版本控制;旧我的项目革新老本较大;

css 隔离

当主利用和子利用同屏渲染时,就可能会有一些款式会互相净化,如果要彻底隔离 CSS 净化,能够采纳 **CSS Module** 或者 ** 命名空间 ** 的形式,给每个子利用模块以特定前缀,即可保障不会相互烦扰,能够采纳 webpack 的 postcss 插件,在打包时 ** 增加特定的前缀 **。而对于子利用与子利用之间的 CSS 隔离,采纳 ** 动静加载 **,即在每次子利用加载时,将该利用所有的 `link` 和 `style` 内容进行标记,在利用卸载后,同步卸载页面上对应的 `link` 和 `style` 即可。另一种举荐的形式是 **shadow DOM**,是能够彻底隔离 css 的一个思路。可将子利用包裹在 Shadow DOM 节点中,只须要将 [shadowRoot](https://developer.mozilla.org/zh-CN/docs/Web/API/ShadowRoot)的 API 设置为 true。但 ** 存在一些问题 **,比方 Shadow DOM 的 css 隔离、dom 事件冒泡终止在 Shadow DOM 父节点的问题,会导致子利用外部在调用一些 UI 组件库的时候显示异样。

* 门户部件模块隔离计划

简略形容就是利用 vue 组件的隔离,包含款式 scoped、组件实例隔离,算不上特地齐备的思考。

因为门户将 UI 组件库、vue.js 等公共依赖从部件脚手架中排除(webpack externals),因而所有的部件都用同一套 UI 组件库,一方面为了 管制开发成本和标准 ,另一方面也是尽可能的将部件的 逻辑简单化 ,每个部件的 逻辑性能尽可能繁多

但子模块领有宿主环境依赖的援用权限,并不合乎隔离要求。除此之外,子模块和宿主环境共享一个 window 对象,能施展的空间就更大了,但之所以凋谢,是因为真实情况下的确有这样的需要,比方子模块须要拜访宿主环境的变量(门户登录的 userName?等)。咱们尽可能将数据通过动静组件的 prop 来传递通信 <component :prop="widget.option" ... />,尽可能通过标准、lint 等伎俩去限度部件外部对宿主的拜访。

在 CSS 款式方面就绝对简略,利用 vue 组件 <style scoped></style>,给该组件生成一个惟一 data 值。如果想要扭转宿主 UI 组件库的款式,能够利用::v-deep 款式穿透 来设置,这样不会影响其余模块的 UI 组件款式。

模块通信

对于拆散的模块来说,音讯订阅(pub/sub)模式的通信机制是十分实用的,在基座利用中定义事件核心 Event,每个微利用别离来注册事件,当被触发事件时再由事件核心对立散发,这就形成了根本的通信机制。

能够引入基于 Flux 的状态治理机,Redux/Vuex,使每个子利用保护本人的 store。

qiankun 利用间通信计划

qiankun 官网提供的利用间通信形式 – Actions 通信,如下图所示,先注册观察者到 观察者池 中,而后通过批改 globalState 能够触发所有的观察者函数,从而达到组件间通信的成果。

* 门户部件通信

门户的部件基于 Vue.js 组件,因而通信机制也依赖于 Vue 组件本人的通信机制,即利用宿主环境 this.$root.$widgetEventBus 空 Vue 实例作为音讯总线,部件之间通过 $emit/$on 进行事件发送及监听。

总结

上述介绍了门户 portal 利用在可视化搭建及 Low Code 方面的实际,也介绍了社区中微前端的相干内容,站在伟人的肩膀上,会看到目前门户基于 Vue 组件实现的部件化仍然存在许多可改善的中央,如部件隔离、集成能力、运行时加载性能等。当然,所有计划的抉择须要结合实际场景和理论需要,综合思考,均衡利弊,没有银弹!


【参考】

  • Low-code_development_platform
  • 「可视化搭建零碎」——从设计到架构,摸索前端畛域技术和业务价值
  • 2020 年大前端技术趋势解读
  • low-code 与 20 年前的 Dreamweaver 有什么区别?
  • 施行前端微服务化的六七种形式 – phodal
  • 浅探 Web Worker 与 JavaScript 沙箱
  • 可能是你见过最欠缺的微前端解决方案​

正文完
 0