7

vivo 商品中台的可视化微前端实践

 2 years ago
source link: https://segmentfault.com/a/1190000041623387
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

在电商领域内,商品是一个重要组成部分,与其对应的商品管理系统,则负责商品的新建、编辑、复制等功能。随着商品管理系统的成熟稳定和业务上的扩展需求,催化出了商品中台的诞生。它可以将现有商品功能最大效率的复用在很多业务上(公司内业务、公司外业务等)。而不是仅限于当前团队的业务使用。

在设计商品中台的前端系统时,我们使用了微前端和可视化技术,其可以达到如下效果:

  • 可视化技术可以让各个业务方的运营等相关人员,直观的看到其配置的数据在页面上的展示效果;
  • 微前端可以帮助商品中台更快更好的适配到各个业务方的项目中。

至此,本篇文章的背景介绍完毕,下面将会阐述如何在商品中台前端系统中做微前端和可视化。

二、可视化技术

目前商品中台的页面如下图所示:

图片

图中左侧内容,就是商品可视化,它的核心能力如下:

  • 图中右侧所有的变动,都能在左侧得到实时更新和展示,如主图、 sku 组合、价格、图文详情、商品参数等功能。
  • 图中左侧的可视化区域是一个标准的 h5 页面,可以把它看成一个子页面,它与外层的父页面在 ui 上是完全隔离的,同时在数据上又是共享的。

下面我将会对可视化技术原理做完整阐述,请大家继续往下看。

三、可视化技术原理

可视化整体技术原理图如下:

图片

从上图可以获得以下信息:

  • 子窗口用 iframe 展示;
  • 子窗口用 vuex 做状态管理;
  • 子窗口和父窗口通过共享状态 ( vue store )来完成数据通信。

看到这,小伙伴可能会有以下疑问:

iframe 和父窗口的数据通信是通过 postMessage 完成的,这里为什么不使用 postMessage 呢?

使用 vuex 完成 iframe 数据通信是如何实现的呢?

回答第一个疑问:为什么不使用 postMessage

实践过的小伙伴应该深有体会,使用 postMessage 的话,数据通信架构基本如下图所示:

图片

结合上图可以分析出 postMessage 有以下缺点:

  • 父窗口含有大量逻辑:父窗口需要将 vuex 的数据进行处理,然后通过 postMessage 进行传输;
  • 数据通信方式不纯粹:vuex 和 postMessage 组合在一起,互相转换,使数据通信更加复杂和难以控制;
  • 不支持 Vue.set , Vue.delete 等;
  • postMessage 只能同步字符串,不能 fn。

综合上面的缺点,在数据通信方面,没有使用 postMessage ,而是使用 vuex 替换掉 postMessage ,来完成 iframe 通信。

回答第二个疑问:使用 vuex 完成 iframe 数据通信是如何实现的呢?

这个问题的答案就是 uni-render 。通过它,可以做到让子窗口通过 iframe 展示的同时,父子窗口共享 store 。那这个 uni-render 是什么呢,可以继续往下读,将在下文给于第二个疑问的具体回答。

3.1 uni-render

uni-render 是一个让父子窗口可以不用 postMessage 就能共享 vue store 的技术方案。它包含以下关键内容:

  • 将 iframe 当成一个 dom 节点;
  • 父窗口渲染子窗口( iframe )暴露的组件;
  • 父子窗口共享 vue store;

uni-render 的技术原理图如下:

图片

这里,我结合商品中台配置可视化区域做一个通俗解释:

  • 首先我们把 vue 项目设置为多页应用,页面分别是商品预览页、商品管理页;
  • 其次,调整 vue 入口,每个页面对应一个入口;
  • 编写 iframe 组件和沙箱 vue;
  • 在商品管理页入口将沙箱 vue 和 store 挂载到 global 对象下;
  • 在商品预览入口将 global.parent 下的沙箱 vue 和 store 分别挂到 window 下和 global 下;
  • 最后,其他的内容按照 vue 多页写法正常编写即可。

通过上述 6 个步骤,就可以让用 iframe 做展示容器的商品预览页和商品管理页共享 store 啦。

这里,小伙伴可能会有疑问,为什么要使用沙箱 vue 呢?

这是因为 vue 的单例机制,子窗口(商品管理页)由父窗口(商品管理页) new Vue 渲染的, 因此在子窗口中使用 use 、 filter 、 mixin 、 全局指令 、 全局组件等, 会覆盖父窗口 vue 对象。所以需要隔离出一个干净的和 vue 一样的 vue ,然后用隔离出的沙箱 vue 来渲染子窗口(商品预览页)的内容。这样就可以 达到父子窗口的 vue 互不影响。

下面将介绍一些具体实现,如 iframe 组件、沙箱 vue 、入口设计。iframe 组件的实现非常简单,如下图所示:

图片

这个不再解释了。

沙箱 vue 的实现非常巧妙,如下图所示:

图片

在 Function 上挂一个 clone函数,这样vue下就会有clone 函数,这样 vue 下就会有 clone函数,这样vue下就会有clone 函数,通过执行 Vue.$$clone() ,将 vue 的各种属性挂载到 SandboxVue 上。同时返回 SandboxVue 。即可得到一个干净的沙箱 vue 。

注意:这里的 vue 指的是 vue2 ,目前 vue3 不是单例机制,在 vue3 中是不需要沙箱 vue 的。vue 多页入口设计,如下图所示:

图片

对应上文通俗解释 4、5步骤。

至此,uni-render 技术方案阐述完了。众所周知,大多数 h5 、 pc 建站的数据通信方案都绕不开 postMessage 。而我们通过 uni-render ,让父窗口和 iframe 子窗口的数据通信不再需要 postMessage ,同时只使用 vue 生态中的 vuex 做数据通信。这带来了非常多的好处,好处如下:

  • 统一数据通信方案;
  • 对 store 数据的 watch 、 computed 、更加纯正,数据通信功能更加强大;
  • 精简代码,和 postMessage 永久告别;
  • 支持同步函数,前方的路是星辰大海。

3.2 可视化总结

综上,商品中台的可视化介绍完了,我们通过 uni-render 技术方案让商品预览页( iframe )和商品管理页的数据通信只通过 vuex 即可完成。让可视化的实时更新更加流畅,可视化交互更加强大。

介绍完可视化,下面我将继续介绍商品中台在微前端上的实践,请大家继续往下阅读。

四、商品中台微前端

这里我们把商品中台设计成了微前端架构,使其能够完全适应复杂的外部业务。

这时,你可能会问,微前端是什么?

4.1 微前端是什么

概念如下:多个小型应用聚合为一个应用供用户使用,每个小型应用可以独立开发、独立运行、独立部署,与技术栈无关。大家可以把主应用想象成商场,那子应用就是商户,这样就好理解了。

注意:商品中台不是主应用,它是一个嵌入外部业务的子应用,不具备外部业务嵌入它本身。

微前端和普通的前端有什么区别呢?区别概括在下图中:

图片

结合微前端概念,再阅读上图,可以感受到微前端所带来的优势和价值。

4.2 为什么要做微前端

整体概括下,主要有以下两个目的:

  • 将商品中台更快、更好的嵌入到各个业务方项目中;
  • 为后面主应用的设计做准备。

因此,我们把商品中台项目设计成了微前端架构,它可以很好的解决前端中台化所面临的各种问题。知道了目的,那么我们是如何去设计微前端的呢?

五、商品中台微前端设计

目前微前端领域最主流的技术方案有以下两种:

  • single-spa 技术方案;
  • iframe 技术方案;

基于这两种技术方案,业界产出一些成熟的框架,如 qiankun 、 qingtian ( vivo 自研)等。设计架构如下简图所示:

图片

下面将介绍商品中台使用 qiankun 框架设计的微前端架构的技术实现。

5.1 qiankun 方案设计架构

为什么使用 qiankun ,最核心的原因是:在国内,使用最多的微前端框架就是 qiankun 。整体效果也不错,所以我们的中台需要设计 qiankun 技术架构来适配那些基于 qiankun 的业务。

在说设计架构前,先给大家介绍下 qiankun 的技术原理,如下图所示:

图片

从图中可以知道, qiankun 的核心在于创建微应用容器。了解技术核心后,下面开始介绍设计架构。设计架构如下图所示:

从图中可以知道,主要有 8 块内容,下面将会依次介绍这 8 块内容。

5.1.1  代码接入

主应用内注册微应用

{
  name: 'goods',
  entry: initEntry('goods'),
  container: '#root-view',
  render,
  // activeRule 作为区分不同微应用的关键字
  activeRule: genActiveRule('/main/goods'),
  props: msg
}

微应用入口

图片

微应用打包

5.1.2 接口跨域

解决接口跨域,主要有以下两种方式:

  • 主应用转发:接口的 host 与主应用一致,由主应用根据路径关键字 cmmdy 进行转发。
  • 微应用配置:微应用服务端配置允许跨域

这里我们选择了第一种方式,也就是主应用转发。

5.1.3 路由适配

这里需要注意的是:微应用 router 需要添加 baseUrl ,并且要与主应用关键字 activeRule 保持一致。如下代码(简写)所示:

const KEY = 'product'
router = new VueRouter({
  mode: 'history',
  base: IN_CMS ? `/main/goods/${KEY}` : `/${KEY}`,
  routes
})

这个 KEY 变量就是关键字。

5.1.4 多页设置

目前多页设置的代码如下图所示:

图片

每个页面作为一个独立的微应用引入, filename 设置与主应用 activeRule 值保持一致。

5.1.5 数据通信

思考一个问题,主应用与微应用之间如何通信?通信这块,主要有两种方案:

  • initGlobalState:也是运行时通信(官方方案);
  • window:挂载到 window 下。

initGlobalState 方案的优缺点如下:

优点:api 提供了数据的 change 事件,双方均能监听到数据变化。

缺点:微应用加载时,获取初始数据的时机太晚 ,不适合用作微应用数据的初始化。

window 方案的优缺点如下:

优点:微应用代码全周期内均可以获取数据,很好的避免官方方案中获取数据太晚的问题。

缺点:需要自己处理对数据变化的监听。

商品中台采取的数据通信方案是结合了上述两种方案,互相取长补短,都有使用。

5.1.6 环境区分

主要有以下两种场景:

  • 区分 qiankun 与非 qiankun 技术栈:使用 window.\_\_POWERED\_BY\_QIANKUN\_\_ 即可判断。
  • 区分同样使用 qiankun 的不同主应用:主应用与微应用之间约定参数,通过 window 对象或者生命周期函数中的 props 对象传递,来进行判断。

5.1.7 本地联调

思考一个问题,本地没有主应用的服务,怎么实现主应用与微应用间的快速联调?解决方案如下:

主应用注册微应用时,将 entry 设置为从 localstorage 中获取,在 localstorage 中手动修改入口 entry 的值为微服务的本地地址,就可以实现本地的联调。核心代码如下:

const timestamp = new Date().getTime()
const initEntry = (subSys) => {
  const LS_KEY_ENTRY = `__entry__${subSys}`
  const customEntry = localStorage.getItem(LS_KEY_ENTRY)
  if (customEntry) {
    return `${customEntry}`
  }
  if (subSys === 'goods') {
    return `//vshop-commodity.vivo.com.cn/goods/?t=${timestamp}`
  }
  return `${location.origin}/${subSys}/?t=${timestamp}`
}

通过上述代码,即可在主应用中对入口地址进行动态适配,达到灵活联调的目的。这块还可以将其做成配置中心的形式,这样就不用在 localstorage 中手动修改入口地址。

5.1.8 权限管理

这部分是属于业务强相关内容,做好顶层解耦即可,本文不再阐述。

5.1.9 qiankun 设计架构总结

到此,商品中台的 qiankun 设计架构的核心内容阐述完了,使用 qiankun 的过程中,也遇到了一些问题,但使用这种流行框架的好处就是, 遇到问题时,可以去查阅相关文档和博客,基本上都可以找到相关的解决思路。基于 qiankun 的设计架构,表现出了不错的效果,也让商品中台能够以 signle-spa 的 技术方案嵌入到其他业务项目中。

5.2 踩坑经验分享

过程中遇到的坑较多,本文挑选几个踩坑经验分享出来。

5.2.1 uni-render 相遇 qiankun 跨域问题

现象:项目接入主应用,uni-render 控制的预览页面空白,控制台报跨域错误。

原因:iframe 预览页面为商品中台域名,而子应用接入主应用后为主应用域名,从而导致跨域。

解决方案:主应用、子应用 html 入口文件头部设置 document.domain ,使两者 domain 保持一致。

5.2.3 uni-render 、qiankun 、 ueditor ”化学反应“

问题一:

现象:qiankun 子应用中富文本组件 ueditor 功能异常。

原因:qiankun 对 ueditor 劫持,导致 ueditor 某些变量无法获取到。

解决方案:在主应用中,通过 excludeAssetFilter 让 ueditor 的静态资源不要被 qiankun 劫持处理。

问题二:

现象:子应用中 ueditor 的请求 url 报错。

原因:ueditor 的请求 url 没加主应用请求前缀。

解决方案:子应用环境中,通过 ue.getActionUrl 给 ueditor 的请求 url 增加前缀。

问题三:

现象:子应用中 ueditor 单图上传失败。

原因:子应用设置了 domain , ueditor 的单图上传是通过 iframe 实现的,但是 iframe 没有设置 domain ,导致上传失败。

解决方案:重写 ueditor 的单图上传,将 iframe 改为 xhr 上传。

至此,踩坑经验也分享完了,还有一些踩坑,这里就不再叙述了。在将几个技术融合在一起的过程中,总是会有一些预想不到的问题,正所谓兵来将挡、水来土掩,我们用正确的心态去面对和解决这些问题即可。

综上,我们对商品中台的可视化和微前端做了整体的阐述,包含以下内容:

  • 通过 uni-render 技术方案解决了可视化页面 iframe 和父页面的数据通信问题。
  • 通过 qiankun 解决了商品中台嵌入基于 single-spa 技术方案的外部业务。
  • uni-render 结合 qiankun 的踩坑经验。

在解决一个场景或者问题时,技术的实现细节不是最重要的,最重要的是脱离技术的 Principles 。

最后用一句话结尾:Principles are higher than techniques. Principles produce techniques in an instant.

作者:vivo互联网前端团队-Yang Kun


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK